UserInfoCard.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <template>
  2. <view class="user-info-card">
  3. <view class="card-title">
  4. <view class="title-line" />
  5. <text class="title-text">基本信息</text>
  6. </view>
  7. <view class="card-content">
  8. <wd-form ref="formRef" :model="model" :title-width="100">
  9. <wd-form-item title="姓名" prop="realname">
  10. <wd-input v-model="model.realname" type="text" :readonly="disabled" align-right />
  11. </wd-form-item>
  12. <wd-form-item title="性别" prop="gender">
  13. <wd-input
  14. v-model="genderLabel"
  15. readonly
  16. type="text"
  17. align-right
  18. placeholder="请选择性别"
  19. suffix-icon="down"
  20. @click="showPicker('gender')"
  21. />
  22. </wd-form-item>
  23. <wd-form-item title="身份证号" prop="idCardNumber">
  24. <wd-input v-model="model.idCardNumber" type="text" align-right :readonly="disabled" />
  25. </wd-form-item>
  26. <wd-form-item title="手机号" prop="username">
  27. <wd-input v-model="model.username" type="text" readonly align-right />
  28. </wd-form-item>
  29. <wd-form-item title="年龄" prop="age">
  30. <wd-input v-model="model.age" type="text" readonly align-right />
  31. </wd-form-item>
  32. <wd-form-item title="学历" prop="degree">
  33. <wd-input
  34. v-model="degreeLabel"
  35. readonly
  36. type="text"
  37. align-right
  38. placeholder="请选择学历"
  39. suffix-icon="down"
  40. @click="showPicker('degree')"
  41. />
  42. </wd-form-item>
  43. </wd-form>
  44. </view>
  45. <wd-picker v-model:visible="pickerShow" :columns="pickerColumns" @confirm="pickerConfirm" />
  46. </view>
  47. </template>
  48. <script setup lang="ts">
  49. import { computed, ref } from 'vue'
  50. import type { DictItem } from '@/services/modules/common/type'
  51. import type { PersonalBusinessCardResponse } from '@/services/modules/mine/personalCard/type'
  52. type PickerType = 'gender' | 'degree'
  53. interface PickerConfirmEvent {
  54. selectedItems?: DictItem | DictItem[]
  55. value?: string | string[]
  56. }
  57. const props = withDefaults(
  58. defineProps<{
  59. title?: string
  60. disabled: false
  61. genderColumns: DictItem[]
  62. degreeColumns: DictItem[]
  63. }>(),
  64. {
  65. title: '基本信息',
  66. }
  67. )
  68. const model = defineModel<PersonalBusinessCardResponse>({
  69. required: true,
  70. })
  71. const pickerType = ref<PickerType>('gender')
  72. const pickerShow = ref(false)
  73. const pickerColumns = ref<DictItem[]>([])
  74. const genderLabel = computed<string>({
  75. get() {
  76. if (!model.value.gender) return ''
  77. const current = props.genderColumns.find((item) => item.value === model.value.gender)
  78. return current?.label ?? ''
  79. },
  80. set() {
  81. // readonly input 占位 setter,避免 v-model 绑定 readonly computed 报警告
  82. },
  83. })
  84. const degreeLabel = computed<string>({
  85. get() {
  86. if (!model.value.degree) return ''
  87. const current = props.degreeColumns.find((item) => item.value === model.value.degree)
  88. return current?.label ?? ''
  89. },
  90. set() {
  91. // readonly input 占位 setter,避免 v-model 绑定 readonly computed 报警告
  92. },
  93. })
  94. function showPicker(type: PickerType) {
  95. if (props.disabled) return
  96. pickerType.value = type
  97. pickerColumns.value = type === 'gender' ? props.genderColumns : props.degreeColumns
  98. pickerShow.value = true
  99. }
  100. function pickerConfirm(event: PickerConfirmEvent) {
  101. const currentValue = Array.isArray(event.value) ? event.value[0] : event.value
  102. if (!currentValue) return
  103. if (pickerType.value === 'gender') {
  104. model.value.gender = currentValue
  105. return
  106. }
  107. model.value.degree = currentValue
  108. }
  109. </script>
  110. <style lang="scss" scoped>
  111. .user-info-card {
  112. position: relative;
  113. margin-bottom: 28rpx;
  114. padding: 30rpx 26rpx 26rpx;
  115. box-sizing: border-box;
  116. overflow: hidden;
  117. border-radius: 30rpx;
  118. background: #ffffff;
  119. box-shadow: 0 18rpx 48rpx rgba(22, 38, 67, 0.1);
  120. // 主阴影:突出卡片层级
  121. // 顶部轻微高光边
  122. &::before {
  123. position: absolute;
  124. top: 0;
  125. left: 28rpx;
  126. right: 28rpx;
  127. height: 1rpx;
  128. content: '';
  129. background: rgba(255, 255, 255, 0.9);
  130. }
  131. // 右上角装饰圆
  132. &::after {
  133. position: absolute;
  134. top: -96rpx;
  135. right: -78rpx;
  136. width: 260rpx;
  137. height: 260rpx;
  138. content: '';
  139. border-radius: 50%;
  140. background: linear-gradient(135deg, rgba(47, 132, 255, 0.18) 0%, rgba(47, 132, 255, 0.08) 100%);
  141. }
  142. }
  143. .card-title {
  144. position: relative;
  145. z-index: 1;
  146. display: flex;
  147. align-items: center;
  148. margin-bottom: 18rpx;
  149. }
  150. .title-line {
  151. width: 8rpx;
  152. height: 34rpx;
  153. margin-right: 14rpx;
  154. border-radius: 999rpx;
  155. background: linear-gradient(180deg, #2f84ff 0%, #57a3ff 100%);
  156. }
  157. .title-text {
  158. font-size: 34rpx;
  159. font-weight: 700;
  160. line-height: 48rpx;
  161. color: #1f2a44;
  162. }
  163. .card-content {
  164. position: relative;
  165. z-index: 1;
  166. overflow: hidden;
  167. border-radius: 22rpx;
  168. box-shadow:
  169. 0 18rpx 44rpx rgba(30, 64, 120, 0.12),
  170. 0 6rpx 18rpx rgba(30, 64, 120, 0.06);
  171. }
  172. </style>