Commit 93dd8b7b562c339b92babf10f5a4f754813bcc0e

Authored by xingyu4j
1 parent ba547e9e

feat: uploadImg 组件

yudao-ui-admin-vue3/src/components/ConfigGlobal/src/ConfigGlobal.vue
@@ -14,7 +14,7 @@ const { variables } = useDesign() @@ -14,7 +14,7 @@ const { variables } = useDesign()
14 const appStore = useAppStore() 14 const appStore = useAppStore()
15 15
16 const props = defineProps({ 16 const props = defineProps({
17 - size: propTypes.oneOf<ElementPlusSize[]>(['default', 'small', 'large']).def('default') 17 + size: propTypes.oneOf<ElementPlusSize>(['default', 'small', 'large']).def('default')
18 }) 18 })
19 19
20 provide('configGlobal', props) 20 provide('configGlobal', props)
yudao-ui-admin-vue3/src/components/UploadFile/index.ts 0 → 100644
  1 +import UploadImg from './src/UploadImg.vue'
  2 +
  3 +export { UploadImg }
yudao-ui-admin-vue3/src/components/UploadFile/src/UploadImg.vue 0 → 100644
  1 +<template>
  2 + <el-upload
  3 + ref="uploadRef"
  4 + :multiple="limit > 1"
  5 + name="file"
  6 + list-type="picture-card"
  7 + v-model:file-list="fileList"
  8 + :show-file-list="true"
  9 + :action="updateUrl"
  10 + :headers="uploadHeaders"
  11 + :limit="limit"
  12 + :before-upload="beforeUpload"
  13 + :on-exceed="handleExceed"
  14 + :on-success="handleFileSuccess"
  15 + :on-error="excelUploadError"
  16 + :on-remove="handleRemove"
  17 + :on-preview="handlePictureCardPreview"
  18 + :class="{ hide: fileList.length >= limit }"
  19 + >
  20 + <Icon icon="ep:upload-filled" />
  21 + </el-upload>
  22 + <!-- 文件列表 -->
  23 + <Dialog v-model="dialogVisible" title="预览" width="800" append-to-body>
  24 + <img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
  25 + </Dialog>
  26 +</template>
  27 +<script setup lang="ts">
  28 +import { ref, watch } from 'vue'
  29 +import { useMessage } from '@/hooks/web/useMessage'
  30 +import { propTypes } from '@/utils/propTypes'
  31 +import { getAccessToken, getTenantId } from '@/utils/auth'
  32 +import { ElUpload, UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
  33 +
  34 +const message = useMessage() // 消息弹窗
  35 +const emit = defineEmits(['input'])
  36 +
  37 +const props = defineProps({
  38 + imgs: propTypes.oneOfType([String, Object, Array]),
  39 + title: propTypes.string.def('图片上传'),
  40 + updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
  41 + fileType: propTypes.array.def(['jpg', 'png', 'gif', 'jpeg']), // 文件类型, 例如['png', 'jpg', 'jpeg']
  42 + fileSize: propTypes.number.def(5), // 大小限制(MB)
  43 + limit: propTypes.number.def(1), // 数量限制
  44 + isShowTip: propTypes.bool.def(false) // 是否显示提示
  45 +})
  46 +// ========== 上传相关 ==========
  47 +const uploadRef = ref<UploadInstance>()
  48 +const uploadList = ref<UploadUserFile[]>([])
  49 +const fileList = ref<UploadUserFile[]>([])
  50 +const uploadNumber = ref<number>(0)
  51 +const dialogImageUrl = ref()
  52 +const dialogVisible = ref(false)
  53 +const uploadHeaders = ref({
  54 + Authorization: 'Bearer ' + getAccessToken(),
  55 + 'tenant-id': getTenantId()
  56 +})
  57 +watch(
  58 + () => props.imgs,
  59 + (val) => {
  60 + if (val) {
  61 + // 首先将值转为数组, 当只穿了一个图片时,会报map方法错误
  62 + const list = Array.isArray(props.imgs)
  63 + ? props.imgs
  64 + : Array.isArray(props.imgs?.split(','))
  65 + ? props.imgs?.split(',')
  66 + : Array.of(props.imgs)
  67 + // 然后将数组转为对象数组
  68 + fileList.value = list.map((item) => {
  69 + if (typeof item === 'string') {
  70 + // edit by 芋道源码
  71 + item = { name: item, url: item }
  72 + }
  73 + return item
  74 + })
  75 + } else {
  76 + fileList.value = []
  77 + return []
  78 + }
  79 + },
  80 + {
  81 + deep: true,
  82 + immediate: true
  83 + }
  84 +)
  85 +// 文件上传之前判断
  86 +const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
  87 + let fileExtension = ''
  88 + if (file.name.lastIndexOf('.') > -1) {
  89 + fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
  90 + }
  91 + const isImg = props.fileType.some((type: string) => {
  92 + if (file.type.indexOf(type) > -1) return true
  93 + return !!(fileExtension && fileExtension.indexOf(type) > -1)
  94 + })
  95 + const isLimit = file.size < props.fileSize * 1024 * 1024
  96 + if (!isImg) {
  97 + message.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式!`)
  98 + return false
  99 + }
  100 + if (!isLimit) {
  101 + message.error(`上传文件大小不能超过${props.fileSize}MB!`)
  102 + return false
  103 + }
  104 + message.success('正在上传文件,请稍候...')
  105 + uploadNumber.value++
  106 +}
  107 +// 处理上传的文件发生变化
  108 +// const handleFileChange = (uploadFile: UploadFile): void => {
  109 +// uploadRef.value.data.path = uploadFile.name
  110 +// }
  111 +// 文件上传成功
  112 +const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
  113 + message.success('上传成功')
  114 + console.info(uploadList.value)
  115 + console.info(fileList.value)
  116 + uploadList.value.push({ name: res.data, url: res.data })
  117 + if (uploadList.value.length == uploadNumber.value) {
  118 + fileList.value = fileList.value.concat(uploadList.value)
  119 + uploadList.value = []
  120 + uploadNumber.value = 0
  121 + emit('input', listToString(fileList.value))
  122 + }
  123 +}
  124 +// 文件数超出提示
  125 +const handleExceed: UploadProps['onExceed'] = (): void => {
  126 + message.error(`上传文件数量不能超过${props.limit}个!`)
  127 +}
  128 +// 上传错误提示
  129 +const excelUploadError: UploadProps['onError'] = (): void => {
  130 + message.error('导入数据失败,请您重新上传!')
  131 +}
  132 +// 删除上传文件
  133 +const handleRemove = (file) => {
  134 + const findex = fileList.value.map((f) => f.name).indexOf(file.name)
  135 + if (findex > -1) {
  136 + fileList.value.splice(findex, 1)
  137 + emit('input', listToString(fileList.value))
  138 + }
  139 +}
  140 +// 对象转成指定字符串分隔
  141 +const listToString = (list: UploadUserFile[], separator?: string) => {
  142 + let strs = ''
  143 + separator = separator || ','
  144 + for (let i in list) {
  145 + strs += list[i].url + separator
  146 + }
  147 + return strs != '' ? strs.substr(0, strs.length - 1) : ''
  148 +}
  149 +// 预览
  150 +const handlePictureCardPreview: UploadProps['onPreview'] = (file) => {
  151 + dialogImageUrl.value = file.url
  152 + dialogVisible.value = true
  153 +}
  154 +</script>
  155 +<style scoped lang="scss">
  156 +// .el-upload--picture-card 控制加号部分
  157 +:deep(.hide .el-upload--picture-card) {
  158 + display: none;
  159 +}
  160 +// 去掉动画效果
  161 +:deep(.el-list-enter-active, .el-list-leave-active) {
  162 + transition: all 0s;
  163 +}
  164 +
  165 +:deep(.el-list-enter, .el-list-leave-active) {
  166 + opacity: 0;
  167 + transform: translateY(0);
  168 +}
  169 +</style>
yudao-ui-admin-vue3/src/views/system/oauth2/client/client.data.ts
@@ -12,7 +12,6 @@ export const rules = reactive({ @@ -12,7 +12,6 @@ export const rules = reactive({
12 clientId: [required], 12 clientId: [required],
13 secret: [required], 13 secret: [required],
14 name: [required], 14 name: [required],
15 - logo: [required],  
16 status: [required], 15 status: [required],
17 accessTokenValiditySeconds: [required], 16 accessTokenValiditySeconds: [required],
18 refreshTokenValiditySeconds: [required], 17 refreshTokenValiditySeconds: [required],
yudao-ui-admin-vue3/src/views/system/oauth2/client/index.vue
@@ -61,7 +61,11 @@ @@ -61,7 +61,11 @@
61 v-if="['create', 'update'].includes(actionType)" 61 v-if="['create', 'update'].includes(actionType)"
62 :schema="allSchemas.formSchema" 62 :schema="allSchemas.formSchema"
63 :rules="rules" 63 :rules="rules"
64 - /> 64 + >
  65 + <template #logo>
  66 + <UploadImg :imgs="uploadLogo" :limit="1" />
  67 + </template>
  68 + </Form>
65 <!-- 表单:详情 --> 69 <!-- 表单:详情 -->
66 <Descriptions 70 <Descriptions
67 v-if="actionType === 'detail'" 71 v-if="actionType === 'detail'"
@@ -138,6 +142,7 @@ import { useMessage } from &#39;@/hooks/web/useMessage&#39; @@ -138,6 +142,7 @@ import { useMessage } from &#39;@/hooks/web/useMessage&#39;
138 import { useVxeGrid } from '@/hooks/web/useVxeGrid' 142 import { useVxeGrid } from '@/hooks/web/useVxeGrid'
139 import { VxeGridInstance } from 'vxe-table' 143 import { VxeGridInstance } from 'vxe-table'
140 import { FormExpose } from '@/components/Form' 144 import { FormExpose } from '@/components/Form'
  145 +import { UploadImg } from '@/components/UploadFile'
141 // 业务相关的 import 146 // 业务相关的 import
142 import * as ClientApi from '@/api/system/oauth2/client' 147 import * as ClientApi from '@/api/system/oauth2/client'
143 import { rules, allSchemas } from './client.data' 148 import { rules, allSchemas } from './client.data'
@@ -159,7 +164,7 @@ const actionType = ref(&#39;&#39;) // 操作按钮的类型 @@ -159,7 +164,7 @@ const actionType = ref(&#39;&#39;) // 操作按钮的类型
159 const actionLoading = ref(false) // 按钮 Loading 164 const actionLoading = ref(false) // 按钮 Loading
160 const formRef = ref<FormExpose>() // 表单 Ref 165 const formRef = ref<FormExpose>() // 表单 Ref
161 const detailRef = ref() // 详情 Ref 166 const detailRef = ref() // 详情 Ref
162 - 167 +const uploadLogo = ref('')
163 // 设置标题 168 // 设置标题
164 const setDialogTile = (type: string) => { 169 const setDialogTile = (type: string) => {
165 dialogTitle.value = t('action.' + type) 170 dialogTitle.value = t('action.' + type)
@@ -169,6 +174,7 @@ const setDialogTile = (type: string) =&gt; { @@ -169,6 +174,7 @@ const setDialogTile = (type: string) =&gt; {
169 174
170 // 新增操作 175 // 新增操作
171 const handleCreate = () => { 176 const handleCreate = () => {
  177 + uploadLogo.value = ''
172 setDialogTile('create') 178 setDialogTile('create')
173 } 179 }
174 180
@@ -177,6 +183,7 @@ const handleUpdate = async (rowId: number) =&gt; { @@ -177,6 +183,7 @@ const handleUpdate = async (rowId: number) =&gt; {
177 setDialogTile('update') 183 setDialogTile('update')
178 // 设置数据 184 // 设置数据
179 const res = await ClientApi.getOAuth2ClientApi(rowId) 185 const res = await ClientApi.getOAuth2ClientApi(rowId)
  186 + uploadLogo.value = res.logo
180 unref(formRef)?.setValues(res) 187 unref(formRef)?.setValues(res)
181 } 188 }
182 189
@@ -202,6 +209,7 @@ const submitForm = async () =&gt; { @@ -202,6 +209,7 @@ const submitForm = async () =&gt; {
202 // 提交请求 209 // 提交请求
203 try { 210 try {
204 const data = unref(formRef)?.formModel as ClientApi.OAuth2ClientVO 211 const data = unref(formRef)?.formModel as ClientApi.OAuth2ClientVO
  212 + data.logo = uploadLogo.value
205 if (actionType.value === 'create') { 213 if (actionType.value === 'create') {
206 await ClientApi.createOAuth2ClientApi(data) 214 await ClientApi.createOAuth2ClientApi(data)
207 message.success(t('common.createSuccess')) 215 message.success(t('common.createSuccess'))