巴青农资商城

profile.vue 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <template>
  2. <view class="form-page">
  3. <view class="form-card">
  4. <view class="form-row">
  5. <text class="form-row__label">用户 ID</text>
  6. <text class="form-row__readonly">{{ profile.memberId || '—' }}</text>
  7. </view>
  8. <view class="form-row">
  9. <text class="form-row__label">会员名称</text>
  10. <text class="form-row__readonly">{{ profile.memberCode || '—' }}</text>
  11. </view>
  12. <view class="form-row">
  13. <text class="form-row__label">手机号</text>
  14. <text class="form-row__readonly">{{ profile.mobile || '—' }}</text>
  15. </view>
  16. <view class="form-row form-row--col">
  17. <text class="form-row__label form-row__label--req">头像</text>
  18. <image-upload v-model="form.avatar" placeholder="上传头像" />
  19. </view>
  20. <view class="form-row">
  21. <text class="form-row__label form-row__label--req">昵称</text>
  22. <input
  23. v-model="form.nickName"
  24. class="form-row__input"
  25. placeholder="请输入昵称"
  26. maxlength="30"
  27. />
  28. </view>
  29. <view class="form-row">
  30. <text class="form-row__label">邮箱</text>
  31. <input v-model="form.email" class="form-row__input" placeholder="选填" />
  32. </view>
  33. <view class="form-row">
  34. <text class="form-row__label">性别</text>
  35. <picker :range="genderLabels" :value="genderIndex" @change="onGenderChange">
  36. <view class="form-row__picker">
  37. <text :class="{ 'form-row__placeholder': genderIndex < 0 }">
  38. {{ genderIndex >= 0 ? genderLabels[genderIndex] : '请选择' }}
  39. </text>
  40. </view>
  41. </picker>
  42. </view>
  43. </view>
  44. <view class="form-footer">
  45. <button class="form-footer__btn" :disabled="saving" @click="handleSave">
  46. {{ saving ? '保存中…' : '保 存' }}
  47. </button>
  48. </view>
  49. </view>
  50. </template>
  51. <script setup>
  52. import { ref, reactive } from 'vue'
  53. import { onLoad } from '@dcloudio/uni-app'
  54. import { getMemberProfile, updateMemberProfile } from '@/api/member'
  55. import { useUserStore } from '@/store/user'
  56. import { ensureApiToken } from '@/utils/apiAuth'
  57. import { GENDER_OPTIONS } from '@/utils/entryConstants'
  58. import ImageUpload from '@/components/mine/ImageUpload.vue'
  59. import { useActionGuard } from '@/utils/actionGuard'
  60. const saveGuard = useActionGuard()
  61. const profile = reactive({
  62. memberId: '',
  63. memberCode: '',
  64. mobile: ''
  65. })
  66. const form = reactive({
  67. nickName: '',
  68. avatar: '',
  69. email: '',
  70. sex: ''
  71. })
  72. const saving = saveGuard.locked
  73. const genderLabels = GENDER_OPTIONS.map((g) => g.label)
  74. const genderIndex = ref(-1)
  75. /** 昵称最长 30 字(对齐功能需求 MS-P2) */
  76. const NICK_NAME_MAX = 30
  77. const userStore = useUserStore()
  78. onLoad(() => {
  79. if (!ensureApiToken()) return
  80. loadProfile()
  81. })
  82. function syncGenderIndex() {
  83. const idx = GENDER_OPTIONS.findIndex((g) => g.value === form.sex)
  84. genderIndex.value = idx >= 0 ? idx : -1
  85. }
  86. function loadProfile() {
  87. getMemberProfile()
  88. .then((res) => {
  89. const m = res.data || {}
  90. profile.memberId = m.memberId
  91. profile.memberCode = m.memberCode
  92. profile.mobile = m.mobile
  93. form.nickName = m.nickName || ''
  94. form.avatar = m.avatar || ''
  95. form.email = m.email || ''
  96. form.sex = m.sex != null ? String(m.sex) : ''
  97. syncGenderIndex()
  98. })
  99. .catch(() => {})
  100. }
  101. function onGenderChange(e) {
  102. const idx = Number(e.detail.value)
  103. genderIndex.value = idx
  104. form.sex = GENDER_OPTIONS[idx]?.value || ''
  105. }
  106. function validate() {
  107. const nick = (form.nickName || '').trim()
  108. if (!nick) {
  109. uni.showToast({ title: '请输入昵称', icon: 'none' })
  110. return false
  111. }
  112. if (nick.length > NICK_NAME_MAX) {
  113. uni.showToast({ title: `昵称不超过 ${NICK_NAME_MAX} 字`, icon: 'none' })
  114. return false
  115. }
  116. const email = (form.email || '').trim()
  117. if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
  118. uni.showToast({ title: '邮箱格式不正确', icon: 'none' })
  119. return false
  120. }
  121. return true
  122. }
  123. function handleSave() {
  124. saveGuard.run(async () => {
  125. if (!validate()) return
  126. await updateMemberProfile({
  127. nickName: form.nickName.trim(),
  128. avatar: form.avatar,
  129. email: (form.email || '').trim(),
  130. sex: form.sex
  131. })
  132. uni.showToast({ title: '保存成功', icon: 'none' })
  133. await userStore.fetchUserInfo()
  134. setTimeout(() => uni.navigateBack(), 800)
  135. })
  136. }
  137. </script>
  138. <style lang="scss" scoped>
  139. @import '@/styles/mine.scss';
  140. </style>