Browse Source

👣 重写前端上传组件,以及编写文件模块

master
wangxiang 3 years ago
parent
commit
7809f6c964
  1. 16
      kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/controller/FileController.java
  2. 2
      kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/FileService.java
  3. 9
      kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/impl/FileServiceImpl.java
  4. 7
      kicc-ui/src/api/platform/system/controller/file.ts
  5. 25
      kicc-ui/src/components/Upload/src/BasicUpload.vue
  6. 2
      kicc-ui/src/components/Upload/src/FileList.vue
  7. 60
      kicc-ui/src/components/Upload/src/UploadModal.vue
  8. 19
      kicc-ui/src/components/Upload/src/UploadPreviewModal.vue
  9. 11
      kicc-ui/src/components/Upload/src/data.tsx
  10. 1
      kicc-ui/src/components/Upload/src/helper.ts
  11. 12
      kicc-ui/src/components/Upload/src/props.ts
  12. 14
      kicc-ui/src/components/Upload/src/typing.ts
  13. 8
      kicc-ui/src/components/Upload/src/useUpload.ts
  14. 18
      kicc-ui/src/utils/http/axios/Axios.ts
  15. 54
      kicc-ui/src/views/system/file/file.data.ts
  16. 143
      kicc-ui/src/views/system/file/index.vue

16
kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/controller/FileController.java

