mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2025-12-10 20:02:54 +00:00
优化
This commit is contained in:
parent
e66d64d83e
commit
f670008cb4
@ -1,428 +1,642 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-button :icon="icon" :disabled="disabled" :type="type" @click="open">
|
<div class="cl-upload__wrap" :class="[customClass]">
|
||||||
{{ $t('导入') }}
|
<div
|
||||||
</el-button>
|
class="cl-upload"
|
||||||
|
:class="[
|
||||||
<cl-form ref="Form">
|
`cl-upload--${type}`,
|
||||||
<template #slot-upload>
|
{
|
||||||
<div v-if="!upload.filename" class="upload">
|
'is-disabled': disabled,
|
||||||
<div class="tips" v-if="template">
|
'is-multiple': multiple,
|
||||||
<span>{{ tips }}</span>
|
'is-small': small
|
||||||
<el-button type="primary" text bg @click="download">{{
|
}
|
||||||
$t('下载模版')
|
]"
|
||||||
}}</el-button>
|
>
|
||||||
</div>
|
<template v-if="!drag">
|
||||||
|
<div v-if="type == 'file'" class="cl-upload__file-btn">
|
||||||
<div class="inner">
|
<el-upload
|
||||||
<cl-upload
|
|
||||||
:ref="setRefs('upload')"
|
:ref="setRefs('upload')"
|
||||||
drag
|
:drag="drag"
|
||||||
:limit-size="limitSize"
|
action=""
|
||||||
:accept="accept"
|
:accept="accept"
|
||||||
|
:show-file-list="false"
|
||||||
|
:before-upload="onBeforeUpload"
|
||||||
|
:http-request="httpRequest"
|
||||||
|
:headers="headers"
|
||||||
|
:multiple="multiple"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:auto-upload="false"
|
|
||||||
:before-upload="onUpload"
|
|
||||||
:size="[220, '100%']"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #slot-list>
|
|
||||||
<div v-if="list.length" class="data-table">
|
|
||||||
<div class="head">
|
|
||||||
<el-button type="success" @click="clear">{{ $t('重新上传') }}</el-button>
|
|
||||||
<el-button
|
|
||||||
type="danger"
|
|
||||||
:disabled="table.selection.length == 0"
|
|
||||||
@click="table.del()"
|
|
||||||
>
|
>
|
||||||
{{ $t('批量删除') }}
|
<slot>
|
||||||
</el-button>
|
<el-button type="success">{{ text }}</el-button>
|
||||||
|
</slot>
|
||||||
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div class="cl-table">
|
<!-- 列表 -->
|
||||||
<el-table
|
<vue-draggable
|
||||||
border
|
v-if="showList"
|
||||||
:data="list"
|
v-model="list"
|
||||||
max-height="600px"
|
class="cl-upload__list"
|
||||||
@selection-change="table.onSelectionChange"
|
tag="div"
|
||||||
@row-click="
|
ghost-class="Ghost"
|
||||||
row => {
|
drag-class="Drag"
|
||||||
row._edit = true;
|
item-key="uid"
|
||||||
|
:disabled="!draggable"
|
||||||
|
@end="update"
|
||||||
|
>
|
||||||
|
<!-- 触发器 -->
|
||||||
|
<template #footer>
|
||||||
|
<div v-if="(type == 'image' || drag) && isAdd" class="cl-upload__footer">
|
||||||
|
<el-upload
|
||||||
|
:ref="setRefs('upload')"
|
||||||
|
action=""
|
||||||
|
:drag="drag"
|
||||||
|
:accept="accept"
|
||||||
|
:show-file-list="false"
|
||||||
|
:before-upload="onBeforeUpload"
|
||||||
|
:http-request="httpRequest"
|
||||||
|
:headers="headers"
|
||||||
|
:multiple="multiple"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
<slot>
|
||||||
|
<!-- 拖拽方式 -->
|
||||||
|
<div v-if="drag" class="cl-upload__demo is-dragger">
|
||||||
|
<el-icon :size="46">
|
||||||
|
<upload-filled />
|
||||||
|
</el-icon>
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
t('点击上传或将文件拖动到此处,文件大小限制{n}M', {
|
||||||
|
n: limitSize
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 点击方式 -->
|
||||||
|
<div v-else class="cl-upload__demo">
|
||||||
|
<el-icon :size="36">
|
||||||
|
<component :is="icon" v-if="icon" />
|
||||||
|
<picture-filled v-else />
|
||||||
|
</el-icon>
|
||||||
|
<span v-if="text" class="text">{{ text }}</span>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<template #item="{ element: item, index }">
|
||||||
|
<el-upload
|
||||||
|
action=""
|
||||||
|
:accept="accept"
|
||||||
|
:show-file-list="false"
|
||||||
|
:http-request="
|
||||||
|
req => {
|
||||||
|
return httpRequest(req, item);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
:before-upload="
|
||||||
|
file => {
|
||||||
|
onBeforeUpload(file, item);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:headers="headers"
|
||||||
|
:disabled="disabled"
|
||||||
>
|
>
|
||||||
<el-table-column
|
<slot name="item" :item="item" :index="index">
|
||||||
type="selection"
|
<div class="cl-upload__item">
|
||||||
width="60px"
|
<upload-item
|
||||||
align="center"
|
:show-tag="showTag"
|
||||||
fixed="left"
|
:item="item"
|
||||||
/>
|
:list="list"
|
||||||
|
:disabled="disabled"
|
||||||
|
:deletable="deletable"
|
||||||
|
@remove="remove(index)"
|
||||||
|
/>
|
||||||
|
|
||||||
<el-table-column
|
<!-- 小图模式 -->
|
||||||
:label="$t('序号')"
|
<el-icon
|
||||||
type="index"
|
v-if="small"
|
||||||
width="80px"
|
class="cl-upload__item-remove"
|
||||||
align="center"
|
@click.stop="remove(index)"
|
||||||
fixed="left"
|
|
||||||
:index="table.onIndex"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<el-table-column
|
|
||||||
v-for="item in table.header"
|
|
||||||
:key="item"
|
|
||||||
:prop="item"
|
|
||||||
:label="item"
|
|
||||||
min-width="160px"
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<span v-if="!scope.row._edit">{{ scope.row[item] }}</span>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<el-input
|
|
||||||
v-model="scope.row[item]"
|
|
||||||
type="textarea"
|
|
||||||
clearable
|
|
||||||
:placeholder="item"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column
|
|
||||||
:label="$t('操作')"
|
|
||||||
width="100px"
|
|
||||||
align="center"
|
|
||||||
fixed="right"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
text
|
|
||||||
bg
|
|
||||||
type="danger"
|
|
||||||
@click.stop="table.del(scope.$index)"
|
|
||||||
>
|
>
|
||||||
{{ $t('删除') }}
|
<circle-close-filled />
|
||||||
</el-button>
|
</el-icon>
|
||||||
</template>
|
</div>
|
||||||
</el-table-column>
|
</slot>
|
||||||
</el-table>
|
</el-upload>
|
||||||
</div>
|
</template>
|
||||||
|
</vue-draggable>
|
||||||
<div class="pagination">
|
</div>
|
||||||
<el-pagination
|
</div>
|
||||||
v-model:current-page="pagination.page"
|
|
||||||
background
|
|
||||||
layout="total, prev, pager, next"
|
|
||||||
:total="upload.list.length"
|
|
||||||
:page-size="pagination.size"
|
|
||||||
@current-change="pagination.onCurrentChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</cl-form>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'cl-import-btn'
|
name: 'cl-upload'
|
||||||
});
|
});
|
||||||
|
|
||||||
import { useForm } from '@cool-vue/crud';
|
import { computed, ref, watch, type PropType, nextTick } from 'vue';
|
||||||
|
import { assign, isArray, isEmpty, isNumber } from 'lodash-es';
|
||||||
|
import VueDraggable from 'vuedraggable';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { reactive, type PropType, computed } from 'vue';
|
import { PictureFilled, UploadFilled, CircleCloseFilled } from '@element-plus/icons-vue';
|
||||||
import * as XLSX from 'xlsx';
|
import { useForm } from '@cool-vue/crud';
|
||||||
import chardet from 'chardet';
|
|
||||||
import { extname } from '/@/cool/utils';
|
|
||||||
import { has } from 'lodash-es';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { useCool } from '/@/cool';
|
import { useCool } from '/@/cool';
|
||||||
|
import { useBase } from '/$/base';
|
||||||
|
import { uuid, isPromise } from '/@/cool/utils';
|
||||||
|
import { getUrls, getType } from '../utils';
|
||||||
|
import { useUpload } from '../hooks';
|
||||||
|
import UploadItem from './upload-item/index.vue';
|
||||||
|
import { CrudProps } from '/#/crud';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
onConfig: Function,
|
...CrudProps,
|
||||||
onSubmit: Function,
|
// 绑定值,单选时字符串,多选时字符串数组
|
||||||
template: {
|
modelValue: {
|
||||||
type: String,
|
type: [String, Array],
|
||||||
default: ''
|
default: () => []
|
||||||
},
|
|
||||||
tips: String,
|
|
||||||
limitSize: {
|
|
||||||
type: Number,
|
|
||||||
default: 10
|
|
||||||
},
|
},
|
||||||
|
// 上传类型
|
||||||
type: {
|
type: {
|
||||||
type: String as PropType<
|
type: String as PropType<'image' | 'file'>,
|
||||||
'default' | 'success' | 'warning' | 'info' | 'text' | 'primary' | 'danger'
|
default: 'image'
|
||||||
>,
|
|
||||||
default: 'success'
|
|
||||||
},
|
},
|
||||||
icon: String,
|
// 允许上传的文件类型
|
||||||
|
accept: String,
|
||||||
|
// 是否多选
|
||||||
|
multiple: Boolean,
|
||||||
|
// 限制数量
|
||||||
|
limit: Number,
|
||||||
|
// 限制大小
|
||||||
|
limitSize: Number,
|
||||||
|
// 是否自动上传
|
||||||
|
autoUpload: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 元素大小
|
||||||
|
size: [String, Number, Array],
|
||||||
|
// 小图模式
|
||||||
|
small: Boolean,
|
||||||
|
// 显示图标
|
||||||
|
icon: null,
|
||||||
|
// 显示文案
|
||||||
|
text: String,
|
||||||
|
// 显示角标
|
||||||
|
showTag: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 是否显示上传列表
|
||||||
|
showFileList: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 列表是否可拖拽
|
||||||
|
draggable: Boolean,
|
||||||
|
// 是否拖拽到特定区域以进行上传
|
||||||
|
drag: Boolean,
|
||||||
|
// 是否禁用
|
||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
accept: {
|
// 是否可删除
|
||||||
type: String,
|
deletable: Boolean,
|
||||||
default:
|
// 自定义样式名
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel,text/csv'
|
customClass: String,
|
||||||
}
|
// 上传前钩子
|
||||||
|
beforeUpload: Function,
|
||||||
|
// 云端上传路径前缀
|
||||||
|
prefixPath: String
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits(['update:modelValue', 'change', 'upload', 'success', 'error', 'progress']);
|
||||||
|
|
||||||
const Form = useForm();
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { refs, setRefs } = useCool();
|
const { refs, setRefs } = useCool();
|
||||||
|
const { user } = useBase();
|
||||||
|
const Form = useForm();
|
||||||
|
const { options, toUpload } = useUpload();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
// 提示信息
|
// 元素尺寸
|
||||||
const tips = computed(() => {
|
const size = computed(() => {
|
||||||
return props.tips || t('请按照模版填写信息');
|
const d = props.size || options.size;
|
||||||
|
return (isArray(d) ? d : [d, d]).map((e: string | number) => (isNumber(e) ? e + 'px' : e));
|
||||||
});
|
});
|
||||||
|
|
||||||
// 上传信息
|
// 是否禁用
|
||||||
const upload = reactive({
|
const disabled = computed(() => {
|
||||||
filename: '',
|
return props.isDisabled || props.disabled;
|
||||||
file: null as File | null,
|
|
||||||
list: [] as any[]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 分页信息
|
// 最大上传数量
|
||||||
const pagination = reactive({
|
const limit = props.limit || options.limit.upload;
|
||||||
size: 20,
|
|
||||||
page: 1,
|
|
||||||
onCurrentChange(page: number) {
|
|
||||||
pagination.page = page;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 数据表格
|
// 图片大小限制
|
||||||
const table = reactive({
|
const limitSize = props.limitSize || options.limit.size;
|
||||||
// 表头
|
|
||||||
header: [] as string[],
|
|
||||||
|
|
||||||
// 选中列表
|
// 文案
|
||||||
selection: [] as any[],
|
const text = computed(() => {
|
||||||
|
if (props.text !== undefined) {
|
||||||
|
return props.text;
|
||||||
|
} else {
|
||||||
|
switch (props.type) {
|
||||||
|
case 'file':
|
||||||
|
return t('选择文件');
|
||||||
|
|
||||||
// 删除行
|
case 'image':
|
||||||
del(index?: number) {
|
return t('选择图片');
|
||||||
if (index !== undefined) {
|
|
||||||
upload.list.splice(index, 1);
|
default:
|
||||||
} else {
|
return '';
|
||||||
upload.list = upload.list.filter(a => {
|
|
||||||
return !table.selection.includes(a._index);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
// 序号
|
|
||||||
onIndex(index: number) {
|
|
||||||
return index + 1 + (pagination.page - 1) * pagination.size;
|
|
||||||
},
|
|
||||||
|
|
||||||
// 选中
|
|
||||||
onSelectionChange(arr: any[]) {
|
|
||||||
table.selection = arr.map(e => e._index);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 数据列表
|
// 请求头
|
||||||
const list = computed(() => {
|
const headers = computed(() => {
|
||||||
return upload.list.filter((_, i) => {
|
return {
|
||||||
return (
|
Authorization: user.token
|
||||||
i >= (pagination.page - 1) * pagination.size && i < pagination.page * pagination.size
|
};
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 列表
|
||||||
|
const list = ref<Upload.Item[]>([]);
|
||||||
|
|
||||||
|
// 显示上传列表
|
||||||
|
const showList = computed(() => {
|
||||||
|
if (props.type == 'file') {
|
||||||
|
return props.showFileList ? !isEmpty(list.value) : false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 文件格式
|
||||||
|
const accept = computed(() => {
|
||||||
|
return props.accept || (props.type == 'file' ? '' : 'image/*');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 能否添加
|
||||||
|
const isAdd = computed(() => {
|
||||||
|
const len = list.value.length;
|
||||||
|
|
||||||
|
if (props.multiple && !disabled.value) {
|
||||||
|
return limit - len > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len == 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 上传前
|
||||||
|
async function onBeforeUpload(file: any, item?: Upload.Item) {
|
||||||
|
function next() {
|
||||||
|
const d = {
|
||||||
|
uid: file.uid,
|
||||||
|
size: file.size,
|
||||||
|
name: file.name,
|
||||||
|
type: getType(file.name),
|
||||||
|
progress: props.autoUpload ? 0 : 100, // 非自动上传时默认100%
|
||||||
|
url: '',
|
||||||
|
preload: '',
|
||||||
|
error: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图片预览地址
|
||||||
|
if (d.type == 'image') {
|
||||||
|
if (file instanceof File) {
|
||||||
|
d.preload = window.webkitURL.createObjectURL(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传事件
|
||||||
|
emit('upload', d, file);
|
||||||
|
|
||||||
|
// 赋值
|
||||||
|
if (item) {
|
||||||
|
assign(item, d);
|
||||||
|
} else {
|
||||||
|
if (props.multiple) {
|
||||||
|
if (!isAdd.value) {
|
||||||
|
ElMessage.warning(t('最多只能上传{n}个文件', { n: limit }));
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
list.value.push(d);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.value = [d];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件大小限制
|
||||||
|
if (file.size / 1024 / 1024 >= limitSize) {
|
||||||
|
ElMessage.error(t('上传文件大小不能超过 {n}MB!', { n: limitSize }));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义上传事件
|
||||||
|
if (props.beforeUpload) {
|
||||||
|
let r = props.beforeUpload(file, item, { next });
|
||||||
|
|
||||||
|
if (isPromise(r)) {
|
||||||
|
r.then(next).catch(() => null);
|
||||||
|
} else {
|
||||||
|
if (r) {
|
||||||
|
r = next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
} else {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除
|
||||||
|
function remove(index: number) {
|
||||||
|
list.value.splice(index, 1);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
// 清空
|
// 清空
|
||||||
function clear() {
|
function clear() {
|
||||||
upload.filename = '';
|
list.value = [];
|
||||||
upload.file = null;
|
|
||||||
upload.list = [];
|
|
||||||
table.header = [];
|
|
||||||
table.selection = [];
|
|
||||||
refs.upload?.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开
|
// 文件上传请求
|
||||||
function open() {
|
async function httpRequest(req: any, item?: Upload.Item) {
|
||||||
|
if (!item) {
|
||||||
|
item = list.value.find(e => e.uid == req.file.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传请求
|
||||||
|
toUpload(req.file, {
|
||||||
|
prefixPath: props.prefixPath,
|
||||||
|
onProgress(progress) {
|
||||||
|
item!.progress = progress;
|
||||||
|
emit('progress', item);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
assign(item!, res);
|
||||||
|
emit('success', item);
|
||||||
|
update();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
item!.error = err.message;
|
||||||
|
emit('error', item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测是否还有未上传的文件
|
||||||
|
function check() {
|
||||||
|
return list.value.find(e => !e.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
function update() {
|
||||||
|
if (!check()) {
|
||||||
|
const urls = getUrls(list.value);
|
||||||
|
|
||||||
|
const val = props.multiple ? getUrls(list.value) : urls[0] || '';
|
||||||
|
|
||||||
|
// 更新绑定值
|
||||||
|
emit('update:modelValue', val);
|
||||||
|
emit('change', val);
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
if (props.prop) {
|
||||||
|
Form.value?.validateField(props.prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空
|
||||||
|
refs.upload?.clearFiles();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动上传
|
||||||
|
function upload(file: File) {
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
Form.value?.open({
|
refs.upload?.clearFiles();
|
||||||
title: t('导入'),
|
|
||||||
width: computed(() => (upload.filename ? '80%' : '800px')),
|
|
||||||
dialog: {
|
|
||||||
'close-on-press-escape': false
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
...(props.onConfig ? props.onConfig(Form) : []),
|
|
||||||
{
|
|
||||||
prop: 'file',
|
|
||||||
component: {
|
|
||||||
name: 'slot-upload'
|
|
||||||
},
|
|
||||||
hidden() {
|
|
||||||
return upload.filename;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: {
|
|
||||||
name: 'slot-list'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
op: {
|
|
||||||
saveButtonText: t('提交')
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
submit(_, { done, close }) {
|
|
||||||
if (!upload.filename) {
|
|
||||||
done();
|
|
||||||
return ElMessage.error(t('请选择文件'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.onSubmit) {
|
nextTick(() => {
|
||||||
props.onSubmit(
|
refs.upload?.handleStart(file);
|
||||||
{
|
refs.upload?.submit();
|
||||||
...upload,
|
|
||||||
..._
|
|
||||||
},
|
|
||||||
{ done, close }
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ElMessage.error(t('[cl-import-btn] onSubmit is required'));
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传
|
// 监听绑定值
|
||||||
function onUpload(raw: File, _: any, { next }: any) {
|
watch(
|
||||||
const reader = new FileReader();
|
() => props.modelValue,
|
||||||
const ext = extname(raw.name);
|
(val: any[] | string) => {
|
||||||
|
if (check()) {
|
||||||
reader.onload = (event: any) => {
|
return false;
|
||||||
try {
|
|
||||||
let data = '';
|
|
||||||
|
|
||||||
if (ext == 'csv') {
|
|
||||||
const detected: any = chardet.detect(new Uint8Array(event.target.result));
|
|
||||||
const decoder = new TextDecoder(detected);
|
|
||||||
data = decoder.decode(event.target.result);
|
|
||||||
} else {
|
|
||||||
data = event.target.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const workbook = XLSX.read(data, { type: 'binary', raw: ext == 'csv' });
|
|
||||||
|
|
||||||
let json: any[] = [];
|
|
||||||
for (const sheet in workbook.Sheets) {
|
|
||||||
if (has(workbook.Sheets, sheet)) {
|
|
||||||
json = json.concat(
|
|
||||||
XLSX.utils.sheet_to_json(workbook.Sheets[sheet], {
|
|
||||||
raw: false,
|
|
||||||
dateNF: 'yyyy-mm-dd',
|
|
||||||
defval: ''
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
upload.list = json.map((e, i) => {
|
|
||||||
return {
|
|
||||||
...e,
|
|
||||||
_index: i
|
|
||||||
};
|
|
||||||
});
|
|
||||||
upload.filename = raw.name;
|
|
||||||
upload.file = raw;
|
|
||||||
|
|
||||||
const sheet = workbook.Sheets[Object.keys(workbook.Sheets)[0]];
|
|
||||||
|
|
||||||
for (const i in sheet) {
|
|
||||||
if (i[0] === '!') continue;
|
|
||||||
|
|
||||||
const row = i.match(/[0-9]+/)?.[0];
|
|
||||||
|
|
||||||
if (row == '1') {
|
|
||||||
table.header.push(sheet[i].v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('change', json);
|
|
||||||
} catch (err) {
|
|
||||||
ElMessage.error(t('文件异常,请检查内容是否正确'));
|
|
||||||
clear();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if (ext == 'csv') {
|
const urls = (isArray(val) ? val : [val]).filter(Boolean);
|
||||||
reader.readAsArrayBuffer(raw);
|
|
||||||
} else {
|
list.value = urls
|
||||||
reader.readAsBinaryString(raw);
|
.map((url, index) => {
|
||||||
|
const old = list.value[index] || {};
|
||||||
|
|
||||||
|
return assign(
|
||||||
|
{
|
||||||
|
progress: 100,
|
||||||
|
uid: uuid()
|
||||||
|
},
|
||||||
|
old,
|
||||||
|
{
|
||||||
|
type: getType(url),
|
||||||
|
url,
|
||||||
|
preload: old.url == url ? old.preload : url // 防止重复预览
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((_, i) => {
|
||||||
|
return props.multiple ? true : i == 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
next();
|
// 导出
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载模版
|
|
||||||
function download() {
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.setAttribute('href', props.template);
|
|
||||||
link.setAttribute('download', '');
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
open,
|
isAdd,
|
||||||
|
list,
|
||||||
|
check,
|
||||||
clear,
|
clear,
|
||||||
Form
|
remove,
|
||||||
|
upload
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.upload {
|
.cl-upload {
|
||||||
display: flex;
|
line-height: normal;
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.inner {
|
.Ghost {
|
||||||
|
.cl-upload__item {
|
||||||
|
border: 1px dashed var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__file {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
:deep(.cl-upload) {
|
&-btn {
|
||||||
.cl-upload__footer,
|
width: fit-content;
|
||||||
.cl-upload__list,
|
}
|
||||||
.el-upload,
|
}
|
||||||
.is-drag {
|
|
||||||
width: 100% !important;
|
&__list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item,
|
||||||
|
&__demo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: v-bind('size[0]');
|
||||||
|
width: v-bind('size[1]');
|
||||||
|
background-color: var(--el-fill-color-light);
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__demo {
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-dragger {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__file-btn {
|
||||||
|
& + .cl-upload__list {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-upload) {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.el-upload-dragger {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent !important;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.is-dragover {
|
||||||
|
&::after {
|
||||||
|
display: block;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px dashed var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
.cl-upload__demo {
|
||||||
|
color: var(--el-text-color-placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.cl-upload__item) {
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: var(--el-disabled-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-multiple {
|
||||||
|
.cl-upload__list {
|
||||||
|
margin-bottom: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-upload__item {
|
||||||
|
margin: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-upload__footer {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-small {
|
||||||
|
.cl-upload__demo {
|
||||||
|
.el-icon {
|
||||||
|
font-size: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-upload__item-remove {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.cl-upload-item) {
|
||||||
|
.cl-upload-item__progress-bar,
|
||||||
|
.cl-upload-item__actions,
|
||||||
|
.cl-upload-item__tag {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-upload-item__progress-value {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.is-disabled) {
|
||||||
|
.cl-upload__demo {
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tips {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
& > span {
|
|
||||||
color: var(--el-color-warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-table {
|
|
||||||
.head {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user