houyaf il y a 3 ans
Parent
commit
7d9c3ae5f6

+ 2 - 2
src/api/system/uploadApi.ts

@@ -6,7 +6,7 @@ import apiRequest from '@/network/http/apiRequest'
  */
 export function uploadFile(data: any) {
   return apiRequest({
-    url: '/admin/uploadFile',
+    url: '/sys/upload/file',
     method: 'POST',
     data,
   })
@@ -17,7 +17,7 @@ export function uploadFile(data: any) {
  */
 export function uploadFiles(data: any) {
   return apiRequest({
-    url: '/admin/uploadFiles',
+    url: '/sys/upload/files',
     method: 'POST',
     data,
   })

+ 269 - 0
src/components/AvatarUpload/index.vue

@@ -0,0 +1,269 @@
+<script lang="ts" setup name="AvatarUpload">
+import type { UploadProps } from 'element-plus'
+import { ElMessage } from 'element-plus'
+import { uploadFile } from '@/api/system/uploadApi'
+
+const props = defineProps({
+  headers: {
+    type: Object,
+    default: () => {},
+  },
+  data: {
+    type: Object,
+    default: () => {},
+  },
+  name: {
+    type: String,
+    default: 'file',
+  },
+  url: {
+    type: String,
+    default: '',
+  },
+  size: {
+    type: Number,
+    default: 2,
+  },
+  width: {
+    type: Number,
+    default: 150,
+  },
+  height: {
+    type: Number,
+    default: 150,
+  },
+  placeholder: {
+    type: String,
+    default: '',
+  },
+  notip: {
+    type: Boolean,
+    default: false,
+  },
+  ext: {
+    type: Array,
+    default: () => ['jpg', 'png', 'gif', 'bmp'],
+  },
+})
+
+const emit = defineEmits(['update:url', 'onSuccess'])
+
+const uploadData = ref({
+  imageViewerVisible: false,
+  progress: {
+    preview: '',
+    percent: 0,
+  },
+})
+
+// 预览
+function preview() {
+  uploadData.value.imageViewerVisible = true
+}
+
+// 关闭预览
+function previewClose() {
+  uploadData.value.imageViewerVisible = false
+}
+
+// 移除
+function remove() {
+  emit('update:url', '')
+}
+
+const beforeUpload: UploadProps['beforeUpload'] = (file) => {
+  const fileName = file.name.split('.')
+  const fileExt = fileName.at(-1)
+  const isTypeOk = props.ext.includes(fileExt)
+  const isSizeOk = file.size / 1024 / 1024 < props.size
+  if (!isTypeOk) {
+    ElMessage.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
+  }
+  if (!isSizeOk) {
+    ElMessage.error(`上传图片大小不能超过 ${props.size}MB!`)
+  }
+  if (isTypeOk && isSizeOk) {
+    uploadData.value.progress.preview = URL.createObjectURL(file)
+  }
+  return isTypeOk && isSizeOk
+}
+
+const onUpload = async (param: any) => {
+  const formData = new FormData()
+  formData.append('uploadfile', param.file)
+  const data = await uploadFile(formData)
+  emit('onSuccess', data)
+}
+
+const onSuccess: UploadProps['onSuccess'] = (res) => {
+  uploadData.value.progress.preview = ''
+  uploadData.value.progress.percent = 0
+}
+</script>
+
+<template>
+  <div class="upload-container">
+    <el-upload
+      :show-file-list="false"
+      :headers="headers"
+      action="/sys/upload/file"
+      :data="data"
+      :name="name"
+      :before-upload="beforeUpload"
+      :http-request="onUpload"
+      :auto-upload="true"
+      drag
+      class="image-upload"
+    >
+      <el-image v-if="url === ''" :src="url === '' ? placeholder : url" :style="`width:${width}px;height:${height}px;`" fit="fill">
+        <template #error>
+          <div class="image-slot" :style="`width:${width}px;height:${height}px;`">
+            <el-icon>
+              <svg-icon name="i-ep:plus" />
+            </el-icon>
+          </div>
+        </template>
+      </el-image>
+      <div v-else class="image">
+        <el-image :src="url" :style="`width:${width}px;height:${height}px;`" fit="fill" />
+        <div class="mask">
+          <div class="actions">
+            <span title="预览" @click.stop="preview">
+              <el-icon>
+                <svg-icon name="i-ep:zoom-in" />
+              </el-icon>
+            </span>
+            <span title="移除" @click.stop="remove">
+              <el-icon>
+                <svg-icon name="i-ep:delete" />
+              </el-icon>
+            </span>
+          </div>
+        </div>
+      </div>
+      <div v-show="url === '' && uploadData.progress.percent" class="progress" :style="`width:${width}px;height:${height}px;`">
+        <el-image :src="uploadData.progress.preview" :style="`width:${width}px;height:${height}px;`" fit="fill" />
+        <el-progress type="circle" :width="Math.min(width, height) * 0.8" :percentage="uploadData.progress.percent" />
+      </div>
+    </el-upload>
+    <div v-if="!notip" class="el-upload__tip">
+      <div style="display: inline-block;">
+        <el-alert :title="`上传图片支持 ${ext.join(' / ')} 格式,且图片大小不超过 ${size}MB,建议图片尺寸为 ${width}*${height}`" type="info" show-icon :closable="false" />
+      </div>
+    </div>
+    <el-image-viewer v-if="uploadData.imageViewerVisible" :url-list="[url]" teleported @close="previewClose" />
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.upload-container {
+  line-height: initial;
+}
+
+.el-image {
+  display: block;
+}
+
+.image {
+  position: relative;
+  border-radius: 6px;
+  overflow: hidden;
+
+  .mask {
+    opacity: 0;
+    position: absolute;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    background-color: var(--el-overlay-color-lighter);
+    transition: opacity 0.3s;
+
+    .actions {
+      width: 100px;
+      height: 100px;
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      justify-content: center;
+
+      @include position-center(xy);
+
+      span {
+        width: 50%;
+        text-align: center;
+        cursor: pointer;
+        color: var(--el-color-white);
+        transition: color 0.1s, transform 0.1s;
+
+        &:hover {
+          transform: scale(1.5);
+        }
+
+        .el-icon {
+          font-size: 24px;
+        }
+      }
+    }
+  }
+
+  &:hover .mask {
+    opacity: 1;
+  }
+}
+
+.image-upload {
+  display: inline-block;
+  vertical-align: top;
+}
+
+:deep(.el-upload) {
+  .el-upload-dragger {
+    display: inline-block;
+    border-radius: 50%;
+    padding: 0;
+
+    &.is-dragover {
+      border-width: 1px;
+    }
+
+    .image-slot {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      width: 100%;
+      height: 100%;
+      color: var(--el-text-color-placeholder);
+      background-color: transparent;
+
+      i {
+        font-size: 30px;
+      }
+    }
+
+    .progress {
+      position: absolute;
+      top: 0;
+
+      &::after {
+        content: "";
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        left: 0;
+        top: 0;
+        background-color: var(--el-overlay-color-lighter);
+      }
+
+      .el-progress {
+        z-index: 1;
+
+        @include position-center(xy);
+
+        .el-progress__text {
+          color: var(--el-text-color-placeholder);
+        }
+      }
+    }
+  }
+}
+</style>

+ 5 - 0
src/components/ImageUpload/index.vue

@@ -63,14 +63,17 @@ const uploadData = ref({
 function preview() {
   uploadData.value.imageViewerVisible = true
 }
+
 // 关闭预览
 function previewClose() {
   uploadData.value.imageViewerVisible = false
 }
+
 // 移除
 function remove() {
   emit('update:url', '')
 }
+
 const beforeUpload: UploadProps['beforeUpload'] = (file) => {
   const fileName = file.name.split('.')
   const fileExt = fileName.at(-1)
@@ -87,9 +90,11 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
   }
   return isTypeOk && isSizeOk
 }
+
 const onProgress: UploadProps['onProgress'] = (file) => {
   uploadData.value.progress.percent = ~~file.percent
 }
+
 const onSuccess: UploadProps['onSuccess'] = (res) => {
   uploadData.value.progress.preview = ''
   uploadData.value.progress.percent = 0

+ 1 - 0
src/types/components.d.ts

@@ -12,6 +12,7 @@ declare module '@vue/runtime-core' {
     AppCatSelector: typeof import('./../components/AppCatSelector/index.vue')['default']
     Auth: typeof import('./../components/Auth/index.vue')['default']
     AuthAll: typeof import('./../components/AuthAll/index.vue')['default']
+    AvatarUpload: typeof import('./../components/AvatarUpload/index.vue')['default']
     BatchActionBar: typeof import('./../components/BatchActionBar/index.vue')['default']
     BizObejctSelector: typeof import('./../components/BizObejctSelector/index.vue')['default']
     BizObjectAttrSelector: typeof import('./../components/BizObjectAttrSelector/index.vue')['default']

+ 76 - 83
src/views/personal/setting.vue

@@ -5,11 +5,8 @@ meta:
   cache: personal-edit.password
 </route>
 
-<script lang="ts" setup name="PersonalSetting">
-import type { UploadProps } from 'element-plus'
-import { ElMessage } from 'element-plus'
-
-const router = useRouter()
+<script lang="ts" setup name="PersonalProfile">
+import type { FormInstance, FormRules, UploadProps } from 'element-plus'
 
 const state = reactive({
   loading: false,
@@ -29,26 +26,61 @@ const state = reactive({
   },
 })
 
-const form = ref({
-  headimg: '',
-  mobile: '',
-  name: '',
-  qq: '',
-  wechat: '',
-})
+const router = useRouter()
+const formRef = ref<FormInstance>()
 
-const handleSuccess: UploadProps['onSuccess'] = (res) => {
-  if (res.error === '') {
-    form.value.headimg = res.data.path
+const validatePass2 = (rule: any, value: any, callback: any) => {
+  if (value === '') {
+    callback(new Error('请再次输入密码'))
+  }
+  else if (value !== state.passData.password) {
+    callback(new Error('两次输入密码不一致!'))
   }
   else {
-    ElMessage.warning(res.error)
+    callback()
   }
 }
-const editPassword = () => {
-  router.push({
-    name: 'personalEditPassword',
-  })
+
+const formRules = ref<FormRules>({
+  accountName: [
+    { required: true, message: '请输入用户名', trigger: 'blur' },
+  ],
+  accountPhone: [
+    { required: true, message: '请输入手机号', trigger: 'blur' },
+  ],
+  accountStaffNo: [
+    { required: false, message: '请输入工号', trigger: 'blur' },
+  ],
+  password: [
+    { required: true, message: '请输入密码', trigger: 'blur' },
+  ],
+  rePassword: [
+    { required: true, validator: validatePass2, trigger: 'blur' },
+  ],
+})
+
+const basicFormRef = ref()
+const passFormRef = ref()
+
+const uploadSuccess: UploadProps['onSuccess'] = (res) => {
+  state.basicData.accountAvatar = res.data.fileUrl
+}
+
+function submitForm(formName: string) {
+  if (formName === 'basic') {
+    basicFormRef.value && basicFormRef.value.validate((valid) => {
+      if (valid) {
+        window.console.log('abc')
+      }
+    })
+  }
+  else {
+    passFormRef.value && passFormRef.value.validate((valid) => {
+      if (valid) {
+        window.console.log('abc')
+      }
+    })
+  }
 }
 </script>
 
@@ -60,19 +92,16 @@ const editPassword = () => {
           <h2>基本设置</h2>
           <el-row :gutter="20">
             <el-col :span="8">
-              <image-upload
-                v-model:url="form.headimg"
-                action="http://scrm.1daas.com/api/upload/upload"
+              <avatar-upload
+                v-model:url="state.basicData.accountAvatar"
                 name="image"
-                :data="{ token: 'TKD628431923530324' }"
                 notip
-                class="headimg-upload"
-                @on-success="handleSuccess"
+                @on-success="uploadSuccess"
               />
             </el-col>
 
             <el-col :span="16">
-              <el-form ref="formRef" :model="state.basicData" label-width="120px" label-suffix=":">
+              <el-form ref="basicFormRef" :model="state.basicData" :rules="formRules" label-width="120px" label-suffix=":">
                 <el-form-item label="帐号名称" prop="accountName">
                   <el-input v-model="state.basicData.accountName" />
                 </el-form-item>
@@ -93,56 +122,30 @@ const editPassword = () => {
                   <el-input v-model="state.basicData.accountIntro" type="textarea" :rows="8" />
                 </el-form-item>
               </el-form>
+              <div class="btn-group">
+                <el-button type="primary" @click="submitForm('basicForm')">
+                  更新
+                </el-button>
+              </div>
             </el-col>
           </el-row>
         </el-tab-pane>
-        <el-tab-pane label="安全设置" class="security">
-          <h2>安全设置</h2>
+        <el-tab-pane label="修改密码" class="security">
+          <h2>修改密码</h2>
           <div class="setting-list">
-            <div class="item">
-              <div class="content">
-                <div class="title">
-                  账户密码
-                </div>
-                <div class="desc">
-                  当前密码强度:强
-                </div>
-              </div>
-              <div class="action">
-                <el-button type="primary" text @click="editPassword">
-                  修改
-                </el-button>
-              </div>
-            </div>
-            <div class="item">
-              <div class="content">
-                <div class="title">
-                  密保手机
-                </div>
-                <div class="desc">
-                  已绑定手机:187****3441
-                </div>
-              </div>
-              <div class="action">
-                <el-button type="primary" text>
-                  修改
-                </el-button>
-              </div>
-            </div>
-            <div class="item">
-              <div class="content">
-                <div class="title">
-                  备用邮箱
-                </div>
-                <div class="desc">
-                  当前未绑定备用邮箱
-                </div>
-              </div>
-              <div class="action">
-                <el-button type="primary" text>
-                  绑定
-                </el-button>
-              </div>
+            <el-form ref="passFormRef" :model="state.passData" :rules="formRules" label-position="right" label-width="130px">
+              <el-form-item label="输入新密码" prop="password">
+                <el-input v-model="state.passData.password" />
+              </el-form-item>
+
+              <el-form-item label="再输入一遍" prop="rePassword">
+                <el-input v-model="state.passData.rePassword" />
+              </el-form-item>
+            </el-form>
+            <div class="btn-group">
+              <el-button type="primary" @click="submitForm('passForm')">
+                更新
+              </el-button>
             </div>
           </div>
         </el-tab-pane>
@@ -179,16 +182,6 @@ h2 {
   font-weight: normal;
 }
 
-.basic {
-  :deep(.headimg-upload) {
-    text-align: center;
-
-    .el-upload-dragger {
-      border-radius: 50%;
-    }
-  }
-}
-
 .security {
   .setting-list {
     .item {