center.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <template>
  2. <div class="ml-[20px] min-h-[70vh] px-[20px] py-[30px] w-[1000px] bg-[#fff] rounded-[var(--rounded-big)]">
  3. <div class="h-full" v-loading="loading">
  4. <div>
  5. <div class="text-[18px] text-[#333] mb-[50px]">我的信息</div>
  6. <div v-if="info">
  7. <el-form :model="info" class="form-wrap" label-width="120px" label-position="left">
  8. <el-form-item :label="t('memberHeadimg')" class="pb-[20px] border-b-[1px] border-dashed border-[#ddd]">
  9. <div class="w-full flex justify-between content-center items-center">
  10. <img v-if="!info.headimg" class="w-[80px] h-[80px]" src="@/assets/images/default_headimg.png" alt="">
  11. <img v-else :src="img(info.headimg)" class="w-[80px] h-[80px]" alt="">
  12. <el-upload class="avatar-uploader" :show-file-list="false" v-bind="upload" ref="uploadRef">
  13. <span class="cursor-pointer text-primary">{{ t('edit') }}</span>
  14. </el-upload>
  15. </div>
  16. </el-form-item>
  17. <el-form-item :label="t('username')" class="pb-[20px] border-b-[1px] border-dashed border-[#ddd]">
  18. <div class="w-full flex justify-between content-center">
  19. <span>{{ info.username }}</span>
  20. </div>
  21. </el-form-item>
  22. <el-form-item :label="t('nickname')" class="pb-[20px] border-b-[1px] border-dashed border-[#ddd]">
  23. <div class="w-full flex justify-between content-center">
  24. <div>
  25. <span>{{ updateNickname.value }}</span>
  26. <span v-if="currentLevel">(当前等级:{{currentLevel}})</span>
  27. </div>
  28. <span class="cursor-pointer text-primary" @click="updateNickname.modal = true">{{ t('edit')}}</span>
  29. </div>
  30. </el-form-item>
  31. <el-form-item :label="t('mobile')" class="pb-[20px] border-b-[1px] border-dashed border-[#ddd]">
  32. <div class="w-full flex justify-between content-center">
  33. <span>{{ info.mobile }}</span>
  34. <span v-if="!info.mobile" class="cursor-pointer text-primary" @click="updateMobileDialog = true">{{ t('edit')}}</span>
  35. </div>
  36. </el-form-item>
  37. </el-form>
  38. <div class="flex justify-end mt-[38px]">
  39. <span class="cursor-pointer w-[130px] h-[40px] leading-[40px] text-center rounded-[4px] bg-[var(--el-color-primary)] text-white text-[14px]" @click="logoutFn">退出</span>
  40. </div>
  41. </div>
  42. </div>
  43. <!-- 更改昵称 -->
  44. <el-dialog v-model="updateNickname.modal" :title="t('nickname')" width="380">
  45. <el-form :model="info">
  46. <el-form-item>
  47. <el-input v-model="updateNickname.value" autocomplete="off" />
  48. </el-form-item>
  49. </el-form>
  50. <template #footer>
  51. <span class="dialog-footer">
  52. <el-button @click="updateNickname.modal = false">{{ t('cancel') }}</el-button>
  53. <el-button type="primary" @click="updateNicknameConfirm">{{ t('confirm') }}</el-button>
  54. </span>
  55. </template>
  56. </el-dialog>
  57. <!-- 更改手机号码 -->
  58. <el-dialog v-model="updateMobileDialog" :title="t('updateMobile')" width="420">
  59. <el-form :model="formData" ref="formRef" :rules="formRules" :validate-on-rule-change="false">
  60. <el-form-item prop="mobile">
  61. <el-input v-model="formData.mobile" :placeholder="t('mobilePlaceholder')" clearable>
  62. </el-input>
  63. </el-form-item>
  64. <el-form-item prop="mobile_code">
  65. <el-input v-model="formData.mobile_code" :placeholder="t('codePlaceholder')">
  66. <template #suffix>
  67. <sms-code :mobile="formData.mobile" type="login" v-model="formData.mobile_key" @click="sendSmsCode" ref="smsCodeRef"></sms-code>
  68. </template>
  69. </el-input>
  70. </el-form-item>
  71. </el-form>
  72. <template #footer>
  73. <span class="dialog-footer">
  74. <el-button @click="updateMobileDialog = false">{{ t('cancel') }}</el-button>
  75. <el-button type="primary" :loading="mobileLoading" @click="updateMobileConfirm">{{ t('confirm') }}</el-button>
  76. </span>
  77. </template>
  78. </el-dialog>
  79. </div>
  80. </div>
  81. </template>
  82. <script lang="ts" setup>
  83. import { reactive, ref, computed } from 'vue'
  84. import useMemberStore from '@/stores/member'
  85. import useAppStore from '@/stores/app'
  86. import { modifyMember, bindMobile, getMemberLevel} from '@/app/api/member'
  87. import { ElMessage, ElMessageBox, UploadFile, UploadFiles } from 'element-plus'
  88. import request from '@/utils/request'
  89. import storage from '@/utils/storage'
  90. import { getToken } from '@/utils/common'
  91. const memberStore = useMemberStore()
  92. const loading = ref(true)
  93. //会员昵称
  94. const updateNickname = reactive({
  95. modal: false,
  96. value: ''
  97. })
  98. const info = computed(() => {
  99. updateNickname.value = memberStore.info?.nickname;
  100. if (memberStore.info) loading.value = false;
  101. return memberStore.info;
  102. })
  103. const appStore = useAppStore()
  104. // 获取会员等级
  105. let currentLevel = ref('')
  106. const getMemberLevelFn = () =>{
  107. getMemberLevel().then(res =>{
  108. if(info.value && res.data && res.data.length){
  109. res.data.forEach((item,index)=>{
  110. if(item.level_id == info.value.member_level){
  111. currentLevel.value = item.level_name
  112. }
  113. })
  114. }
  115. })
  116. }
  117. getMemberLevelFn()
  118. const uploadRef = ref(null)
  119. const upload = computed(() => {
  120. const headers: Record<string, any> = {}
  121. headers.token = getToken()
  122. headers['site-id'] = storage.get('siteId') || 1
  123. return {
  124. action: `${request.options.baseURL}/file/image`,
  125. limit: 1,
  126. headers,
  127. onSuccess: (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  128. let img = uploadFile?.response?.data?.url;
  129. if (response.code == 200 || response.code == 1) {
  130. modifyMember({
  131. field: 'headimg',
  132. value: img
  133. }).then(() => {
  134. memberStore.info.headimg = img
  135. uploadRef.value.clearFiles()
  136. })
  137. } else {
  138. uploadFile.status = 'fail'
  139. ElMessage({ message: response.msg, type: 'error' })
  140. }
  141. }
  142. }
  143. })
  144. // 修改会员名称
  145. const updateNicknameConfirm = () => {
  146. if (!updateNickname.value) { ElMessage.error('会员昵称不能为空'); return }
  147. modifyMember({
  148. field: 'nickname',
  149. value: updateNickname.value
  150. }).then(res => {
  151. updateNickname.modal = false
  152. })
  153. }
  154. // 手机号
  155. const updateMobileDialog = ref(false)
  156. const formData = reactive({
  157. mobile: '',
  158. mobile_code: '',
  159. mobile_key: ''
  160. })
  161. const mobileLoading = ref(false)
  162. const formRef = ref<FormInstance>()
  163. const formRules = computed(() => {
  164. return {
  165. 'mobile': [
  166. {
  167. required: true,
  168. message: t('mobilePlaceholder'),
  169. trigger: ['blur', 'change'],
  170. },
  171. {
  172. validator(rule: any, value: string, callback: any) {
  173. const phonePattern = /^1[3456789]\d{9}$/
  174. if (!phonePattern.test(value)) {
  175. return callback(new Error(t('mobileTips')))
  176. } else {
  177. return callback()
  178. }
  179. },
  180. message: t('mobileError'),
  181. trigger: ['blur'],
  182. }
  183. ],
  184. 'mobile_code': {
  185. required: true,
  186. message: t('codePlaceholder'),
  187. trigger: ['change']
  188. }
  189. }
  190. })
  191. const smsCodeRef = ref<AnyObject | null>(null)
  192. const sendSmsCode = async () => {
  193. await formRef.value?.validateField('mobile', async (valid, fields) => {
  194. if (valid) {
  195. smsCodeRef.value?.send()
  196. }
  197. })
  198. }
  199. const updateMobileConfirm = async () => {
  200. await formRef.value?.validate(async (valid, fields) => {
  201. if (valid) {
  202. if (mobileLoading.value) return
  203. mobileLoading.value = true
  204. bindMobile(formData).then((res) => {
  205. memberStore.getMemberInfo()
  206. mobileLoading.value = false
  207. updateMobileDialog.value = false
  208. }).catch(() => {
  209. mobileLoading.value = false
  210. })
  211. }
  212. })
  213. }
  214. // 退出登录
  215. const logoutFn = () => {
  216. ElMessageBox.confirm('您确定要退出账号吗?', '提示',
  217. {
  218. confirmButtonText: '确定',
  219. cancelButtonText: '取消',
  220. confirmButtonClass:'!bg-[var(--el-color-primary)] !border-[var(--el-color-primary)]',
  221. cancelButtonClass:'!border-[#dcdfe6]',
  222. type: 'warning'
  223. }
  224. ).then(() => {
  225. memberStore.logout()
  226. navigateTo(`/`)
  227. })
  228. }
  229. </script>
  230. <style lang="scss" scoped>
  231. .box-card {
  232. border: none !important;
  233. }
  234. ::v-deep .form-wrap .el-form-item {
  235. align-items: center;
  236. }
  237. </style>