1.新增批量导入 2.新增财务报表
This commit is contained in:
@@ -3,68 +3,49 @@
|
||||
<!-- 表格工具栏 -->
|
||||
<div class="flex-x-between mb-[10px]">
|
||||
<!-- 左侧工具栏 -->
|
||||
<div>
|
||||
<div style="display: flex;">
|
||||
<template v-for="item in toolbar" :key="item">
|
||||
<template v-if="typeof item === 'string'">
|
||||
<!-- 新增 -->
|
||||
<template v-if="item === 'add'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="primary"
|
||||
icon="plus"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="primary" icon="plus"
|
||||
@click="handleToolbar(item)">
|
||||
新增
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 删除 -->
|
||||
<template v-else-if="item === 'delete'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="danger"
|
||||
icon="delete"
|
||||
:disabled="removeIds.length === 0"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="danger" icon="delete"
|
||||
:disabled="removeIds.length === 0" @click="handleToolbar(item)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 导入 -->
|
||||
<template v-else-if="item === 'import'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="default"
|
||||
icon="upload"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="default" icon="upload"
|
||||
@click="handleToolbar(item)">
|
||||
导入
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 导出 -->
|
||||
<template v-else-if="item === 'export'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="default"
|
||||
icon="download"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="default" icon="download"
|
||||
@click="handleToolbar(item)">
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 其他 -->
|
||||
<template v-else-if="typeof item === 'object'">
|
||||
<el-button
|
||||
v-if="item.hidden === undefined || item.hidden === false"
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
||||
:icon="item.icon"
|
||||
:type="item.type ?? 'default'"
|
||||
@click="handleToolbar(item.name)"
|
||||
>
|
||||
<el-button v-if="item.hidden === undefined || item.hidden === false"
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]" :icon="item.icon" :type="item.type ?? 'default'"
|
||||
@click="handleToolbar(item.name)">
|
||||
{{ item.text }}
|
||||
</el-button>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 插槽 -->
|
||||
<slot name="custom" />
|
||||
</div>
|
||||
<!-- 右侧工具栏 -->
|
||||
<div>
|
||||
@@ -89,68 +70,36 @@
|
||||
</template>
|
||||
<!-- 导出 -->
|
||||
<template v-else-if="item === 'exports'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:export`]"
|
||||
icon="download"
|
||||
circle
|
||||
title="导出"
|
||||
@click="handleToolbar(item)"
|
||||
/>
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:export`]" icon="download" circle title="导出"
|
||||
@click="handleToolbar(item)" />
|
||||
</template>
|
||||
<!-- 导入 -->
|
||||
<template v-else-if="item === 'imports'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:import`]"
|
||||
icon="upload"
|
||||
circle
|
||||
title="导入"
|
||||
@click="handleToolbar(item)"
|
||||
/>
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:import`]" icon="upload" circle title="导入"
|
||||
@click="handleToolbar(item)" />
|
||||
</template>
|
||||
<!-- 搜索 -->
|
||||
<template v-else-if="item === 'search'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:query`]"
|
||||
icon="search"
|
||||
circle
|
||||
title="搜索"
|
||||
@click="handleToolbar(item)"
|
||||
/>
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:query`]" icon="search" circle title="搜索"
|
||||
@click="handleToolbar(item)" />
|
||||
</template>
|
||||
</template>
|
||||
<!-- 其他 -->
|
||||
<template v-else-if="typeof item === 'object'">
|
||||
<template v-if="item.auth">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
||||
:icon="item.icon"
|
||||
circle
|
||||
:title="item.title"
|
||||
@click="handleToolbar(item.name)"
|
||||
/>
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]" :icon="item.icon" circle
|
||||
:title="item.title" @click="handleToolbar(item.name)" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button
|
||||
:icon="item.icon"
|
||||
circle
|
||||
:title="item.title"
|
||||
@click="handleToolbar(item.name)"
|
||||
/>
|
||||
<el-button :icon="item.icon" circle :title="item.title" @click="handleToolbar(item.name)" />
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
v-bind="contentConfig.table"
|
||||
:data="pageData"
|
||||
:row-key="pk"
|
||||
@selection-change="handleSelectionChange"
|
||||
@filter-change="handleFilterChange"
|
||||
>
|
||||
<el-table ref="tableRef" v-loading="loading" v-bind="contentConfig.table" :data="pageData" :row-key="pk"
|
||||
@selection-change="handleSelectionChange" @filter-change="handleFilterChange">
|
||||
<template v-for="col in cols" :key="col">
|
||||
<el-table-column v-if="col.show" v-bind="col">
|
||||
<template #default="scope">
|
||||
@@ -159,24 +108,15 @@
|
||||
<template v-if="col.prop">
|
||||
<template v-if="Array.isArray(scope.row[col.prop])">
|
||||
<template v-for="(item, index) in scope.row[col.prop]" :key="item">
|
||||
<el-image
|
||||
:src="item"
|
||||
:preview-src-list="scope.row[col.prop]"
|
||||
:initial-index="index"
|
||||
:preview-teleported="true"
|
||||
:style="`width: ${col.imageWidth ?? 40}px; height: ${
|
||||
col.imageHeight ?? 40
|
||||
}px`"
|
||||
/>
|
||||
<el-image :src="item" :preview-src-list="scope.row[col.prop]" :initial-index="index"
|
||||
:preview-teleported="true" :style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40
|
||||
}px`" />
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-image
|
||||
:src="scope.row[col.prop]"
|
||||
:preview-src-list="[scope.row[col.prop]]"
|
||||
<el-image :src="scope.row[col.prop]" :preview-src-list="[scope.row[col.prop]]"
|
||||
:preview-teleported="true"
|
||||
:style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40}px`"
|
||||
/>
|
||||
:style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40}px`" />
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
@@ -198,30 +138,20 @@
|
||||
<template v-else-if="col.templet === 'switch'">
|
||||
<template v-if="col.prop">
|
||||
<!-- pageData.length>0: 解决el-switch组件会在表格初始化的时候触发一次change事件 -->
|
||||
<el-switch
|
||||
v-model="scope.row[col.prop]"
|
||||
:active-value="col.activeValue ?? 1"
|
||||
:inactive-value="col.inactiveValue ?? 0"
|
||||
:inline-prompt="true"
|
||||
:active-text="col.activeText ?? ''"
|
||||
:inactive-text="col.inactiveText ?? ''"
|
||||
:validate-event="false"
|
||||
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
|
||||
@change="
|
||||
<el-switch v-model="scope.row[col.prop]" :active-value="col.activeValue ?? 1"
|
||||
:inactive-value="col.inactiveValue ?? 0" :inline-prompt="true" :active-text="col.activeText ?? ''"
|
||||
:inactive-text="col.inactiveText ?? ''" :validate-event="false"
|
||||
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)" @change="
|
||||
pageData.length > 0 && handleModify(col.prop, scope.row[col.prop], scope.row)
|
||||
"
|
||||
/>
|
||||
" />
|
||||
</template>
|
||||
</template>
|
||||
<!-- 生成输入框组件 -->
|
||||
<template v-else-if="col.templet === 'input'">
|
||||
<template v-if="col.prop">
|
||||
<el-input
|
||||
v-model="scope.row[col.prop]"
|
||||
:type="col.inputType ?? 'text'"
|
||||
<el-input v-model="scope.row[col.prop]" :type="col.inputType ?? 'text'"
|
||||
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
|
||||
@blur="handleModify(col.prop, scope.row[col.prop], scope.row)"
|
||||
/>
|
||||
@blur="handleModify(col.prop, scope.row[col.prop], scope.row)" />
|
||||
</template>
|
||||
</template>
|
||||
<!-- 格式化为价格 -->
|
||||
@@ -253,7 +183,7 @@
|
||||
{{
|
||||
scope.row[col.prop]
|
||||
? useDateFormat(scope.row[col.prop], col.dateFormat ?? "YYYY-MM-DD HH:mm:ss")
|
||||
.value
|
||||
.value
|
||||
: ""
|
||||
}}
|
||||
</template>
|
||||
@@ -264,21 +194,15 @@
|
||||
<template v-if="typeof item === 'string'">
|
||||
<!-- 编辑/删除 -->
|
||||
<template v-if="item === 'edit' || item === 'delete'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
:type="item === 'edit' ? 'primary' : 'danger'"
|
||||
:icon="item"
|
||||
size="small"
|
||||
link
|
||||
@click="
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
:type="item === 'edit' ? 'primary' : 'danger'" :icon="item" size="small" link @click="
|
||||
handleOperat({
|
||||
name: item,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
})
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ item === "edit" ? "编辑" : "删除" }}
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -286,63 +210,43 @@
|
||||
<!-- 其他 -->
|
||||
<template v-else-if="typeof item === 'object'">
|
||||
<template v-if="item.hidden === undefined || item.hidden === false">
|
||||
<el-button
|
||||
v-if="item.isBtn"
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
||||
:icon="item.icon"
|
||||
:type="item.type ?? 'primary'"
|
||||
size="small"
|
||||
link
|
||||
@click="
|
||||
<el-button v-if="item.isBtn" v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
||||
:icon="item.icon" :type="item.type ?? 'primary'" size="small" link @click="
|
||||
handleOperat({
|
||||
name: item.name,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
})
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ item.text }}
|
||||
</el-button>
|
||||
|
||||
<el-dropdown style="margin-top: 4px" v-else>
|
||||
<el-button
|
||||
v-if="item.render === undefined || item.render(scope.row)"
|
||||
v-bind="
|
||||
item.auth
|
||||
? { 'v-hasPerm': [`${contentConfig.pageName}:${item.auth}`] }
|
||||
: {}
|
||||
"
|
||||
:icon="item.icon"
|
||||
:type="item.type ?? 'primary'"
|
||||
size="small"
|
||||
link
|
||||
@click="
|
||||
<el-button v-if="item.render === undefined || item.render(scope.row)" v-bind="item.auth
|
||||
? { 'v-hasPerm': [`${contentConfig.pageName}:${item.auth}`] }
|
||||
: {}
|
||||
" :icon="item.icon" :type="item.type ?? 'primary'" size="small" link @click="
|
||||
handleOperat({
|
||||
name: item.name,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
})
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ item.text }}
|
||||
</el-button>
|
||||
<template #dropdown v-if="item.options && item.options.length > 0">
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@click="
|
||||
handleOperat({
|
||||
name: item.name,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
command: opt.command ? opt.command : '',
|
||||
})
|
||||
"
|
||||
v-for="opt in item.options"
|
||||
:key="opt.value"
|
||||
>
|
||||
<el-dropdown-item @click="
|
||||
handleOperat({
|
||||
name: item.name,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
command: opt.command ? opt.command : '',
|
||||
})
|
||||
" v-for="opt in item.options" :key="opt.value">
|
||||
{{ opt.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -364,33 +268,18 @@
|
||||
<template v-if="showPagination">
|
||||
<el-scrollbar>
|
||||
<div class="mt-[12px]">
|
||||
<el-pagination
|
||||
v-bind="pagination"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
<el-pagination v-bind="pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<!-- 导出弹窗 -->
|
||||
<el-dialog
|
||||
v-model="exportsModalVisible"
|
||||
:align-center="true"
|
||||
title="导出数据"
|
||||
width="600px"
|
||||
style="padding-right: 0"
|
||||
@close="handleCloseExportsModal"
|
||||
>
|
||||
<el-dialog v-model="exportsModalVisible" :align-center="true" title="导出数据" width="600px" style="padding-right: 0"
|
||||
@close="handleCloseExportsModal">
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form
|
||||
ref="exportsFormRef"
|
||||
label-width="auto"
|
||||
style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="exportsFormData"
|
||||
:rules="exportsFormRules"
|
||||
>
|
||||
<el-form ref="exportsFormRef" label-width="auto" style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="exportsFormData" :rules="exportsFormRules">
|
||||
<el-form-item label="文件名" prop="filename">
|
||||
<el-input v-model="exportsFormData.filename" clearable />
|
||||
</el-form-item>
|
||||
@@ -400,16 +289,10 @@
|
||||
<el-form-item label="数据源" prop="origin">
|
||||
<el-select v-model="exportsFormData.origin">
|
||||
<el-option label="当前数据 (当前页的数据)" :value="ExportsOriginEnum.CURRENT" />
|
||||
<el-option
|
||||
label="选中数据 (所有选中的数据)"
|
||||
:value="ExportsOriginEnum.SELECTED"
|
||||
:disabled="selectionData.length <= 0"
|
||||
/>
|
||||
<el-option
|
||||
label="全量数据 (所有分页的数据)"
|
||||
:value="ExportsOriginEnum.REMOTE"
|
||||
:disabled="contentConfig.exportsAction === undefined"
|
||||
/>
|
||||
<el-option label="选中数据 (所有选中的数据)" :value="ExportsOriginEnum.SELECTED"
|
||||
:disabled="selectionData.length <= 0" />
|
||||
<el-option label="全量数据 (所有分页的数据)" :value="ExportsOriginEnum.REMOTE"
|
||||
:disabled="contentConfig.exportsAction === undefined" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段" prop="fields">
|
||||
@@ -430,35 +313,17 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 导入弹窗 -->
|
||||
<el-dialog
|
||||
v-model="importModalVisible"
|
||||
:align-center="true"
|
||||
title="导入数据"
|
||||
width="600px"
|
||||
style="padding-right: 0"
|
||||
@close="handleCloseImportModal"
|
||||
>
|
||||
<el-dialog v-model="importModalVisible" :align-center="true" title="导入数据" width="600px" style="padding-right: 0"
|
||||
@close="handleCloseImportModal">
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form
|
||||
ref="importFormRef"
|
||||
label-width="auto"
|
||||
style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="importFormData"
|
||||
:rules="importFormRules"
|
||||
>
|
||||
<el-form ref="importFormRef" label-width="auto" style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="importFormData" :rules="importFormRules">
|
||||
<el-form-item label="文件名" prop="files">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
v-model:file-list="importFormData.files"
|
||||
class="w-full"
|
||||
<el-upload ref="uploadRef" v-model:file-list="importFormData.files" class="w-full"
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
:drag="true"
|
||||
:limit="1"
|
||||
:auto-upload="false"
|
||||
:on-exceed="handleFileExceed"
|
||||
>
|
||||
:drag="true" :limit="1" :auto-upload="false" :on-exceed="handleFileExceed">
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
<span>将文件拖到此处,或</span>
|
||||
@@ -467,13 +332,8 @@
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
*.xlsx / *.xls
|
||||
<el-link
|
||||
v-if="contentConfig.importTemplate"
|
||||
type="primary"
|
||||
icon="download"
|
||||
:underline="false"
|
||||
@click="handleDownloadTemplate"
|
||||
>
|
||||
<el-link v-if="contentConfig.importTemplate" type="primary" icon="download" :underline="false"
|
||||
@click="handleDownloadTemplate">
|
||||
下载模板
|
||||
</el-link>
|
||||
</div>
|
||||
@@ -485,11 +345,7 @@
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="importFormData.files.length === 0"
|
||||
@click="handleImportSubmit"
|
||||
>
|
||||
<el-button type="primary" :disabled="importFormData.files.length === 0" @click="handleImportSubmit">
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="handleCloseImportModal">取 消</el-button>
|
||||
@@ -1005,10 +861,10 @@ function fetchPageData(formData: IObject = {}, isRestart = false) {
|
||||
.indexAction(
|
||||
showPagination
|
||||
? {
|
||||
[request.pageName]: pagination.currentPage,
|
||||
[request.limitName]: pagination.pageSize,
|
||||
...formData,
|
||||
}
|
||||
[request.pageName]: pagination.currentPage,
|
||||
[request.limitName]: pagination.pageSize,
|
||||
...formData,
|
||||
}
|
||||
: formData
|
||||
)
|
||||
.then((data) => {
|
||||
@@ -1073,7 +929,7 @@ function saveXlsx(fileData: BlobPart, fileName: string) {
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
}
|
||||
function test(rows: any[]) {}
|
||||
function test(rows: any[]) { }
|
||||
|
||||
const defaultSelData = ref<IObject[]>([]);
|
||||
// 设置默认选择
|
||||
@@ -1115,17 +971,21 @@ defineExpose({
|
||||
:deep(.el-table .el-table__cell) {
|
||||
z-index: inherit;
|
||||
}
|
||||
|
||||
.el-card {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
overflow: visible;
|
||||
|
||||
.el-table__header-wrapper {
|
||||
position: sticky;
|
||||
z-index: calc(var(--el-table-index) + 2);
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table td.el-table__cell div) {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -1,44 +1,42 @@
|
||||
<!-- 文件上传组件 -->
|
||||
<template>
|
||||
<div>
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
:style="props.style"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:http-request="handleUpload"
|
||||
:on-progress="handleProgress"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:accept="props.accept"
|
||||
:limit="props.limit"
|
||||
multiple
|
||||
>
|
||||
<!-- 上传文件按钮 -->
|
||||
<el-button type="primary" :disabled="fileList.length >= props.limit">
|
||||
{{ props.uploadBtnText }}
|
||||
</el-button>
|
||||
|
||||
<el-upload v-model:file-list="fileList" :style="props.style" :before-upload="handleBeforeUpload"
|
||||
:http-request="handleUpload" :on-progress="handleProgress" :on-success="handleSuccess" :on-error="handleError"
|
||||
:accept="props.accept" :limit="props.limit" multiple drag :tip="'支持多个文件上传,单个文件不超过 ' + props.maxFileSize + 'MB'">
|
||||
<!-- 拖拽上传区域 -->
|
||||
<div>
|
||||
<el-icon class="el-icon--upload">
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或 <em>点击上传</em>
|
||||
<br />
|
||||
<small>支持格式:{{ props.accept }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文件列表 -->
|
||||
<template #file="{ file }">
|
||||
<div class="el-upload-list__item-info">
|
||||
<a class="el-upload-list__item-name" @click="handleDownload(file)">
|
||||
<el-icon><Document /></el-icon>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
<span class="el-upload-list__item-file-name">{{ file.name }}</span>
|
||||
<span class="el-icon--close" @click="handleRemove(file.url!)">
|
||||
<el-icon><Close /></el-icon>
|
||||
<span class="el-icon--close" @click="handleRemove(file.url)">
|
||||
<el-icon>
|
||||
<Close />
|
||||
</el-icon>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
<el-progress
|
||||
:style="{
|
||||
display: showProgress ? 'inline-flex' : 'none',
|
||||
width: '100%',
|
||||
}"
|
||||
:percentage="progressPercent"
|
||||
/>
|
||||
<el-progress :style="{
|
||||
display: showProgress ? 'inline-flex' : 'none',
|
||||
width: '100%',
|
||||
marginTop: '10px',
|
||||
}" :percentage="progressPercent" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -48,9 +46,14 @@ import {
|
||||
UploadProgressEvent,
|
||||
UploadRequestOptions,
|
||||
} from "element-plus";
|
||||
import { Upload, Document, Close } from "@element-plus/icons-vue";
|
||||
|
||||
import CommonApi, { FileInfo, uploadResponse } from "@/api/account/common";
|
||||
|
||||
const emit = defineEmits<{
|
||||
"upload-success": [fileInfo: string];
|
||||
}>();
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 请求携带的额外参数
|
||||
@@ -96,7 +99,6 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: "上传文件",
|
||||
},
|
||||
|
||||
/**
|
||||
* 样式
|
||||
*/
|
||||
@@ -104,7 +106,7 @@ const props = defineProps({
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
width: "300px",
|
||||
width: "100%",
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -147,6 +149,7 @@ function handleBeforeUpload(file: UploadRawFile) {
|
||||
ElMessage.warning("上传图片不能大于" + props.maxFileSize + "M");
|
||||
return false;
|
||||
}
|
||||
showProgress.value = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -190,10 +193,13 @@ const handleProgress = (event: UploadProgressEvent) => {
|
||||
const handleSuccess = (fileInfo: string) => {
|
||||
ElMessage.success("上传成功");
|
||||
modelValue.value = [...modelValue.value, fileInfo];
|
||||
emit("upload-success", fileInfo);
|
||||
showProgress.value = false;
|
||||
};
|
||||
|
||||
const handleError = (error: any) => {
|
||||
ElMessage.error("上传失败");
|
||||
showProgress.value = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
242
src/components/Upload/GfileUpload.vue
Normal file
242
src/components/Upload/GfileUpload.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<!-- 文件上传组件 -->
|
||||
<template>
|
||||
<div>
|
||||
<el-upload v-model:file-list="fileList" :style="props.style" :before-upload="handleBeforeUpload"
|
||||
:on-change="handleChange" :accept="accept" multiple drag :tip="'支持多个文件上传,单个文件不超过 ' + props.maxFileSize + 'MB'"
|
||||
:auto-upload="false">
|
||||
<!-- 拖拽上传区域 -->
|
||||
<div>
|
||||
<el-icon class="el-icon--upload">
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或 <em>点击上传</em>
|
||||
<br />
|
||||
<small>支持格式:{{ accept }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文件列表 -->
|
||||
<template #file="{ file }">
|
||||
<div class="el-upload-list__item-info">
|
||||
<a class="el-upload-list__item-name" @click="handleDownload(file)">
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
<span class="el-upload-list__item-file-name">{{ file.name }}</span>
|
||||
<span class="el-icon--close" @click="handleRemove(file.url)">
|
||||
<el-icon>
|
||||
<Close />
|
||||
</el-icon>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { UploadFilled, Document, Close } from "@element-plus/icons-vue";
|
||||
|
||||
const emit = defineEmits({
|
||||
"file-selected": null,
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 文件上传数量限制
|
||||
*/
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
/**
|
||||
* 单个文件上传大小限制(单位MB)
|
||||
*/
|
||||
maxFileSize: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
/**
|
||||
* 上传文件类型
|
||||
*/
|
||||
accept: {
|
||||
type: String,
|
||||
default: "*",
|
||||
},
|
||||
/**
|
||||
* 选择文件成功的回调函数
|
||||
*/
|
||||
successCallback: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
/**
|
||||
* 样式
|
||||
*/
|
||||
style: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
width: "100%",
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const fileList = defineModel({
|
||||
type: Array,
|
||||
default: () => [],
|
||||
});
|
||||
|
||||
/**
|
||||
* 文件选择变化
|
||||
*/
|
||||
function handleChange(file, fileList) {
|
||||
if (file.status === 'ready') {
|
||||
// 如果限制为1个文件,移除之前的文件,只保留当前新文件
|
||||
if (props.limit === 1 && fileList.length > 1) {
|
||||
fileList.splice(0, fileList.length - 1);
|
||||
}
|
||||
const rawFile = file.raw;
|
||||
// 限制文件类型
|
||||
if (props.accept !== "*") {
|
||||
const acceptedTypes = props.accept.split(',').map(type => type.trim());
|
||||
const isAccepted = acceptedTypes.some(type => {
|
||||
let checkType = type;
|
||||
if (!type.startsWith('.') && !type.includes('/')) {
|
||||
checkType = '.' + type;
|
||||
}
|
||||
if (checkType.startsWith('.')) {
|
||||
return rawFile.name.toLowerCase().endsWith(checkType.toLowerCase());
|
||||
} else if (checkType.includes('*')) {
|
||||
const [main] = checkType.split('/');
|
||||
return rawFile.type.startsWith(main + '/');
|
||||
} else {
|
||||
return rawFile.type === checkType;
|
||||
}
|
||||
});
|
||||
if (!isAccepted) {
|
||||
ElMessage.warning("文件类型不符合要求");
|
||||
// 从 fileList 中移除不符合的文件
|
||||
fileList.splice(fileList.indexOf(file), 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (props.successCallback) {
|
||||
props.successCallback(rawFile);
|
||||
} else {
|
||||
emit("file-selected", rawFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传前校验
|
||||
*/
|
||||
function handleBeforeUpload(file) {
|
||||
// 限制文件大小
|
||||
if (file.size > props.maxFileSize * 1024 * 1024) {
|
||||
ElMessage.warning("上传文件不能大于" + props.maxFileSize + "M");
|
||||
return false;
|
||||
}
|
||||
// 限制文件类型
|
||||
if (props.accept !== "*") {
|
||||
const acceptedTypes = props.accept.split(',').map(type => type.trim());
|
||||
const isAccepted = acceptedTypes.some(type => {
|
||||
if (type.startsWith('.')) {
|
||||
return file.name.toLowerCase().endsWith(type.toLowerCase());
|
||||
} else {
|
||||
return file.type === type;
|
||||
}
|
||||
});
|
||||
if (!isAccepted) {
|
||||
ElMessage.warning("文件类型不符合要求");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
function handleRemove(fileUrl) {
|
||||
// 从 fileList 中移除
|
||||
fileList.value = fileList.value.filter(f => f.url !== fileUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*/
|
||||
function handleDownload(file) {
|
||||
// 本地文件,不支持下载
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.el-upload-list__item .el-icon--close {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 5px;
|
||||
color: var(--el-text-color-regular);
|
||||
cursor: pointer;
|
||||
opacity: 0.75;
|
||||
transition: opacity var(--el-transition-duration);
|
||||
transform: translateY(-50%);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-upload-list) {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
:deep(.el-upload-list__item) {
|
||||
margin: 5px 0;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background: #f0f0f0;
|
||||
border-color: var(--el-color-primary-light-3);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.el-upload-list__item-info {
|
||||
flex: 1;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--el-text-color-primary);
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
margin-right: 8px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-upload-list__item-file-name {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-icon--close {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
252
src/components/importData/index.vue
Normal file
252
src/components/importData/index.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<!-- 批量导入数据dialog -->
|
||||
<template>
|
||||
<div>
|
||||
<div class="btn_row">
|
||||
<el-button type="primary" icon="Upload" @click="show">批量导入</el-button>
|
||||
<el-button icon="Download" @click="downloadTemplateAjax">下载银收客模板</el-button>
|
||||
</div>
|
||||
<el-dialog title="批量导入" width="800px" v-model="visible" :close-on-click-modal="false"
|
||||
:close-on-press-escape="false">
|
||||
<div class="row">
|
||||
<tabHeader v-model="tabActive" :list="tabs" />
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<div class="import_container" v-if="tabActive == 0">
|
||||
<div class="header_title">第一步:选择模板</div>
|
||||
<div class="row mt14 pb50">
|
||||
<div class="list">
|
||||
<div class="item" :class="{ active: platformActive == index }" v-for="(item, index) in platformList"
|
||||
:key="item.id" @click="selectPlatform(item)">
|
||||
<img class="img" :src="item.img" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header_title">第二步:上传文件
|
||||
<span>单次仅可上传一个文件</span>
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<GfileUpload v-model="form.files" :accept="platformList[platformActive]?.file_type || ''" :limit="1"
|
||||
@file-selected="fileSelected" />
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<div class="footer_wrap">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" :disabled="!form.files.length" :loading="loading" @click="startImportHandle">
|
||||
<template v-if="!form.files.length">请选择文件</template>
|
||||
<template v-else>开始导入</template>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="tableData" border stripe v-if="tabActive == 1" height="419px">
|
||||
<el-table-column prop="file_name" label="文件名称" width="300" />
|
||||
<el-table-column prop="created_time" label="导入时间" width="200" />
|
||||
<el-table-column prop="status_text" label="导入状态" width="150">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status == 0" type="info" disable-transitions>待处理...</el-tag>
|
||||
<el-tag v-else-if="scope.row.status == 1" type="warning" disable-transitions>处理中...</el-tag>
|
||||
<el-tag v-else-if="scope.row.status == 2" type="success" disable-transitions>处理完成</el-tag>
|
||||
<el-tag v-else-if="scope.row.status == -1" type="danger" disable-transitions>导入失败</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="importResult" label="导入结果">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.status == 2">
|
||||
<div class="column">
|
||||
<div>
|
||||
<el-text type="success">成功:{{ scope.row.success_num }} 条</el-text>
|
||||
</div>
|
||||
<div>
|
||||
<el-text type="danger">失败:{{ scope.row.fail_num }} 条</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { getplatlist, uploadFile, importlist, downloadTemp } from '@/importDataApi/index.js';
|
||||
import GfileUpload from '../Upload/GfileUpload.vue';
|
||||
import tabHeader from '@/views/marketing_center/components/tabHeader.vue';
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: [String, Number],
|
||||
default: 3, // 3商品 4台桌区域 5台桌 6会员 7菜品销售统计 8台桌销售统计 9订单销售统计
|
||||
},
|
||||
});
|
||||
|
||||
const platformList = ref([]);
|
||||
const platformActive = ref(0);
|
||||
function selectPlatform(item) {
|
||||
form.value.files = [];
|
||||
platformActive.value = platformList.value.findIndex(i => i.id === item.id);
|
||||
}
|
||||
|
||||
const tabs = ref([
|
||||
{ label: '导入数据', name: 'importData' },
|
||||
{ label: '导入记录', name: 'importRecord' },
|
||||
]);
|
||||
|
||||
const tabActive = ref(0)
|
||||
|
||||
watch(tabActive, (newVal) => {
|
||||
if (newVal === 1) {
|
||||
getImportRecord();
|
||||
}
|
||||
});
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const form = ref({
|
||||
files: [],
|
||||
platform: ''
|
||||
})
|
||||
|
||||
function fileSelected(file) {
|
||||
console.log('fileSelected', file);
|
||||
}
|
||||
|
||||
// 开始导入
|
||||
async function startImportHandle() {
|
||||
try {
|
||||
form.value.platform = platformList.value[platformActive.value]?.id;
|
||||
const formData = new FormData();
|
||||
formData.append('file', form.value.files[0].raw);
|
||||
formData.append('shop_id', localStorage.getItem('shopId'));
|
||||
formData.append('type', props.type);
|
||||
formData.append('platform', form.value.platform);
|
||||
loading.value = true;
|
||||
await uploadFile(formData);
|
||||
ElMessage.success('文件上传成功,正在导入数据,请在导入记录中查看导入结果');
|
||||
form.value.files = []
|
||||
tabActive.value = 1
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// 获取平台列表
|
||||
async function getPlatformList() {
|
||||
try {
|
||||
const res = await getplatlist({ plat_type: props.type });
|
||||
platformList.value = res;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 导入记录
|
||||
const tableData = ref([]);
|
||||
async function getImportRecord() {
|
||||
try {
|
||||
const res = await importlist({ shop_id: localStorage.getItem('shopId') });
|
||||
tableData.value = res;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 下载模板
|
||||
async function downloadTemplateAjax() {
|
||||
try {
|
||||
const res = await downloadTemp({ plat_type: props.type });
|
||||
window.open(res, '_blank');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getPlatformList()
|
||||
});
|
||||
|
||||
function show() {
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.btn_row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-left: 14px;
|
||||
}
|
||||
|
||||
.header_title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
&.mt14 {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
&.pb50 {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.import_container {
|
||||
.list {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
|
||||
.item {
|
||||
width: 122px;
|
||||
height: 42px;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer_wrap {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user