@ -4,16 +4,14 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cloud.kicc.system.api.entity.File;
import com.cloud.kicc.system.api.entity.Role;
import com.cloud.kicc.system.api.entity.RoleMenu;
import com.cloud.kicc.system.service.FileService;
import com.cloud.kicc.common.core.api.R; import com.cloud.kicc.common.core.api.R;
import com.cloud.kicc.common.core.constant.AppConstants; import com.cloud.kicc.common.core.constant.AppConstants;
import com.cloud.kicc.common.log.annotation.SysLog; import com.cloud.kicc.common.log.annotation.SysLog;
import com.cloud.kicc.common.security.annotation.Inner; import com.cloud.kicc.common.security.annotation.Inner;
import com.cloud.kicc.system.api.entity.File;
import com.cloud.kicc.system.api.entity.Role;
import com.cloud.kicc.system.service.FileService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -24,7 +22,6 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
/** /**
*<p> *<p>
@ -44,7 +41,7 @@ public class FileController {
private LambdaQueryWrapper<File> getQueryWrapper(File file) { private LambdaQueryWrapper<File> getQueryWrapper(File file) {
return new LambdaQueryWrapper<File>() return new LambdaQueryWrapper<File>()
.like(StrUtil.isNotBlank(file.getFileName()), File::getFileName, file.getFileName()); .like(StrUtil.isNotBlank(file.getOriginal()), File::getOriginal, file.getOriginal());
} }
@GetMapping("/list") @GetMapping("/list")
@ -60,13 +57,14 @@ public class FileController {
} }
@Inner(false) @Inner(false)
@GetMapping("/{bucket}/{fileName}") @GetMapping("/getFile/{bucket}/{fileName}")
public void getById(@PathVariable String bucket, @PathVariable String fileName, HttpServletResponse response) { public void getById(@PathVariable String bucket, @PathVariable String fileName, HttpServletResponse response) {
fileService.getFile(bucket, fileName, response); fileService.getFile(bucket, fileName, response);
} }
/** 获取上传模板 */ /** 获取上传模板 */
@SneakyThrows @SneakyThrows
@Inner(false)
@GetMapping("/local/{fileName}") @GetMapping("/local/{fileName}")
public void localFile(@PathVariable String fileName, HttpServletResponse response) { public void localFile(@PathVariable String fileName, HttpServletResponse response) {
ClassPathResource resource = new ClassPathResource("file/" + fileName); ClassPathResource resource = new ClassPathResource("file/" + fileName);
@ -76,7 +74,7 @@ public class FileController {
@SysLog("删除文件管理") @SysLog("删除文件管理")
@DeleteMapping("/remove/{ids:[\\w,]+}") @DeleteMapping("/remove/{ids:[\\w,]+}")
@PreAuthorize("@pms.hasPermission('sys_file_del')") @PreAuthorize("@pms.hasPermission('file_del')")
@ApiOperation(value = "通过id删除文件管理", notes = "通过id删除文件管理") @ApiOperation(value = "通过id删除文件管理", notes = "通过id删除文件管理")
public R remove(@PathVariable String[] ids) { public R remove(@PathVariable String[] ids) {
for (int i = 0; i < ids.length; ++i) { for (int i = 0; i < ids.length; ++i) {

2
kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/FileService.java

@ -37,6 +37,6 @@ public interface FileService extends IService<File> {
* @param id * @param id
* @return Boolean * @return Boolean
*/ */
Boolean deleteFile(String id); void deleteFile(String id);
} }

9
kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/impl/FileServiceImpl.java

@ -3,6 +3,7 @@ package com.cloud.kicc.system.service.impl;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3Object;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -44,7 +45,7 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements Fi
Map<String, String> resultMap = new HashMap<>(4); Map<String, String> resultMap = new HashMap<>(4);
resultMap.put("bucketName", ossProperties.getBucketName()); resultMap.put("bucketName", ossProperties.getBucketName());
resultMap.put("fileName", fileName); resultMap.put("fileName", fileName);
resultMap.put("url", String.format("/admin/sys-file/%s/%s", ossProperties.getBucketName(), fileName)); resultMap.put("url", String.format("/system_proxy/system/file/%s/%s", ossProperties.getBucketName(), fileName));
try { try {
ossTemplate.putObject(ossProperties.getBucketName(), fileName, file.getContentType(), file.getInputStream()); ossTemplate.putObject(ossProperties.getBucketName(), fileName, file.getContentType(), file.getInputStream());
// 文件管理数据记录,收集管理追踪文件 // 文件管理数据记录,收集管理追踪文件
@ -69,10 +70,12 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements Fi
@Override @Override
@SneakyThrows @SneakyThrows
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Boolean deleteFile(String id) { public void deleteFile(String id) {
File file = this.getById(id); File file = this.getById(id);
if (ObjectUtil.isNotEmpty(file)) {
ossTemplate.removeObject(ossProperties.getBucketName(), file.getFileName()); ossTemplate.removeObject(ossProperties.getBucketName(), file.getFileName());
return this.removeById(id); this.removeById(id);
}
} }
/** /**

7
kicc-ui/src/api/platform/system/controller/file.ts

@ -5,10 +5,13 @@
*/ */
import type { FileParams, FileResult } from '/@/api/platform/system/entity/file'; import type { FileParams, FileResult } from '/@/api/platform/system/entity/file';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import { downloadByUrl } from '/@/utils/file/download';
import { useGlobSetting } from '/@/hooks/setting';
const { apiUrl } = useGlobSetting();
enum Api { enum Api {
list = '/system_proxy/system/file/list', list = '/system_proxy/system/file/list',
get = '/system_proxy/system/file', get = '/system_proxy/system/file/getFile',
getLocal = '/system_proxy/system/file/local', getLocal = '/system_proxy/system/file/local',
del = '/system_proxy/system/file/remove' del = '/system_proxy/system/file/remove'
} }
@ -17,7 +20,7 @@ enum Api {
export const listFile = (params?: Partial<FileParams>) => defHttp.get<FileResult>({ url: Api.list, params }, { isReturnResultResponse: true }); export const listFile = (params?: Partial<FileParams>) => defHttp.get<FileResult>({ url: Api.list, params }, { isReturnResultResponse: true });
/** 获取文件 */ /** 获取文件 */
export const getFile = (bucket: string, fileName: string) => defHttp.get({ url: `${Api.get}/${bucket}/${fileName}` }); export const getFile = (bucket: string, fileName: string) => Promise.resolve(downloadByUrl({ url: `${apiUrl}/${Api.get}/${bucket}/${fileName}`, fileName: fileName }));
/** 获取本地模板文件 */ /** 获取本地模板文件 */
export const getLocalFile = (fileName: string) => defHttp.get({ url: `${Api.getLocal}/${fileName}` }); export const getLocalFile = (fileName: string) => defHttp.get({ url: `${Api.getLocal}/${fileName}` });

25
kicc-ui/src/components/Upload/src/BasicUpload.vue

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<a-button-group> <Space>
<a-button type="primary" preIcon="carbon:cloud-upload" @click="openUploadModal"> <a-button type="primary" preIcon="carbon:cloud-upload" @click="openUploadModal">
{{ t('component.upload.upload') }} {{ t('component.upload.upload') }}
</a-button> </a-button>
@ -18,14 +18,14 @@
</template> </template>
</a-button> </a-button>
</Tooltip> </Tooltip>
</a-button-group> </Space>
<UploadModal <UploadModal
v-bind="bindValue" v-bind="bindValue"
:previewFileList="fileList" :previewFileList="fileList"
@register="registerUploadModal" @register="registerUploadModal"
@change="handleChange" @change="handleChange"
@delete="handleDelete" @delete="handleDelete"
@success="handleSuccess"
/> />
<UploadPreviewModal <UploadPreviewModal
@ -38,26 +38,26 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, watch, unref, computed } from 'vue'; import { defineComponent, ref, watch, unref, computed } from 'vue';
import UploadModal from './UploadModal.vue';
import UploadPreviewModal from './UploadPreviewModal.vue';
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
import { Tooltip } from 'ant-design-vue'; import { Tooltip, Space } from 'ant-design-vue';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { uploadContainerProps } from './props'; import { uploadContainerProps } from './props';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { isArray } from '/@/utils/is'; import { isArray } from '/@/utils/is';
import UploadModal from './UploadModal.vue';
import UploadPreviewModal from './UploadPreviewModal.vue';
export default defineComponent({ export default defineComponent({
name: 'BasicUpload', name: 'BasicUpload',
components: { UploadModal, UploadPreviewModal, Icon, Tooltip }, components: { UploadModal, Space, UploadPreviewModal, Icon, Tooltip },
props: uploadContainerProps, props: uploadContainerProps,
emits: ['change', 'delete', 'preview-delete', 'update:value'], emits: ['change', 'delete', 'success', 'preview-delete', 'update:value'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
const { t } = useI18n(); const { t } = useI18n();
// modal // modal
const [registerUploadModal, { openModal: openUploadModal }] = useModal(); const [registerUploadModal, { openModal: openUploadModal, closeModal: closeUploadModal }] = useModal();
// modal // modal
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal(); const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
@ -80,7 +80,7 @@
(value = []) => { (value = []) => {
fileList.value = isArray(value) ? value : []; fileList.value = isArray(value) ? value : [];
}, },
{ immediate: true } { immediate: true },
); );
// modal // modal
@ -97,6 +97,10 @@
emit('change', fileList.value); emit('change', fileList.value);
} }
function handleSuccess() {
emit('success', closeUploadModal);
}
function handleDelete(record: Recordable) { function handleDelete(record: Recordable) {
emit('delete', record); emit('delete', record);
} }
@ -108,6 +112,7 @@
return { return {
registerUploadModal, registerUploadModal,
openUploadModal, openUploadModal,
handleSuccess,
handleChange, handleChange,
handlePreviewChange, handlePreviewChange,
registerPreviewModal, registerPreviewModal,

2
kicc-ui/src/components/Upload/src/FileList.vue

@ -15,7 +15,7 @@
nextTick(() => { nextTick(() => {
modalFn?.redoModalHeight?.(); modalFn?.redoModalHeight?.();
}); });
} },
); );
return () => { return () => {
const { columns, actionColumn, dataSource } = props; const { columns, actionColumn, dataSource } = props;

60
kicc-ui/src/components/Upload/src/UploadModal.vue

@ -7,7 +7,8 @@
:closeFunc="handleCloseFunc" :closeFunc="handleCloseFunc"
:maskClosable="false" :maskClosable="false"
:keyboard="false" :keyboard="false"
wrapClassName="upload-modal" :showOkBtn="showUploadSaveBtn"
class="upload-modal"
:okButtonProps="getOkButtonProps" :okButtonProps="getOkButtonProps"
:cancelButtonProps="{ disabled: isUploadingRef }" :cancelButtonProps="{ disabled: isUploadingRef }"
@register="register" @register="register"
@ -35,6 +36,7 @@
:accept="getStringAccept" :accept="getStringAccept"
:multiple="multiple" :multiple="multiple"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:show-upload-list="false"
class="upload-modal-toolbar__btn" class="upload-modal-toolbar__btn"
> >
<a-button type="primary"> <a-button type="primary">
@ -46,23 +48,25 @@
</BasicModal> </BasicModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, reactive, ref, toRefs, unref, computed, PropType } from 'vue'; import {defineComponent, reactive, ref, toRefs, unref, computed, PropType, watch} from 'vue';
import { Upload, Alert } from 'ant-design-vue'; import { Upload, Alert } from 'ant-design-vue';
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
// import { BasicTable, useTable } from '/@/components/Table';
// hooks // hooks
import { useUploadType } from './useUpload'; import { useUploadType } from './useUpload';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
// types // types
import { FileItem, UploadResultStatus } from './typing'; import {FileItem, PreviewFileItem, UploadResultStatus} from './typing';
import { basicProps } from './props'; import { basicProps } from './props';
import { createTableColumns, createActionColumn } from './data'; import { createTableColumns, createActionColumn } from './data';
// utils // utils
import { checkFileType, checkImgType, getBase64WithFile } from './helper'; import { checkImgType, getBase64WithFile } from './helper';
import { buildUUID } from '/@/utils/uuid'; import { buildUUID } from '/@/utils/uuid';
import { isFunction } from '/@/utils/is'; import {isArray, isFunction} from '/@/utils/is';
import { warn } from '/@/utils/log'; import { warn } from '/@/utils/log';
import FileList from './FileList.vue'; import FileList from './FileList.vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import {useTimeoutFn} from "/@/hooks/core/useTimeout";
export default defineComponent({ export default defineComponent({
components: { BasicModal, Upload, Alert, FileList }, components: { BasicModal, Upload, Alert, FileList },
@ -73,7 +77,7 @@
default: () => [], default: () => [],
}, },
}, },
emits: ['change', 'register', 'delete'], emits: ['change', 'register', 'delete', 'success'],
setup(props, { emit }) { setup(props, { emit }) {
const state = reactive<{ fileList: FileItem[] }>({ const state = reactive<{ fileList: FileItem[] }>({
fileList: [], fileList: [],
@ -84,10 +88,17 @@
const fileListRef = ref<FileItem[]>([]); const fileListRef = ref<FileItem[]>([]);
const { accept, helpText, maxNumber, maxSize } = toRefs(props); const { accept, helpText, maxNumber, maxSize } = toRefs(props);
watch(() => props.previewFileList, (value) => {
if (!isArray(value)) value = [];
fileListRef.value = value.filter((item) => !!item);
},
{ immediate: true }
);
const { t } = useI18n(); const { t } = useI18n();
const [register, { closeModal }] = useModalInner(); const [register, { closeModal }] = useModalInner();
const { getAccept, getStringAccept, getHelpText } = useUploadType({ const { getStringAccept, getHelpText } = useUploadType({
acceptRef: accept, acceptRef: accept,
helpTextRef: helpText, helpTextRef: helpText,
maxNumberRef: maxNumber, maxNumberRef: maxNumber,
@ -105,7 +116,7 @@
const getOkButtonProps = computed(() => { const getOkButtonProps = computed(() => {
const someSuccess = fileListRef.value.some( const someSuccess = fileListRef.value.some(
(item) => item.status === UploadResultStatus.SUCCESS (item) => item.status === UploadResultStatus.SUCCESS,
); );
return { return {
disabled: isUploadingRef.value || fileListRef.value.length === 0 || !someSuccess, disabled: isUploadingRef.value || fileListRef.value.length === 0 || !someSuccess,
@ -114,7 +125,7 @@
const getUploadBtnText = computed(() => { const getUploadBtnText = computed(() => {
const someError = fileListRef.value.some( const someError = fileListRef.value.some(
(item) => item.status === UploadResultStatus.ERROR (item) => item.status === UploadResultStatus.ERROR,
); );
return isUploadingRef.value return isUploadingRef.value
? t('component.upload.uploading') ? t('component.upload.uploading')
@ -127,18 +138,12 @@
function beforeUpload(file: File) { function beforeUpload(file: File) {
const { size, name } = file; const { size, name } = file;
const { maxSize } = props; const { maxSize } = props;
const accept = unref(getAccept);
// //
if (maxSize && file.size / 1024 / 1024 >= maxSize) { if (maxSize && file.size / 1024 / 1024 >= maxSize) {
createMessage.error(t('component.upload.maxSizeMultiple', [maxSize])); createMessage.error(t('component.upload.maxSizeMultiple', [maxSize]));
return false; return false;
} }
// ,
if (accept.length > 0 && !checkFileType(file, accept)) {
createMessage.error!(t('component.upload.acceptUpload', [accept.join(',')]));
return false;
}
const commonItem = { const commonItem = {
uuid: buildUUID(), uuid: buildUUID(),
file, file,
@ -150,6 +155,7 @@
// //
if (checkImgType(file)) { if (checkImgType(file)) {
// beforeUpload // beforeUpload
// file.thumbUrl = await getBase64(file);
getBase64WithFile(file).then(({ result: thumbUrl }) => { getBase64WithFile(file).then(({ result: thumbUrl }) => {
fileListRef.value = [ fileListRef.value = [
...unref(fileListRef), ...unref(fileListRef),
@ -172,6 +178,14 @@
emit('delete', record); emit('delete', record);
} }
//
// function handlePreview(record: FileItem) {
// const { thumbUrl = '' } = record;
// createImgPreview({
// imageList: [thumbUrl],
// });
// }
async function uploadApiByItem(item: FileItem) { async function uploadApiByItem(item: FileItem) {
const { api } = props; const { api } = props;
if (!api || !isFunction(api)) { if (!api || !isFunction(api)) {
@ -181,13 +195,17 @@
item.status = UploadResultStatus.UPLOADING; item.status = UploadResultStatus.UPLOADING;
const { data } = await props.api?.( const { data } = await props.api?.(
{ {
data: {
...(props.uploadParams || {}), ...(props.uploadParams || {}),
},
file: item.file, file: item.file,
name: props.name,
filename: props.filename,
}, },
function onUploadProgress(progressEvent: ProgressEvent) { function onUploadProgress(progressEvent: ProgressEvent) {
const complete = ((progressEvent.loaded / progressEvent.total) * 100) | 0; const complete = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
item.percent = complete; item.percent = complete;
} },
); );
item.status = UploadResultStatus.SUCCESS; item.status = UploadResultStatus.SUCCESS;
item.responseData = data; item.responseData = data;
@ -219,12 +237,15 @@
const data = await Promise.all( const data = await Promise.all(
uploadFileList.map((item) => { uploadFileList.map((item) => {
return uploadApiByItem(item); return uploadApiByItem(item);
}) }),
); );
isUploadingRef.value = false; isUploadingRef.value = false;
// : // :
const errorList = data.filter((item: any) => !item.success); const errorList = data.filter((item: any) => !item.success);
if (errorList.length > 0) throw errorList; if (errorList.length > 0) throw errorList;
useTimeoutFn(() => {
emit('success');
}, 300);
} catch (e) { } catch (e) {
isUploadingRef.value = false; isUploadingRef.value = false;
throw e; throw e;
@ -270,14 +291,15 @@
} }
return { return {
columns: createTableColumns(), columns: createTableColumns() as any[],
actionColumn: createActionColumn(handleRemove), actionColumn: createActionColumn(handleRemove) as any,
register, register,
closeModal, closeModal,
getHelpText, getHelpText,
getStringAccept, getStringAccept,
getOkButtonProps, getOkButtonProps,
beforeUpload, beforeUpload,
// registerTable,
fileListRef, fileListRef,
state, state,
isUploadingRef, isUploadingRef,

19
kicc-ui/src/components/Upload/src/UploadPreviewModal.vue

@ -2,7 +2,7 @@
<BasicModal <BasicModal
width="800px" width="800px"
:title="t('component.upload.preview')" :title="t('component.upload.preview')"
wrapClassName="upload-preview-modal" class="upload-preview-modal"
v-bind="$attrs" v-bind="$attrs"
:showOkBtn="false" :showOkBtn="false"
@register="register" @register="register"
@ -12,6 +12,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, watch, ref } from 'vue'; import { defineComponent, watch, ref } from 'vue';
// import { BasicTable, useTable } from '/@/components/Table';
import FileList from './FileList.vue'; import FileList from './FileList.vue';
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import { previewProps } from './props'; import { previewProps } from './props';
@ -44,7 +45,7 @@
}; };
}); });
}, },
{ immediate: true } { immediate: true },
); );
// //
@ -55,11 +56,19 @@
emit('delete', removed[0].url); emit('delete', removed[0].url);
emit( emit(
'list-change', 'list-change',
fileListRef.value.map((item) => item.url) fileListRef.value.map((item) => item.url),
); );
} }
} }
// //
// function handlePreview(record: PreviewFileItem) {
// const { url = '' } = record;
// createImgPreview({
// imageList: [url],
// });
// }
// //
function handleDownload(record: PreviewFileItem) { function handleDownload(record: PreviewFileItem) {
const { url = '' } = record; const { url = '' } = record;
@ -71,8 +80,8 @@
register, register,
closeModal, closeModal,
fileListRef, fileListRef,
columns: createPreviewColumns(), columns: createPreviewColumns() as any[],
actionColumn: createPreviewActionColumn({ handleRemove, handleDownload }), actionColumn: createPreviewActionColumn({ handleRemove, handleDownload }) as any,
}; };
}, },
}); });

11
kicc-ui/src/components/Upload/src/data.tsx

@ -1,6 +1,9 @@
import type { BasicColumn, ActionItem } from '/@/components/Table'; import type { BasicColumn, ActionItem } from '/@/components/Table';
import { FileItem, PreviewFileItem, UploadResultStatus } from './typing'; import { FileItem, PreviewFileItem, UploadResultStatus } from './typing';
import { isImgTypeByName } from './helper'; import {
// checkImgType,
isImgTypeByName,
} from './helper';
import { Progress, Tag } from 'ant-design-vue'; import { Progress, Tag } from 'ant-design-vue';
import TableAction from '/@/components/Table/src/components/TableAction.vue'; import TableAction from '/@/components/Table/src/components/TableAction.vue';
import ThumbUrl from './ThumbUrl.vue'; import ThumbUrl from './ThumbUrl.vue';
@ -89,6 +92,12 @@ export function createActionColumn(handleRemove: Function): BasicColumn {
onClick: handleRemove.bind(null, record), onClick: handleRemove.bind(null, record),
}, },
]; ];
// if (checkImgType(record)) {
// actions.unshift({
// label: t('component.upload.preview'),
// onClick: handlePreview.bind(null, record),
// });
// }
return <TableAction actions={actions} outside={true} />; return <TableAction actions={actions} outside={true} />;
}, },
}; };

1
kicc-ui/src/components/Upload/src/helper.ts

@ -1,5 +1,6 @@
export function checkFileType(file: File, accepts: string[]) { export function checkFileType(file: File, accepts: string[]) {
const newTypes = accepts.join('|'); const newTypes = accepts.join('|');
// const reg = /\.(jpg|jpeg|png|gif|txt|doc|docx|xls|xlsx|xml)$/i;
const reg = new RegExp('\\.(' + newTypes + ')$', 'i'); const reg = new RegExp('\\.(' + newTypes + ')$', 'i');
return reg.test(file.name); return reg.test(file.name);

12
kicc-ui/src/components/Upload/src/props.ts

@ -34,6 +34,18 @@ export const basicProps = {
default: null, default: null,
required: true, required: true,
}, },
name: {
type: String as PropType<string>,
default: 'file',
},
showUploadSaveBtn: {
type: Boolean as PropType<boolean>,
default: false,
},
filename: {
type: String as PropType<string>,
default: null,
},
}; };
export const uploadContainerProps = { export const uploadContainerProps = {

14
kicc-ui/src/components/Upload/src/typing.ts

@ -1,4 +1,4 @@
import { UploadResult } from '/@/api/platform/core/entity/upload'; import { UploadApiResult } from '/@/api/sys/model/uploadModel';
export enum UploadResultStatus { export enum UploadResultStatus {
SUCCESS = 'success', SUCCESS = 'success',
@ -14,7 +14,7 @@ export interface FileItem {
percent: number; percent: number;
file: File; file: File;
status?: UploadResultStatus; status?: UploadResultStatus;
responseData?: UploadResult; responseData?: UploadApiResult;
uuid: string; uuid: string;
} }
@ -26,28 +26,28 @@ export interface PreviewFileItem {
export interface FileBasicColumn { export interface FileBasicColumn {
/** /**
* VNode colSpanrowSpan * Renderer of the table cell. The return value should be a VNode, or an object for colSpan/rowSpan config
* @type Function | ScopedSlot * @type Function | ScopedSlot
*/ */
customRender?: Function; customRender?: Function;
/** /**
* * Title of this column
* @type any (string | slot) * @type any (string | slot)
*/ */
title: string; title: string;
/** /**
* * Width of this column
* @type string | number * @type string | number
*/ */
width?: number; width?: number;
/** /**
* a.b.c * Display field of the data record, could be set like a.b.c
* @type string * @type string
*/ */
dataIndex: string; dataIndex: string;
/** /**
* * specify how content is aligned
* @default 'left' * @default 'left'
* @type string * @type string
*/ */

8
kicc-ui/src/components/Upload/src/useUpload.ts

@ -22,7 +22,13 @@ export function useUploadType({
}); });
const getStringAccept = computed(() => { const getStringAccept = computed(() => {
return unref(getAccept) return unref(getAccept)
.map((item) => `.${item}`) .map((item) => {
if (item.indexOf('/') > 0 || item.startsWith('.')) {
return item;
} else {
return `.${item}`;
}
})
.join(','); .join(',');
}); });

18
kicc-ui/src/utils/http/axios/Axios.ts

@ -115,11 +115,17 @@ export class VAxios {
/** 上传文件 */ /** 上传文件 */
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) { uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
const formData = new window.FormData(); const formData = new window.FormData();
const customFilename = params.name || 'file';
if (params.filename) {
formData.append(customFilename, params.file, params.filename);
} else {
formData.append(customFilename, params.file);
}
if (params.data) { if (params.data) {
Object.keys(params.data).forEach((key) => { Object.keys(params.data).forEach((key) => {
if (!params.data) return; const value = params.data![key];
const value = params.data[key];
if (Array.isArray(value)) { if (Array.isArray(value)) {
value.forEach((item) => { value.forEach((item) => {
formData.append(`${key}[]`, item); formData.append(`${key}[]`, item);
@ -127,15 +133,9 @@ export class VAxios {
return; return;
} }
formData.append(key, params.data[key]); formData.append(key, params.data![key]);
}); });
} }
formData.append(params.name || 'file', params.file, params.filename);
const customParams = omit(params, 'file', 'filename', 'file');
Object.keys(customParams).forEach((key) => {
formData.append(key, customParams[key]);
});
return this.axiosInstance.request<T>({ return this.axiosInstance.request<T>({
...config, ...config,

54
kicc-ui/src/views/system/file/file.data.ts

@ -0,0 +1,54 @@
/**
* @program: kicc-ui
* @description:
* @author: entfrm开发团队-
* @create: 2022/4/21
*/
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
/** 表格列配置 */
export const columns: BasicColumn[] = [
{
title: '源文件名称',
dataIndex: 'original'
},
{
title: '空间名称',
dataIndex: 'bucketName'
},
{
title: '文件名称',
dataIndex: 'fileName'
},
{
title: '文件类型',
dataIndex: 'type'
},
{
title: '文件大小',
dataIndex: 'fileSize'
},
{
title: '上传人',
dataIndex: 'createByName'
},
{
title: '创建时间',
dataIndex: 'createTime'
}
];
/** 搜索表单配置 */
export const searchFormSchema: FormSchema[] = [
{
field: 'original',
label: '源文件名称',
component: 'Input',
componentProps: {
placeholder: '请输入源文件名称',
},
colProps: { span: 6 }
}
];

143
kicc-ui/src/views/system/file/index.vue

@ -0,0 +1,143 @@
<template>
<div>
<BasicTable @register="registerTable"
@selection-change="handleSelectionChange"
>
<template #toolbar>
<BasicUpload v-model:value="state.fileList"
v-auth="['file_upload']"
:maxSize="20"
:maxNumber="10"
:showPreviewNumber="false"
:emptyHidePreview="true"
:api="commonUpload"
:accept="['image/*']"
multiple
@success="handleUploadSuccess"
/>
<a-button v-auth="['file_del']"
type="primary"
:disabled="state.multiple"
@click="handleDel()"
>删除</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="[
{
label: '文件下载',
icon: 'fa6-regular:pen-to-square',
auth: ['file_download'],
onClick: handleFileDownLoad.bind(null, record)
},
{
label: '删除',
icon: 'ant-design:delete-outlined',
color: 'error',
auth: ['file_del'],
onClick: handleDel.bind(null, record)
}]"
/>
</template>
</BasicTable>
</div>
</template>
<script lang="ts" setup>
/**
* 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致
* 采用vben-动态表格表单封装组件编写,采用 setup 写法
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔
*/
import { reactive, toRaw } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { listFile, delFile, getFile } from '/@/api/platform/system/controller/file';
import { columns, searchFormSchema } from './file.data';
import { useMessage } from '/@/hooks/web/useMessage';
import { commonUpload } from '/@/api/platform/core/controller/upload';
import { BasicUpload } from '/@/components/Upload';
/** 类型规范统一声明定义区域 */
interface TableState {
single: boolean;
multiple: boolean;
fileList: Recordable[];
}
/** 通用变量统一声明区域 */
const state = reactive<TableState>({
//
single: true,
//
multiple: true,
//
fileList: []
});
const { createConfirm, createMessage } = useMessage();
const [registerTable, { reload, clearSelectedRowKeys, getSelectRowKeys }] = useTable({
title: '文件列表',
api: listFile,
rowKey: 'id',
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true
},
rowSelection: { type: 'checkbox' },
useSearchForm: true,
showTableSetting: true,
bordered: true,
clickToRowSelect: false,
showIndexColumn: false,
actionColumn: {
width: 220,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: false
},
handleSearchInfoFn: () => clearSelectedRowKeys()
});
/** 处理多选框选中数据 */
function handleSelectionChange(selection?: Recordable) {
const rowSelection = toRaw(selection?.keys) || [];
state.single = rowSelection.length != 1;
state.multiple = !rowSelection.length;
}
/** 处理上传成功回调 */
function handleUploadSuccess(closeUploadModal: Fn) {
state.fileList = [];
closeUploadModal();
handleRefreshTable();
}
/** 处理行内文件下载 */
function handleFileDownLoad(record?: Recordable) {
getFile(record.bucketName,record.fileName).then(() => createMessage.success(`${record.fileName}文件下载成功!`));
}
/** 删除按钮操作,行内删除 */
async function handleDel(record?: Recordable) {
const ids = record?.id || getSelectRowKeys();
createConfirm({
iconType: 'warning',
title: '警告',
content: `是否确认删除文件编号为${ids}文件吗?`,
onOk: async () => {
await delFile(ids);
createMessage.success('删除成功!');
handleRefreshTable();
}
});
}
/** 处理表格刷新 */
function handleRefreshTable() {
clearSelectedRowKeys();
reload();
}
</script>
Loading…
Cancel
Save