index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <template>
  2. <div class="page-content user">
  3. <div class="content">
  4. <div class="left-wrap">
  5. <div class="user-wrap box-style">
  6. <img class="bg" src="@imgs/user/bg.webp" />
  7. <el-upload
  8. :file-list="[]"
  9. class="upload-demo"
  10. name="avatar"
  11. accept="jpg,jpeg,png,webp"
  12. :show-file-list="false"
  13. :action="uploadServer"
  14. :headers="{Authorization: useUserStore().accessToken}"
  15. :on-success="handleSuccess"
  16. >
  17. <img class="avatar" :src="userInfo.avatar || 'https://img.lewaimai.com/zqcrm/avatar/20250926/9PLsci1dwXLcb8Iw.jpg'" title="点击上传"/>
  18. <template #tip>
  19. <div class="el-upload__tip">
  20. 点击头像上传
  21. </div>
  22. </template>
  23. </el-upload>
  24. <h2 class="name">{{ userInfo.username }}</h2>
  25. <!-- <p class="des">逐趣CRM 是一款漂亮的后台管理系统模版.</p>-->
  26. <div class="outer-info">
  27. <div>
  28. <i class="iconfont-sys">&#xe608;</i>
  29. <span>{{ form.sex ? '女' : '男' }}</span>
  30. </div>
  31. <div>
  32. <i class="iconfont-sys">&#xe6f4;</i>
  33. <span>{{ form.phone }}</span>
  34. </div>
  35. <div>
  36. <i class="iconfont-sys">&#xe72e;</i>
  37. <span>{{ form.email }}</span>
  38. </div>
  39. <div>
  40. <i class="iconfont-sys">&#xe811;</i>
  41. <span>{{ form.descr}}</span>
  42. </div>
  43. </div>
  44. <!-- <div class="lables">
  45. <h3>标签</h3>
  46. <div>
  47. <div v-for="item in lableList" :key="item">
  48. {{ item }}
  49. </div>
  50. </div>
  51. </div>-->
  52. </div>
  53. <!-- <el-carousel class="gallery" height="160px"
  54. :interval="5000"
  55. indicator-position="none"
  56. >
  57. <el-carousel-item class="item" v-for="item in galleryList" :key="item">
  58. <img :src="item"/>
  59. </el-carousel-item>
  60. </el-carousel> -->
  61. </div>
  62. <div class="right-wrap">
  63. <div class="info box-style">
  64. <h1 class="title">基本设置</h1>
  65. <ElForm
  66. :model="form"
  67. class="form"
  68. ref="ruleFormRef"
  69. :rules="rules"
  70. label-width="86px"
  71. label-position="top"
  72. >
  73. <ElRow>
  74. <ElFormItem label="用户名" prop="realName">
  75. <el-input v-model="form.username" :disabled="!isEdit" />
  76. </ElFormItem>
  77. <ElFormItem label="性别" prop="sex" class="right-input">
  78. <ElSelect v-model="form.sex" placeholder="Select" :disabled="!isEdit">
  79. <ElOption
  80. v-for="item in options"
  81. :key="item.value"
  82. :label="item.label"
  83. :value="item.value"
  84. />
  85. </ElSelect>
  86. </ElFormItem>
  87. </ElRow>
  88. <ElRow>
  89. <ElFormItem label="手机" prop="phone">
  90. <ElInput v-model="form.phone" :disabled="!isEdit" />
  91. </ElFormItem>
  92. <ElFormItem label="邮箱" prop="email" class="right-input">
  93. <ElInput v-model="form.email" :disabled="!isEdit" />
  94. </ElFormItem>
  95. </ElRow>
  96. <ElFormItem label="个人介绍" prop="des">
  97. <ElInput type="text" v-model="form.descr" :disabled="!isEdit" />
  98. </ElFormItem>
  99. <div class="el-form-item-right">
  100. <ElButton type="primary" style="width: 90px" v-ripple @click="edit">
  101. {{ isEdit ? '保存' : '编辑' }}
  102. </ElButton>
  103. </div>
  104. </ElForm>
  105. </div>
  106. <div class="info box-style" style="margin-top: 20px">
  107. <h1 class="title">更改密码</h1>
  108. <ElForm ref="pwdFormRef" :model="pwdForm" :rules="pwdRules" class="form" label-width="86px" label-position="top">
  109. <ElFormItem label="当前密码" prop="password">
  110. <ElInput
  111. v-model="pwdForm.password"
  112. type="password"
  113. :disabled="!isEditPwd"
  114. show-password
  115. />
  116. </ElFormItem>
  117. <ElFormItem label="新密码" prop="newPassword">
  118. <ElInput
  119. v-model="pwdForm.newPassword"
  120. type="password"
  121. :disabled="!isEditPwd"
  122. show-password
  123. />
  124. </ElFormItem>
  125. <ElFormItem label="确认新密码" prop="confirmPassword">
  126. <ElInput
  127. v-model="pwdForm.confirmPassword"
  128. type="password"
  129. :disabled="!isEditPwd"
  130. show-password
  131. />
  132. </ElFormItem>
  133. <div class="el-form-item-right">
  134. <ElButton type="primary" style="width: 90px" v-ripple @click="editPwd">
  135. {{ isEditPwd ? '保存' : '编辑' }}
  136. </ElButton>
  137. </div>
  138. </ElForm>
  139. </div>
  140. </div>
  141. </div>
  142. </div>
  143. </template>
  144. <script setup lang="ts">
  145. import { useUserStore } from '@/store/modules/user'
  146. import {ElForm, ElMessage, FormInstance, FormRules, UploadProps} from 'element-plus'
  147. import {commonApi} from "@/api/commonApi";
  148. import {UserService} from "@/api/usersApi";
  149. import {computed} from "vue";
  150. defineOptions({ name: 'UserCenter' })
  151. const uploadServer = computed(() => import.meta.env.VITE_API_URL + import.meta.env.VITE_UPLOAD_URL)
  152. const userStore = useUserStore()
  153. const userInfo = computed(() => userStore.getUserInfo)
  154. const isEdit = ref(false)
  155. const isEditPwd = ref(false)
  156. const date = ref('')
  157. const form = reactive<Form.User>({
  158. username: userInfo.value.username || '',
  159. email: userInfo.value.email || '',
  160. phone: userInfo.value.phone || '',
  161. sex: userInfo.value.sex || 0,
  162. descr: userInfo.value.descr || '',
  163. })
  164. const handleSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
  165. if (response.code === 200) {
  166. ElMessage.success('上传成功!')
  167. userInfo.value.avatar = response.data.url
  168. } else {
  169. ElMessage.error(response.msg)
  170. }
  171. }
  172. const pwdForm = reactive<Form.ChangePassword>({
  173. password: '',
  174. newPassword: '',
  175. confirmPassword: ''
  176. })
  177. const ruleFormRef = ref<FormInstance>()
  178. const pwdFormRef = ref<FormInstance>()
  179. const pwdRules = reactive<FormRules>({
  180. password: [
  181. {required: true, message: '请输入密码', trigger: 'blur'},
  182. {min: 1, max: 12, message: '长度在 1 到 12 个字符', trigger: 'blur'}
  183. ],
  184. newPassword: [
  185. {required: true, message: '请输入新密码', trigger: 'blur'},
  186. {min: 6, max: 12, message: '长度在 6 到 12 个字符', trigger: 'blur'}
  187. ],
  188. confirmPassword: [{required: true, message: '请确认新密码', trigger: 'blur'}],
  189. })
  190. const rules = reactive<FormRules>({
  191. username: [
  192. { required: true, message: '请输入用户名', trigger: 'blur' },
  193. { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
  194. ],
  195. descr: [
  196. { max: 20, message: '长度最多20个字符', trigger: 'blur' }
  197. ],
  198. phone: [{ required: true, message: '请输入手机号码', trigger: 'blur' }],
  199. email: [{ type: 'email', message: '请输入正确邮箱', trigger: 'blur' }],
  200. })
  201. const options = [
  202. {
  203. value: 0,
  204. label: '男'
  205. },
  206. {
  207. value: 1,
  208. label: '女'
  209. }
  210. ]
  211. const lableList: Array<string> = ['专注设计', '很有想法', '辣~', '大长腿', '川妹子', '海纳百川']
  212. onMounted(() => {
  213. getDate()
  214. })
  215. const getDate = () => {
  216. const d = new Date()
  217. const h = d.getHours()
  218. let text = ''
  219. if (h >= 6 && h < 9) {
  220. text = '早上好'
  221. } else if (h >= 9 && h < 11) {
  222. text = '上午好'
  223. } else if (h >= 11 && h < 13) {
  224. text = '中午好'
  225. } else if (h >= 13 && h < 18) {
  226. text = '下午好'
  227. } else if (h >= 18 && h < 24) {
  228. text = '晚上好'
  229. } else if (h >= 0 && h < 6) {
  230. text = '很晚了,早点睡'
  231. }
  232. date.value = text
  233. }
  234. const edit = async () => {
  235. isEdit.value = !isEdit.value
  236. if (!isEdit.value) {
  237. if (!ruleFormRef.value) return
  238. await ruleFormRef.value.validate((valid) => {
  239. if (valid) {
  240. commonApi.editUSer(form)
  241. .then(() => {
  242. ElMessage.success('修改成功')
  243. userStore.setUserForm(form)
  244. })
  245. .catch(() => {
  246. ElMessage.error('操作失败')
  247. })
  248. }
  249. })
  250. }
  251. }
  252. const editPwd = async () => {
  253. isEditPwd.value = !isEditPwd.value
  254. if (!isEditPwd.value) {
  255. if (!pwdFormRef.value) return
  256. // 保存密码
  257. await pwdFormRef.value.validate((valid) => {
  258. if (valid) {
  259. commonApi.changePassword(pwdForm)
  260. .then(() => {
  261. ElMessage.success('密码修改成功')
  262. })
  263. .catch(() => {
  264. ElMessage.error('操作失败')
  265. })
  266. }
  267. })
  268. }
  269. }
  270. </script>
  271. <style lang="scss">
  272. .user {
  273. .icon {
  274. width: 1.4em;
  275. height: 1.4em;
  276. overflow: hidden;
  277. vertical-align: -0.15em;
  278. fill: currentcolor;
  279. }
  280. }
  281. </style>
  282. <style lang="scss" scoped>
  283. .page-content {
  284. width: 100%;
  285. height: 100%;
  286. padding: 0 !important;
  287. background: transparent !important;
  288. border: none !important;
  289. box-shadow: none !important;
  290. $box-radius: calc(var(--custom-radius) + 4px);
  291. .box-style {
  292. border: 1px solid var(--art-border-color);
  293. }
  294. .content {
  295. position: relative;
  296. display: flex;
  297. justify-content: space-between;
  298. margin-top: 10px;
  299. .left-wrap {
  300. width: 450px;
  301. margin-right: 25px;
  302. .user-wrap {
  303. position: relative;
  304. height: 600px;
  305. padding: 35px 40px;
  306. overflow: hidden;
  307. text-align: center;
  308. background: var(--art-main-bg-color);
  309. border-radius: $box-radius;
  310. .bg {
  311. position: absolute;
  312. top: 0;
  313. left: 0;
  314. width: 100%;
  315. height: 200px;
  316. object-fit: cover;
  317. }
  318. .avatar {
  319. position: relative;
  320. z-index: 10;
  321. width: 80px;
  322. height: 80px;
  323. margin-top: 120px;
  324. object-fit: cover;
  325. border: 2px solid #fff;
  326. border-radius: 50%;
  327. }
  328. .name {
  329. margin-top: 20px;
  330. font-size: 22px;
  331. font-weight: 400;
  332. }
  333. .des {
  334. margin-top: 20px;
  335. font-size: 14px;
  336. }
  337. .outer-info {
  338. width: 300px;
  339. margin: auto;
  340. margin-top: 30px;
  341. text-align: left;
  342. > div {
  343. margin-top: 10px;
  344. span {
  345. margin-left: 8px;
  346. font-size: 14px;
  347. }
  348. }
  349. }
  350. .lables {
  351. margin-top: 40px;
  352. h3 {
  353. font-size: 15px;
  354. font-weight: 500;
  355. }
  356. > div {
  357. display: flex;
  358. flex-wrap: wrap;
  359. justify-content: center;
  360. margin-top: 15px;
  361. > div {
  362. padding: 3px 6px;
  363. margin: 0 10px 10px 0;
  364. font-size: 12px;
  365. background: var(--art-main-bg-color);
  366. border: 1px solid var(--art-border-color);
  367. border-radius: 2px;
  368. }
  369. }
  370. }
  371. }
  372. .gallery {
  373. margin-top: 25px;
  374. border-radius: 10px;
  375. .item {
  376. img {
  377. width: 100%;
  378. height: 100%;
  379. object-fit: cover;
  380. }
  381. }
  382. }
  383. }
  384. .right-wrap {
  385. flex: 1;
  386. overflow: hidden;
  387. border-radius: $box-radius;
  388. .info {
  389. background: var(--art-main-bg-color);
  390. border-radius: $box-radius;
  391. .title {
  392. padding: 15px 25px;
  393. font-size: 20px;
  394. font-weight: 400;
  395. color: var(--art-text-gray-800);
  396. border-bottom: 1px solid var(--art-border-color);
  397. }
  398. .form {
  399. box-sizing: border-box;
  400. padding: 30px 25px;
  401. > .el-row {
  402. .el-form-item {
  403. width: calc(50% - 10px);
  404. }
  405. .el-input,
  406. .el-select {
  407. width: 100%;
  408. }
  409. }
  410. .right-input {
  411. margin-left: 20px;
  412. }
  413. .el-form-item-right {
  414. display: flex;
  415. align-items: center;
  416. justify-content: end;
  417. .el-button {
  418. width: 110px !important;
  419. }
  420. }
  421. }
  422. }
  423. }
  424. }
  425. }
  426. @media only screen and (max-width: $device-ipad-vertical) {
  427. .page-content {
  428. .content {
  429. display: block;
  430. margin-top: 5px;
  431. .left-wrap {
  432. width: 100%;
  433. }
  434. .right-wrap {
  435. width: 100%;
  436. margin-top: 15px;
  437. }
  438. }
  439. }
  440. }
  441. </style>