Profile.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <script setup lang="ts">
  2. import { reactive, ref } from "vue";
  3. import { formUpload } from "@/api/mock";
  4. import { message } from "@/utils/message";
  5. import { type UserInfo, getMine } from "@/api/user";
  6. import type { FormInstance, FormRules } from "element-plus";
  7. import ReCropperPreview from "@/components/ReCropperPreview";
  8. import { createFormData, deviceDetection } from "@pureadmin/utils";
  9. import uploadLine from "~icons/ri/upload-line";
  10. defineOptions({
  11. name: "Profile"
  12. });
  13. const imgSrc = ref("");
  14. const cropperBlob = ref();
  15. const cropRef = ref();
  16. const uploadRef = ref();
  17. const isShow = ref(false);
  18. const userInfoFormRef = ref<FormInstance>();
  19. const userInfos = reactive({
  20. avatar: "",
  21. nickname: "",
  22. email: "",
  23. phone: "",
  24. description: ""
  25. });
  26. const rules = reactive<FormRules<UserInfo>>({
  27. nickname: [{ required: true, message: "昵称必填", trigger: "blur" }]
  28. });
  29. function queryEmail(queryString, callback) {
  30. const emailList = [
  31. { value: "@qq.com" },
  32. { value: "@126.com" },
  33. { value: "@163.com" }
  34. ];
  35. let results = [];
  36. let queryList = [];
  37. emailList.map(item =>
  38. queryList.push({ value: queryString.split("@")[0] + item.value })
  39. );
  40. results = queryString
  41. ? queryList.filter(
  42. item =>
  43. item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
  44. )
  45. : queryList;
  46. callback(results);
  47. }
  48. const onChange = uploadFile => {
  49. const reader = new FileReader();
  50. reader.onload = e => {
  51. imgSrc.value = e.target.result as string;
  52. isShow.value = true;
  53. };
  54. reader.readAsDataURL(uploadFile.raw);
  55. };
  56. const handleClose = () => {
  57. cropRef.value.hidePopover();
  58. uploadRef.value.clearFiles();
  59. isShow.value = false;
  60. };
  61. const onCropper = ({ blob }) => (cropperBlob.value = blob);
  62. const handleSubmitImage = () => {
  63. const formData = createFormData({
  64. files: new File([cropperBlob.value], "avatar")
  65. });
  66. formUpload(formData)
  67. .then(({ success, data }) => {
  68. if (success) {
  69. message("更新头像成功", { type: "success" });
  70. handleClose();
  71. } else {
  72. message("更新头像失败");
  73. }
  74. })
  75. .catch(error => {
  76. message(`提交异常 ${error}`, { type: "error" });
  77. });
  78. };
  79. // 更新信息
  80. const onSubmit = async (formEl: FormInstance) => {
  81. await formEl.validate((valid, fields) => {
  82. if (valid) {
  83. console.log(userInfos);
  84. message("更新信息成功", { type: "success" });
  85. } else {
  86. console.log("error submit!", fields);
  87. }
  88. });
  89. };
  90. getMine().then(res => {
  91. Object.assign(userInfos, res.data);
  92. });
  93. </script>
  94. <template>
  95. <div
  96. :class="[
  97. 'min-w-[180px]',
  98. deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
  99. ]"
  100. >
  101. <h3 class="my-8!">个人信息</h3>
  102. <el-form
  103. ref="userInfoFormRef"
  104. label-position="top"
  105. :rules="rules"
  106. :model="userInfos"
  107. >
  108. <el-form-item label="头像">
  109. <el-avatar :size="80" :src="userInfos.avatar" />
  110. <el-upload
  111. ref="uploadRef"
  112. accept="image/*"
  113. action="#"
  114. :limit="1"
  115. :auto-upload="false"
  116. :show-file-list="false"
  117. :on-change="onChange"
  118. >
  119. <el-button plain class="ml-4!">
  120. <IconifyIconOffline :icon="uploadLine" />
  121. <span class="ml-2">更新头像</span>
  122. </el-button>
  123. </el-upload>
  124. </el-form-item>
  125. <el-form-item label="昵称" prop="nickname">
  126. <el-input v-model="userInfos.nickname" placeholder="请输入昵称" />
  127. </el-form-item>
  128. <el-form-item label="邮箱" prop="email">
  129. <el-autocomplete
  130. v-model="userInfos.email"
  131. :fetch-suggestions="queryEmail"
  132. :trigger-on-focus="false"
  133. placeholder="请输入邮箱"
  134. clearable
  135. class="w-full"
  136. />
  137. </el-form-item>
  138. <el-form-item label="联系电话">
  139. <el-input
  140. v-model="userInfos.phone"
  141. placeholder="请输入联系电话"
  142. clearable
  143. />
  144. </el-form-item>
  145. <el-form-item label="简介">
  146. <el-input
  147. v-model="userInfos.description"
  148. placeholder="请输入简介"
  149. type="textarea"
  150. :autosize="{ minRows: 6, maxRows: 8 }"
  151. maxlength="56"
  152. show-word-limit
  153. />
  154. </el-form-item>
  155. <el-button type="primary" @click="onSubmit(userInfoFormRef)">
  156. 更新信息
  157. </el-button>
  158. </el-form>
  159. <el-dialog
  160. v-model="isShow"
  161. width="40%"
  162. title="编辑头像"
  163. destroy-on-close
  164. :closeOnClickModal="false"
  165. :before-close="handleClose"
  166. :fullscreen="deviceDetection()"
  167. >
  168. <ReCropperPreview ref="cropRef" :imgSrc="imgSrc" @cropper="onCropper" />
  169. <template #footer>
  170. <div class="dialog-footer">
  171. <el-button bg text @click="handleClose">取消</el-button>
  172. <el-button bg text type="primary" @click="handleSubmitImage">
  173. 确定
  174. </el-button>
  175. </div>
  176. </template>
  177. </el-dialog>
  178. </div>
  179. </template>