center.vue 9.9 KB

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