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. 11
      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. 27
      kicc-ui/src/components/Upload/src/BasicUpload.vue
  6. 82
      kicc-ui/src/components/Upload/src/FileList.vue
  7. 38
      kicc-ui/src/components/Upload/src/ThumbUrl.vue
  8. 112
      kicc-ui/src/components/Upload/src/UploadModal.vue
  9. 51
      kicc-ui/src/components/Upload/src/UploadPreviewModal.vue
  10. 17
      kicc-ui/src/components/Upload/src/data.tsx
  11. 1
      kicc-ui/src/components/Upload/src/helper.ts
  12. 12
      kicc-ui/src/components/Upload/src/props.ts
  13. 14
      kicc-ui/src/components/Upload/src/typing.ts
  14. 18
      kicc-ui/src/components/Upload/src/useUpload.ts
  15. 18
      kicc-ui/src/utils/http/axios/Axios.ts
  16. 54
      kicc-ui/src/views/system/file/file.data.ts
  17. 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; @@ -4,16 +4,14 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
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.constant.AppConstants;
import com.cloud.kicc.common.log.annotation.SysLog;
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.ApiOperation;
import lombok.AllArgsConstructor;
@ -24,7 +22,6 @@ import org.springframework.web.bind.annotation.*; @@ -24,7 +22,6 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
/**
*<p>
@ -44,7 +41,7 @@ public class FileController { @@ -44,7 +41,7 @@ public class FileController {
private LambdaQueryWrapper<File> getQueryWrapper(File 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")
@ -60,13 +57,14 @@ public class FileController { @@ -60,13 +57,14 @@ public class FileController {
}
@Inner(false)
@GetMapping("/{bucket}/{fileName}")
@GetMapping("/getFile/{bucket}/{fileName}")
public void getById(@PathVariable String bucket, @PathVariable String fileName, HttpServletResponse response) {
fileService.getFile(bucket, fileName, response);
}
/** 获取上传模板 */
@SneakyThrows
@Inner(false)
@GetMapping("/local/{fileName}")
public void localFile(@PathVariable String fileName, HttpServletResponse response) {
ClassPathResource resource = new ClassPathResource("file/" + fileName);
@ -76,7 +74,7 @@ public class FileController { @@ -76,7 +74,7 @@ public class FileController {
@SysLog("删除文件管理")
@DeleteMapping("/remove/{ids:[\\w,]+}")
@PreAuthorize("@pms.hasPermission('sys_file_del')")
@PreAuthorize("@pms.hasPermission('file_del')")
@ApiOperation(value = "通过id删除文件管理", notes = "通过id删除文件管理")
public R remove(@PathVariable String[] ids) {
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> { @@ -37,6 +37,6 @@ public interface FileService extends IService<File> {
* @param id
* @return Boolean
*/
Boolean deleteFile(String id);
void deleteFile(String id);
}

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

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

@ -5,10 +5,13 @@ @@ -5,10 +5,13 @@
*/
import type { FileParams, FileResult } from '/@/api/platform/system/entity/file';
import { defHttp } from '/@/utils/http/axios';
import { downloadByUrl } from '/@/utils/file/download';
import { useGlobSetting } from '/@/hooks/setting';
const { apiUrl } = useGlobSetting();
enum Api {
list = '/system_proxy/system/file/list',
get = '/system_proxy/system/file',
get = '/system_proxy/system/file/getFile',
getLocal = '/system_proxy/system/file/local',
del = '/system_proxy/system/file/remove'
}
@ -17,7 +20,7 @@ enum Api { @@ -17,7 +20,7 @@ enum Api {
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}` });

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

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

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

@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
nextTick(() => {
modalFn?.redoModalHeight?.();
});
}
},
);
return () => {
const { columns, actionColumn, dataSource } = props;
@ -33,35 +33,35 @@ @@ -33,35 +33,35 @@
})}
</colgroup>
<thead>
<tr class="file-table-tr">
{columnList.map((item) => {
const { title = '', align = 'center', dataIndex } = item;
return (
<tr class="file-table-tr">
{columnList.map((item) => {
const { title = '', align = 'center', dataIndex } = item;
return (
<th class={['file-table-th', align]} key={dataIndex}>
{title}
</th>
);
})}
</tr>
);
})}
</tr>
</thead>
<tbody>
{dataSource.map((record = {}, index) => {
return (
{dataSource.map((record = {}, index) => {
return (
<tr class="file-table-tr" key={`${index + record.name || ''}`}>
{columnList.map((item) => {
const { dataIndex = '', customRender, align = 'center' } = item;
const render = customRender && isFunction(customRender);
return (
<td class={['file-table-td', align]} key={dataIndex}>
{render
? customRender?.({ text: record[dataIndex], record })
: record[dataIndex]}
</td>
<td class={['file-table-td', align]} key={dataIndex}>
{render
? customRender?.({ text: record[dataIndex], record })
: record[dataIndex]}
</td>
);
})}
</tr>
);
})}
);
})}
</tbody>
</table>
);
@ -70,35 +70,35 @@ @@ -70,35 +70,35 @@
});
</script>
<style lang="less">
.file-table {
width: 100%;
border-collapse: collapse;
.file-table {
width: 100%;
border-collapse: collapse;
.center {
text-align: center;
}
.center {
text-align: center;
}
.left {
text-align: left;
}
.left {
text-align: left;
}
.right {
text-align: right;
}
.right {
text-align: right;
}
&-th,
&-td {
padding: 12px 8px;
}
&-th,
&-td {
padding: 12px 8px;
}
thead {
background-color: @background-color-light;
}
thead {
background-color: @background-color-light;
}
table,
td,
th {
border: 1px solid @border-color-base;
}
table,
td,
th {
border: 1px solid @border-color-base;
}
}
</style>

38
kicc-ui/src/components/Upload/src/ThumbUrl.vue

@ -1,29 +1,29 @@ @@ -1,29 +1,29 @@
<template>
<span class="thumb">
<Image v-if="fileUrl" :src="fileUrl" :width="104"/>
<Image v-if="fileUrl" :src="fileUrl" :width="104" />
</span>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { propTypes } from '/@/utils/propTypes';
import { Image } from 'ant-design-vue';
import { defineComponent } from 'vue';
import { propTypes } from '/@/utils/propTypes';
import { Image } from 'ant-design-vue';
export default defineComponent({
components: { Image },
props: {
fileUrl: propTypes.string.def(''),
fileName: propTypes.string.def(''),
},
});
export default defineComponent({
components: { Image },
props: {
fileUrl: propTypes.string.def(''),
fileName: propTypes.string.def(''),
},
});
</script>
<style lang="less">
.thumb {
img {
position: static;
display: block;
cursor: zoom-in;
border-radius: 4px;
object-fit: cover;
}
.thumb {
img {
position: static;
display: block;
cursor: zoom-in;
border-radius: 4px;
object-fit: cover;
}
}
</style>

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

@ -7,7 +7,8 @@ @@ -7,7 +7,8 @@
:closeFunc="handleCloseFunc"
:maskClosable="false"
:keyboard="false"
wrapClassName="upload-modal"
:showOkBtn="showUploadSaveBtn"
class="upload-modal"
:okButtonProps="getOkButtonProps"
:cancelButtonProps="{ disabled: isUploadingRef }"
@register="register"
@ -35,6 +36,7 @@ @@ -35,6 +36,7 @@
:accept="getStringAccept"
:multiple="multiple"
:before-upload="beforeUpload"
:show-upload-list="false"
class="upload-modal-toolbar__btn"
>
<a-button type="primary">
@ -46,23 +48,25 @@ @@ -46,23 +48,25 @@
</BasicModal>
</template>
<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 { BasicModal, useModalInner } from '/@/components/Modal';
// import { BasicTable, useTable } from '/@/components/Table';
// hooks
import { useUploadType } from './useUpload';
import { useMessage } from '/@/hooks/web/useMessage';
// types
import { FileItem, UploadResultStatus } from './typing';
// types
import {FileItem, PreviewFileItem, UploadResultStatus} from './typing';
import { basicProps } from './props';
import { createTableColumns, createActionColumn } from './data';
// utils
import { checkFileType, checkImgType, getBase64WithFile } from './helper';
import { checkImgType, getBase64WithFile } from './helper';
import { buildUUID } from '/@/utils/uuid';
import { isFunction } from '/@/utils/is';
import {isArray, isFunction} from '/@/utils/is';
import { warn } from '/@/utils/log';
import FileList from './FileList.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import {useTimeoutFn} from "/@/hooks/core/useTimeout";
export default defineComponent({
components: { BasicModal, Upload, Alert, FileList },
@ -73,21 +77,28 @@ @@ -73,21 +77,28 @@
default: () => [],
},
},
emits: ['change', 'register', 'delete'],
emits: ['change', 'register', 'delete', 'success'],
setup(props, { emit }) {
const state = reactive<{ fileList: FileItem[] }>({
fileList: [],
});
//
//
const isUploadingRef = ref(false);
const fileListRef = ref<FileItem[]>([]);
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 [register, { closeModal }] = useModalInner();
const { getAccept, getStringAccept, getHelpText } = useUploadType({
const { getStringAccept, getHelpText } = useUploadType({
acceptRef: accept,
helpTextRef: helpText,
maxNumberRef: maxNumber,
@ -98,14 +109,14 @@ @@ -98,14 +109,14 @@
const getIsSelectFile = computed(() => {
return (
fileListRef.value.length > 0 &&
!fileListRef.value.every((item) => item.status === UploadResultStatus.SUCCESS)
fileListRef.value.length > 0 &&
!fileListRef.value.every((item) => item.status === UploadResultStatus.SUCCESS)
);
});
const getOkButtonProps = computed(() => {
const someSuccess = fileListRef.value.some(
(item) => item.status === UploadResultStatus.SUCCESS
(item) => item.status === UploadResultStatus.SUCCESS,
);
return {
disabled: isUploadingRef.value || fileListRef.value.length === 0 || !someSuccess,
@ -114,31 +125,25 @@ @@ -114,31 +125,25 @@
const getUploadBtnText = computed(() => {
const someError = fileListRef.value.some(
(item) => item.status === UploadResultStatus.ERROR
(item) => item.status === UploadResultStatus.ERROR,
);
return isUploadingRef.value
? t('component.upload.uploading')
: someError
? t('component.upload.reUploadFailed')
: t('component.upload.startUpload');
? t('component.upload.reUploadFailed')
: t('component.upload.startUpload');
});
//
function beforeUpload(file: File) {
const { size, name } = file;
const { maxSize } = props;
const accept = unref(getAccept);
//
if (maxSize && file.size / 1024 / 1024 >= maxSize) {
createMessage.error(t('component.upload.maxSizeMultiple', [maxSize]));
return false;
}
// ,
if (accept.length > 0 && !checkFileType(file, accept)) {
createMessage.error!(t('component.upload.acceptUpload', [accept.join(',')]));
return false;
}
const commonItem = {
uuid: buildUUID(),
file,
@ -150,6 +155,7 @@ @@ -150,6 +155,7 @@
//
if (checkImgType(file)) {
// beforeUpload
// file.thumbUrl = await getBase64(file);
getBase64WithFile(file).then(({ result: thumbUrl }) => {
fileListRef.value = [
...unref(fileListRef),
@ -172,6 +178,14 @@ @@ -172,6 +178,14 @@
emit('delete', record);
}
//
// function handlePreview(record: FileItem) {
// const { thumbUrl = '' } = record;
// createImgPreview({
// imageList: [thumbUrl],
// });
// }
async function uploadApiByItem(item: FileItem) {
const { api } = props;
if (!api || !isFunction(api)) {
@ -181,13 +195,17 @@ @@ -181,13 +195,17 @@
item.status = UploadResultStatus.UPLOADING;
const { data } = await props.api?.(
{
...(props.uploadParams || {}),
data: {
...(props.uploadParams || {}),
},
file: item.file,
name: props.name,
filename: props.filename,
},
function onUploadProgress(progressEvent: ProgressEvent) {
const complete = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
item.percent = complete;
}
},
);
item.status = UploadResultStatus.SUCCESS;
item.responseData = data;
@ -215,16 +233,19 @@ @@ -215,16 +233,19 @@
isUploadingRef.value = true;
//
const uploadFileList =
fileListRef.value.filter((item) => item.status !== UploadResultStatus.SUCCESS) || [];
fileListRef.value.filter((item) => item.status !== UploadResultStatus.SUCCESS) || [];
const data = await Promise.all(
uploadFileList.map((item) => {
return uploadApiByItem(item);
})
uploadFileList.map((item) => {
return uploadApiByItem(item);
}),
);
isUploadingRef.value = false;
// :
const errorList = data.filter((item: any) => !item.success);
if (errorList.length > 0) throw errorList;
useTimeoutFn(() => {
emit('success');
}, 300);
} catch (e) {
isUploadingRef.value = false;
throw e;
@ -270,14 +291,15 @@ @@ -270,14 +291,15 @@
}
return {
columns: createTableColumns(),
actionColumn: createActionColumn(handleRemove),
columns: createTableColumns() as any[],
actionColumn: createActionColumn(handleRemove) as any,
register,
closeModal,
getHelpText,
getStringAccept,
getOkButtonProps,
beforeUpload,
// registerTable,
fileListRef,
state,
isUploadingRef,
@ -292,25 +314,25 @@ @@ -292,25 +314,25 @@
});
</script>
<style lang="less">
.upload-modal {
.ant-upload-list {
display: none;
}
.upload-modal {
.ant-upload-list {
display: none;
}
.ant-table-wrapper .ant-spin-nested-loading {
padding: 0;
}
.ant-table-wrapper .ant-spin-nested-loading {
padding: 0;
}
&-toolbar {
display: flex;
align-items: center;
margin-bottom: 8px;
&-toolbar {
display: flex;
align-items: center;
margin-bottom: 8px;
&__btn {
margin-left: 8px;
text-align: right;
flex: 1;
}
&__btn {
margin-left: 8px;
text-align: right;
flex: 1;
}
}
}
</style>

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

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
<BasicModal
width="800px"
:title="t('component.upload.preview')"
wrapClassName="upload-preview-modal"
class="upload-preview-modal"
v-bind="$attrs"
:showOkBtn="false"
@register="register"
@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
</template>
<script lang="ts">
import { defineComponent, watch, ref } from 'vue';
// import { BasicTable, useTable } from '/@/components/Table';
import FileList from './FileList.vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { previewProps } from './props';
@ -35,16 +36,16 @@ @@ -35,16 +36,16 @@
(value) => {
if (!isArray(value)) value = [];
fileListRef.value = value
.filter((item) => !!item)
.map((item) => {
return {
url: item,
type: item.split('.').pop() || '',
name: item.split('/').pop() || '',
};
});
.filter((item) => !!item)
.map((item) => {
return {
url: item,
type: item.split('.').pop() || '',
name: item.split('/').pop() || '',
};
});
},
{ immediate: true }
{ immediate: true },
);
//
@ -54,12 +55,20 @@ @@ -54,12 +55,20 @@
const removed = fileListRef.value.splice(index, 1);
emit('delete', removed[0].url);
emit(
'list-change',
fileListRef.value.map((item) => item.url)
'list-change',
fileListRef.value.map((item) => item.url),
);
}
}
// //
// function handlePreview(record: PreviewFileItem) {
// const { url = '' } = record;
// createImgPreview({
// imageList: [url],
// });
// }
//
function handleDownload(record: PreviewFileItem) {
const { url = '' } = record;
@ -71,20 +80,20 @@ @@ -71,20 +80,20 @@
register,
closeModal,
fileListRef,
columns: createPreviewColumns(),
actionColumn: createPreviewActionColumn({ handleRemove, handleDownload }),
columns: createPreviewColumns() as any[],
actionColumn: createPreviewActionColumn({ handleRemove, handleDownload }) as any,
};
},
});
</script>
<style lang="less">
.upload-preview-modal {
.ant-upload-list {
display: none;
}
.upload-preview-modal {
.ant-upload-list {
display: none;
}
.ant-table-wrapper .ant-spin-nested-loading {
padding: 0;
}
.ant-table-wrapper .ant-spin-nested-loading {
padding: 0;
}
}
</style>

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

@ -1,6 +1,9 @@ @@ -1,6 +1,9 @@
import type { BasicColumn, ActionItem } from '/@/components/Table';
import { FileItem, PreviewFileItem, UploadResultStatus } from './typing';
import { isImgTypeByName } from './helper';
import {
// checkImgType,
isImgTypeByName,
} from './helper';
import { Progress, Tag } from 'ant-design-vue';
import TableAction from '/@/components/Table/src/components/TableAction.vue';
import ThumbUrl from './ThumbUrl.vue';
@ -89,6 +92,12 @@ export function createActionColumn(handleRemove: Function): BasicColumn { @@ -89,6 +92,12 @@ export function createActionColumn(handleRemove: Function): BasicColumn {
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} />;
},
};
@ -114,9 +123,9 @@ export function createPreviewColumns(): BasicColumn[] { @@ -114,9 +123,9 @@ export function createPreviewColumns(): BasicColumn[] {
}
export function createPreviewActionColumn({
handleRemove,
handleDownload,
}: {
handleRemove,
handleDownload,
}: {
handleRemove: Fn;
handleDownload: Fn;
}): BasicColumn {

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

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

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

@ -34,6 +34,18 @@ export const basicProps = { @@ -34,6 +34,18 @@ export const basicProps = {
default: null,
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 = {

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

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { UploadResult } from '/@/api/platform/core/entity/upload';
import { UploadApiResult } from '/@/api/sys/model/uploadModel';
export enum UploadResultStatus {
SUCCESS = 'success',
@ -14,7 +14,7 @@ export interface FileItem { @@ -14,7 +14,7 @@ export interface FileItem {
percent: number;
file: File;
status?: UploadResultStatus;
responseData?: UploadResult;
responseData?: UploadApiResult;
uuid: string;
}
@ -26,28 +26,28 @@ export interface PreviewFileItem { @@ -26,28 +26,28 @@ export interface PreviewFileItem {
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
*/
customRender?: Function;
/**
*
* Title of this column
* @type any (string | slot)
*/
title: string;
/**
*
* Width of this column
* @type string | number
*/
width?: number;
/**
* a.b.c
* Display field of the data record, could be set like a.b.c
* @type string
*/
dataIndex: string;
/**
*
* specify how content is aligned
* @default 'left'
* @type string
*/

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

@ -2,11 +2,11 @@ import { Ref, unref, computed } from 'vue'; @@ -2,11 +2,11 @@ import { Ref, unref, computed } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
export function useUploadType({
acceptRef,
helpTextRef,
maxNumberRef,
maxSizeRef,
}: {
acceptRef,
helpTextRef,
maxNumberRef,
maxSizeRef,
}: {
acceptRef: Ref<string[]>;
helpTextRef: Ref<string>;
maxNumberRef: Ref<number>;
@ -22,7 +22,13 @@ export function useUploadType({ @@ -22,7 +22,13 @@ export function useUploadType({
});
const getStringAccept = computed(() => {
return unref(getAccept)
.map((item) => `.${item}`)
.map((item) => {
if (item.indexOf('/') > 0 || item.startsWith('.')) {
return item;
} else {
return `.${item}`;
}
})
.join(',');
});

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

@ -115,11 +115,17 @@ export class VAxios { @@ -115,11 +115,17 @@ export class VAxios {
/** 上传文件 */
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
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) {
Object.keys(params.data).forEach((key) => {
if (!params.data) return;
const value = params.data[key];
const value = params.data![key];
if (Array.isArray(value)) {
value.forEach((item) => {
formData.append(`${key}[]`, item);
@ -127,15 +133,9 @@ export class VAxios { @@ -127,15 +133,9 @@ export class VAxios {
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>({
...config,

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

@ -0,0 +1,54 @@ @@ -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 @@ @@ -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