index.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <template>
  2. <v-card class="pa-5 card-box d-flex flex-column align-center">
  3. <CtForm ref="CtFormRef" :items="formItems" style="width: 700px;">
  4. <template #avatar="{ item }">
  5. <div style="color: #7a7a7a;">头像</div>
  6. <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
  7. <v-avatar class="elevation-5" size=80 :image="getUserAvatar(item.value, baseInfo?.sex)"></v-avatar>
  8. <div v-show="showIcon" @click="openFileInput" v-bind="$attrs" class="mdi mdi-camera-outline">
  9. <input
  10. type="file"
  11. ref="fileInput"
  12. accept="image/png, image/jpg, image/jpeg"
  13. style="display: none;"
  14. @change="handleUploadFile"
  15. />
  16. </div>
  17. </div>
  18. <div style="font-size: 14px; color: var(--color-999);">只支持JPG、JPEG、PNG类型的图片,大小不超过10M</div>
  19. </template>
  20. </CtForm>
  21. <v-btn class="buttons mt-5" color="primary" @click.stop="handleSubmit">{{ $t('common.save') }}</v-btn>
  22. </v-card>
  23. <Loading :visible="overlay"></Loading>
  24. <ImgCropper :visible="isShowCopper" :image="selectPic" :cropBoxResizable="true" @submit="handleHideCopper" :aspectRatio="1 / 1" @close="isShowCopper = false"></ImgCropper>
  25. </template>
  26. <script setup>
  27. defineOptions({ name: 'information-setting'})
  28. import { ref } from 'vue'
  29. import { saveUserInfo } from '@/api/enterprise'
  30. import { uploadFile } from '@/api/common'
  31. import { useI18n } from '@/hooks/web/useI18n'
  32. import { getDict } from '@/hooks/web/useDictionaries'
  33. import { useUserStore } from '@/store/user'
  34. import Snackbar from '@/plugins/snackbar'
  35. import { getUserAvatar } from '@/utils/avatar'
  36. const { t } = useI18n()
  37. const userStore = useUserStore()
  38. const showIcon = ref(false)
  39. // 图片裁剪
  40. const overlay = ref(false)
  41. const selectPic = ref('')
  42. const isShowCopper = ref(false)
  43. const CtFormRef = ref()
  44. const formItems = ref({
  45. options: [
  46. {
  47. slotName: 'avatar',
  48. key: 'avatar',
  49. value: '',
  50. flexStyle: 'align-center'
  51. },
  52. {
  53. type: 'ifRadio',
  54. key: 'sex',
  55. value: '2',
  56. label: '性别',
  57. width: 90,
  58. dictTypeName: 'menduner_sex',
  59. items: []
  60. },
  61. {
  62. type: 'text',
  63. key: 'name',
  64. value: '',
  65. label: '用户名 *',
  66. rules: [v => !!v || '请输入用户名']
  67. },
  68. {
  69. type: 'phoneNumber',
  70. key: 'phone',
  71. value: '',
  72. label: '手机号码 *',
  73. rules: [v => !!v || '请输入手机号码']
  74. },
  75. {
  76. type: 'text',
  77. key: 'email',
  78. value: '',
  79. label: '电子邮箱'
  80. }
  81. ]
  82. })
  83. // 用户基本信息
  84. const baseInfo = ref(JSON.parse(localStorage.getItem('baseInfo')) || {})
  85. const query = ref({})
  86. // 获取字典数据以及字段回显
  87. formItems.value.options.forEach(item => {
  88. if (item.dictTypeName) {
  89. getDict(item.dictTypeName).then(({ data }) => {
  90. data = data?.length && data || []
  91. item.items = data
  92. })
  93. }
  94. if (Object.keys(baseInfo).length) {
  95. item.value = baseInfo.value[item.key]
  96. query.value.id = baseInfo.value.id
  97. }
  98. })
  99. // 监听store变化
  100. userStore.$subscribe((mutation, state) => {
  101. baseInfo.value = state.baseInfo
  102. })
  103. // 选择文件
  104. const fileInput = ref()
  105. const clicked = ref(false)
  106. const openFileInput = () => {
  107. if (clicked.value) return
  108. clicked.value = true
  109. fileInput.value.click()
  110. clicked.value = false
  111. }
  112. // 上传头像
  113. const handleUploadFile = async (e) => {
  114. const file = e.target.files[0]
  115. if (!file) return
  116. const reader = new FileReader()
  117. reader.readAsDataURL(file)
  118. reader.onload = () => {
  119. selectPic.value = String(reader.result)
  120. isShowCopper.value = true
  121. }
  122. }
  123. const handleHideCopper = (data) => {
  124. isShowCopper.value = false
  125. if (data) {
  126. const { file } = data
  127. if (!file) return
  128. const formData = new FormData()
  129. formData.append('file', file)
  130. uploadFile(formData).then(async ({ data }) => {
  131. if (!data) return
  132. formItems.value.options.find(e => e.key === 'avatar').value = data
  133. })
  134. }
  135. }
  136. // 提交
  137. const handleSubmit = async () => {
  138. const { valid } = await CtFormRef.value.formRef.validate()
  139. if (!valid) return
  140. overlay.value = true
  141. formItems.value.options.forEach(item => {
  142. query.value[item.key] = item.value
  143. })
  144. await saveUserInfo(query.value)
  145. setTimeout(async () => {
  146. await userStore.getEnterpriseInfo()
  147. Snackbar.success(t('common.submittedSuccessfully'))
  148. overlay.value = false
  149. }, 1000)
  150. }
  151. </script>
  152. <style scoped lang="scss">
  153. .avatarsBox {
  154. height: 80px;
  155. width: 80px;
  156. position: relative;
  157. cursor: pointer;
  158. margin: 32px;
  159. margin-right: 40px;
  160. .img {
  161. width: 100%;
  162. height: 100%;
  163. }
  164. .mdi {
  165. font-size: 42px;
  166. color: #fff;
  167. }
  168. div {
  169. position: absolute;
  170. top: 50%;
  171. left: 50%;
  172. transform: translate(-50%, -50%);
  173. border-radius: 50%;
  174. }
  175. }
  176. </style>