增加模版
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import request from '@/utils/request'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { blobValidate } from '@/utils/bms'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
export function listFileTemplate(query) {
|
||||
return request({
|
||||
url: '/system/fileTemplate/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getFileTemplate(templateId) {
|
||||
return request({
|
||||
url: '/system/fileTemplate/' + templateId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getFileTemplateByCode(fileCode) {
|
||||
return request({
|
||||
url: '/system/fileTemplate/code/' + encodeURIComponent(fileCode),
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addFileTemplate(data) {
|
||||
return request({
|
||||
url: '/system/fileTemplate',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateFileTemplate(data) {
|
||||
return request({
|
||||
url: '/system/fileTemplate',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function delFileTemplate(templateIds) {
|
||||
return request({
|
||||
url: '/system/fileTemplate/' + templateIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function uploadFileTemplate(templateId, file) {
|
||||
const form = new FormData()
|
||||
form.append('templateId', templateId)
|
||||
form.append('file', file)
|
||||
return request({
|
||||
url: '/system/fileTemplate/upload',
|
||||
method: 'post',
|
||||
data: form,
|
||||
headers: { 'Content-Type': 'multipart/form-data', repeatSubmit: false },
|
||||
timeout: 120000
|
||||
})
|
||||
}
|
||||
|
||||
/** Download template by file_code (shared by import dialogs). */
|
||||
export async function downloadFileTemplate(fileCode, fallbackName) {
|
||||
const code = String(fileCode || '').trim()
|
||||
if (!code) throw new Error('fileCode required')
|
||||
const data = await request({
|
||||
url: '/system/fileTemplate/download/' + encodeURIComponent(code),
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
timeout: 120000
|
||||
})
|
||||
if (!blobValidate(data)) {
|
||||
const text = await data.text()
|
||||
const rsp = JSON.parse(text)
|
||||
throw new Error(errorCode[rsp.code] || rsp.msg || errorCode.default || 'download failed')
|
||||
}
|
||||
const name = (fallbackName || code) + '.xlsx'
|
||||
saveAs(new Blob([data]), name)
|
||||
return true
|
||||
}
|
||||
@@ -198,6 +198,11 @@ const props = defineProps({
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
// System template file_code (sys_file_template); preferred over static templateUrl
|
||||
templateFileCode: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 确定按钮文字
|
||||
submitText: {
|
||||
type: String,
|
||||
@@ -229,7 +234,9 @@ const uploadUrl = computed(() => {
|
||||
return import.meta.env.VITE_APP_BASE_API + props.action + '?' + query.toString()
|
||||
})
|
||||
|
||||
const hasTemplate = computed(() => !!(props.templateDownloadHandler || props.templateUrl || props.templateAction))
|
||||
const hasTemplate = computed(() => !!(
|
||||
props.templateDownloadHandler || props.templateFileCode || props.templateUrl || props.templateAction
|
||||
))
|
||||
|
||||
// 打开对话框(供父组件通过 ref 调用)
|
||||
function open() {
|
||||
@@ -254,7 +261,7 @@ function handleClosed() {
|
||||
selectedFiles.value = []
|
||||
}
|
||||
|
||||
// 下载模板:自定义 handler > 静态文件 > 接口 proxy.download
|
||||
// Download: custom handler > system fileCode (OSS) > static url > proxy.download
|
||||
async function handleDownloadTemplate() {
|
||||
if (typeof props.templateDownloadHandler === 'function') {
|
||||
try {
|
||||
@@ -265,6 +272,16 @@ async function handleDownloadTemplate() {
|
||||
}
|
||||
return
|
||||
}
|
||||
if (props.templateFileCode) {
|
||||
try {
|
||||
const { downloadFileTemplate } = await import('@/api/system/fileTemplate')
|
||||
await downloadFileTemplate(props.templateFileCode, props.templateFileName)
|
||||
} catch (e) {
|
||||
const tip = (e && e.message) ? e.message : '下载模板失败'
|
||||
proxy.$modal.msgError(tip)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (props.templateUrl) {
|
||||
const link = document.createElement('a')
|
||||
link.href = props.templateUrl
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
:title="titleAll"
|
||||
width="560px"
|
||||
:show-update-support="false"
|
||||
template-url="/templates/product/product-import-all-template.xlsx"
|
||||
:template-file-code="tplCodeAll"
|
||||
:template-file-name="tplAll"
|
||||
:import-desc="allImportDesc"
|
||||
:file-limit="5"
|
||||
@@ -117,7 +117,7 @@
|
||||
:show-update-support="false"
|
||||
:before-submit="validateCountryImport"
|
||||
:import-desc="countryImportDesc"
|
||||
template-url="/templates/product/product-import-country-template.xlsx"
|
||||
:template-file-code="tplCodeCountry"
|
||||
:template-file-name="tplCtry"
|
||||
:file-limit="5"
|
||||
:submit-handler="runImportCountry"
|
||||
@@ -207,8 +207,14 @@ const labCountry = '目的国家'
|
||||
const labPickCountry = '请选择国家'
|
||||
const labImporting = '正在导入'
|
||||
const labSaving = '正在保存'
|
||||
const tplAll = '产品多国家价表导入模板'
|
||||
const tplCtry = '产品按国家导入模板'
|
||||
const tplAll = computed(() => (props.kind === 'product'
|
||||
? '产品多国家价表导入模板'
|
||||
: '红线价多国导入模板'))
|
||||
const tplCtry = computed(() => (props.kind === 'product'
|
||||
? '产品按国家导入模板'
|
||||
: '红线价单国导入模板'))
|
||||
const tplCodeAll = computed(() => (props.kind === 'product' ? 'PRODUCT_ALL' : 'REDLINE_ALL'))
|
||||
const tplCodeCountry = computed(() => (props.kind === 'product' ? 'PRODUCT_COUNTRY' : 'REDLINE_COUNTRY'))
|
||||
const titleAll = props.kind === 'product' ? '产品 Excel 导入' : '红线价 Excel 导入'
|
||||
const titleCtry = '按国家导入'
|
||||
const headerTitle = props.kind === 'product' ? '编辑产品价格' : '编辑红线价'
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
title="批量修改计费重"
|
||||
width="520px"
|
||||
action="/price/billing/bill/chargeWeight/importData"
|
||||
template-file-code="CHARGE_WEIGHT"
|
||||
template-file-name="批量修改计费重导入模板"
|
||||
:show-update-support="false"
|
||||
:import-desc="chargeWeightImportDesc"
|
||||
@@ -641,10 +642,16 @@ function buildChargeWeightTemplateQuery() {
|
||||
|
||||
async function downloadChargeWeightImportTemplate() {
|
||||
const payload = buildChargeWeightTemplateQuery()
|
||||
if (payload.fillByFilter && !hasBatchFeeQuery(tableRef.value?.getQueryParams?.() || {})) {
|
||||
throw new Error('请先设置列表筛选条件')
|
||||
// Checked: backend fills orderNos from list filters. Unchecked: OSS template CHARGE_WEIGHT.
|
||||
if (payload.fillByFilter) {
|
||||
if (!hasBatchFeeQuery(tableRef.value?.getQueryParams?.() || {})) {
|
||||
throw new Error('请先设置列表筛选条件')
|
||||
}
|
||||
await downloadChargeWeightTemplate(payload)
|
||||
return
|
||||
}
|
||||
await downloadChargeWeightTemplate(payload)
|
||||
const { downloadFileTemplate } = await import('@/api/system/fileTemplate')
|
||||
await downloadFileTemplate('CHARGE_WEIGHT', '批量修改计费重导入模板')
|
||||
}
|
||||
|
||||
function openChargeWeightImportAttach() {
|
||||
|
||||
@@ -144,6 +144,8 @@
|
||||
:show-update-support="false"
|
||||
:import-desc="importDesc"
|
||||
:file-limit="5"
|
||||
template-file-code="SALES_ALL"
|
||||
template-file-name="销售价多国导入模板"
|
||||
:submit-handler="runImportAsync"
|
||||
@success="onImportSuccess"
|
||||
/>
|
||||
@@ -155,6 +157,8 @@
|
||||
:show-update-support="false"
|
||||
:import-desc="countryImportDesc"
|
||||
:file-limit="5"
|
||||
template-file-code="SALES_COUNTRY"
|
||||
template-file-name="销售价单国导入模板"
|
||||
:before-submit="validateCountryImport"
|
||||
:submit-handler="runCountryImportAsync"
|
||||
@success="onImportSuccess"
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
row-key="templateId"
|
||||
selection
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" v-hasPermi="['system:fileTemplate:add']" @click="handleAdd">{{ labAdd }}</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="selection.length !== 1" v-hasPermi="['system:fileTemplate:edit']" @click="handleUpdate(selection[0])">{{ labEdit }}</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="!selection.length" v-hasPermi="['system:fileTemplate:remove']" @click="handleDelete(ids)">{{ labRemove }}</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<template #col-status="{ row }">
|
||||
<el-tag size="small" :type="row.status === '0' ? 'success' : 'info'">{{ row.status === '0' ? labOn : labOff }}</el-tag>
|
||||
</template>
|
||||
<template #col-filePath="{ row }">
|
||||
<span>{{ row.filePath ? labUploaded : labEmpty }}</span>
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" :disabled="!row.filePath" v-hasPermi="['system:fileTemplate:download']" @click="handleDownload(row)">{{ labDown }}</el-button>
|
||||
<el-button link type="primary" v-hasPermi="['system:fileTemplate:edit']" @click="handleUpdate(row)">{{ labEdit }}</el-button>
|
||||
<el-button link type="primary" v-hasPermi="['system:fileTemplate:remove']" @click="handleDelete(row.templateId)">{{ labRemove }}</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<el-dialog :title="title" v-model="open" width="560px" append-to-body @closed="onFormClosed">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item :label="labType" prop="templateType">
|
||||
<el-select v-model="form.templateType" clearable :placeholder="phType" style="width: 100%;">
|
||||
<el-option v-for="d in sys_file_template_type" :key="d.value" :label="d.label" :value="d.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="labCode" prop="fileCode">
|
||||
<el-input v-model="form.fileCode" :placeholder="phCode" maxlength="64" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="labName" prop="templateName">
|
||||
<el-input v-model="form.templateName" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="labFile" prop="uploadFile">
|
||||
<div class="tpl-upload">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
drag
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
accept=".xlsx,.xls"
|
||||
:on-change="onUploadChange"
|
||||
:on-remove="onUploadRemove"
|
||||
:on-exceed="onUploadExceed"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">{{ labDrop }}</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
<span v-if="form.fileName">{{ labCurrentFile }}{{ form.fileName }}</span>
|
||||
<span v-else>{{ labFileTip }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="labStatus" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio value="0">{{ labOn }}</el-radio>
|
||||
<el-radio value="1">{{ labOff }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="labRemark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" maxlength="500" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="saving" @click="submitForm">{{ labOk }}</el-button>
|
||||
<el-button @click="open = false">{{ labCancel }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="FileTemplate">
|
||||
import { UploadFilled } from '@element-plus/icons-vue'
|
||||
import {
|
||||
listFileTemplate, getFileTemplate, addFileTemplate, updateFileTemplate,
|
||||
delFileTemplate, uploadFileTemplate, downloadFileTemplate
|
||||
} from '@/api/system/fileTemplate'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { sys_file_template_type } = useDict('sys_file_template_type')
|
||||
const tableRef = ref()
|
||||
const formRef = ref()
|
||||
const uploadRef = ref()
|
||||
const open = ref(false)
|
||||
const title = ref('')
|
||||
const saving = ref(false)
|
||||
const pendingFile = ref(null)
|
||||
|
||||
const labAdd = '新增'
|
||||
const labEdit = '修改'
|
||||
const labRemove = '删除'
|
||||
const labDown = '下载'
|
||||
const labType = '类型'
|
||||
const labCode = '文件代码'
|
||||
const labName = '模板名称'
|
||||
const labFile = '模板文件'
|
||||
const labStatus = '状态'
|
||||
const labRemark = '备注'
|
||||
const labOn = '正常'
|
||||
const labOff = '停用'
|
||||
const labOk = '确定'
|
||||
const labCancel = '取消'
|
||||
const labUploaded = '已上传'
|
||||
const labEmpty = '未上传'
|
||||
const labDrop = '将 xlsx/xls 拖到此处,或点击上传'
|
||||
const labCurrentFile = '当前文件:'
|
||||
const labFileTip = '仅支持 .xlsx / .xls'
|
||||
const phType = '请选择类型'
|
||||
const phCode = 'PRODUCT_ALL'
|
||||
|
||||
const searchColumns = [
|
||||
{ label: labType, prop: 'templateType', component: 'select', dict: 'sys_file_template_type', placeholder: labType },
|
||||
{ label: labCode, prop: 'fileCode', component: 'input', placeholder: labCode },
|
||||
{ label: labName, prop: 'templateName', component: 'input', placeholder: labName },
|
||||
{ label: labStatus, prop: 'status', component: 'select', options: [
|
||||
{ value: '0', label: labOn }, { value: '1', label: labOff }
|
||||
] }
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{ label: labType, prop: 'templateType', width: 140, dict: 'sys_file_template_type' },
|
||||
{ label: labCode, prop: 'fileCode', minWidth: 160 },
|
||||
{ label: labName, prop: 'templateName', minWidth: 180 },
|
||||
{ label: '文件', prop: 'fileName', minWidth: 160, showOverflowTooltip: true },
|
||||
{ label: '附件', prop: 'filePath', width: 100 },
|
||||
{ label: labStatus, prop: 'status', width: 90 },
|
||||
{ label: '更新时间', prop: 'updateTime', width: 170 },
|
||||
{ label: '操作', prop: 'action', width: 180, fixed: 'right' }
|
||||
]
|
||||
|
||||
const form = reactive({
|
||||
templateId: undefined,
|
||||
templateType: undefined,
|
||||
fileCode: '',
|
||||
templateName: '',
|
||||
fileName: '',
|
||||
filePath: '',
|
||||
status: '0',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
templateType: [{ required: true, message: phType, trigger: 'change' }],
|
||||
fileCode: [{ required: true, message: labCode, trigger: 'blur' }],
|
||||
templateName: [{ required: true, message: labName, trigger: 'blur' }]
|
||||
}
|
||||
|
||||
function queryList(params) {
|
||||
return listFileTemplate(params)
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.templateId = undefined
|
||||
form.templateType = undefined
|
||||
form.fileCode = ''
|
||||
form.templateName = ''
|
||||
form.fileName = ''
|
||||
form.filePath = ''
|
||||
form.status = '0'
|
||||
form.remark = ''
|
||||
pendingFile.value = null
|
||||
nextTick(() => {
|
||||
uploadRef.value?.clearFiles?.()
|
||||
formRef.value?.clearValidate?.()
|
||||
})
|
||||
}
|
||||
|
||||
function onFormClosed() {
|
||||
resetForm()
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
resetForm()
|
||||
title.value = labAdd
|
||||
open.value = true
|
||||
}
|
||||
|
||||
function handleUpdate(row) {
|
||||
resetForm()
|
||||
getFileTemplate(row.templateId).then(res => {
|
||||
const data = res.data || {}
|
||||
form.templateId = data.templateId
|
||||
form.templateType = data.templateType || undefined
|
||||
form.fileCode = data.fileCode || ''
|
||||
form.templateName = data.templateName || ''
|
||||
form.fileName = data.fileName || ''
|
||||
form.filePath = data.filePath || ''
|
||||
form.status = data.status || '0'
|
||||
form.remark = data.remark || ''
|
||||
title.value = labEdit
|
||||
open.value = true
|
||||
})
|
||||
}
|
||||
|
||||
function onUploadChange(file) {
|
||||
pendingFile.value = file?.raw || null
|
||||
}
|
||||
|
||||
function onUploadRemove() {
|
||||
pendingFile.value = null
|
||||
}
|
||||
|
||||
function onUploadExceed(files) {
|
||||
uploadRef.value?.clearFiles?.()
|
||||
const raw = files?.[0]
|
||||
if (raw) {
|
||||
uploadRef.value?.handleStart?.(raw)
|
||||
pendingFile.value = raw
|
||||
}
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
const valid = await formRef.value.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
saving.value = true
|
||||
try {
|
||||
let templateId = form.templateId
|
||||
if (templateId) {
|
||||
await updateFileTemplate({
|
||||
templateId: form.templateId,
|
||||
templateType: form.templateType,
|
||||
fileCode: form.fileCode,
|
||||
templateName: form.templateName,
|
||||
status: form.status,
|
||||
remark: form.remark
|
||||
})
|
||||
} else {
|
||||
const res = await addFileTemplate({
|
||||
templateType: form.templateType,
|
||||
fileCode: form.fileCode,
|
||||
templateName: form.templateName,
|
||||
status: form.status,
|
||||
remark: form.remark
|
||||
})
|
||||
templateId = res?.data?.templateId
|
||||
if (!templateId) {
|
||||
throw new Error('保存成功但未返回模板ID')
|
||||
}
|
||||
}
|
||||
if (pendingFile.value) {
|
||||
await uploadFileTemplate(templateId, pendingFile.value)
|
||||
}
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
open.value = false
|
||||
tableRef.value?.refresh?.()
|
||||
} catch (e) {
|
||||
if (e?.message) proxy.$modal.msgError(e.message)
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleDelete(ids) {
|
||||
const idList = Array.isArray(ids) ? ids : [ids]
|
||||
proxy.$modal.confirm('确认删除选中模板?').then(() => {
|
||||
return delFileTemplate(idList.join(','))
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
tableRef.value?.refresh?.()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
async function handleDownload(row) {
|
||||
try {
|
||||
await downloadFileTemplate(row.fileCode, row.templateName || row.fileCode)
|
||||
} catch (e) {
|
||||
proxy.$modal.msgError(e?.message || '下载失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tpl-upload {
|
||||
width: 100%;
|
||||
}
|
||||
.tpl-upload :deep(.el-upload) {
|
||||
width: 100%;
|
||||
}
|
||||
.tpl-upload :deep(.el-upload-dragger) {
|
||||
width: 100%;
|
||||
padding: 16px 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user