feat/合同模板管理
This commit is contained in:
parent
a19f48034d
commit
5fb095f392
3296
Swagger.json
3296
Swagger.json
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,100 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 合同模板列表
|
||||||
|
export function listContractTemplates(query) {
|
||||||
|
return request({
|
||||||
|
url: '/api/v1/contract-template',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
contractType: query.contractType,
|
||||||
|
templateName: query.templateName,
|
||||||
|
pageSize: query.pageSize,
|
||||||
|
pageIndex: query.pageNum
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取单个合同模板详情
|
||||||
|
export function getContractTemplate(id) {
|
||||||
|
return request({
|
||||||
|
url: `/api/v1/contract-template/${id}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增合同模板(文件必填)
|
||||||
|
export function createContractTemplate(data) {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('contractType', data.contractType)
|
||||||
|
formData.append('templateName', data.templateName)
|
||||||
|
formData.append('publishDate', data.publishDate)
|
||||||
|
if (data.file) {
|
||||||
|
formData.append('file', data.file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return request({
|
||||||
|
url: '/api/v1/contract-template',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
repeatSubmit: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改合同模板(文件可选)
|
||||||
|
export function updateContractTemplate(data) {
|
||||||
|
const formData = new FormData()
|
||||||
|
if (data.contractType) {
|
||||||
|
formData.append('contractType', data.contractType)
|
||||||
|
}
|
||||||
|
if (data.templateName) {
|
||||||
|
formData.append('templateName', data.templateName)
|
||||||
|
}
|
||||||
|
if (data.publishDate) {
|
||||||
|
formData.append('publishDate', data.publishDate)
|
||||||
|
}
|
||||||
|
if (data.file) {
|
||||||
|
formData.append('file', data.file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return request({
|
||||||
|
url: `/api/v1/contract-template/${data.id}`,
|
||||||
|
method: 'put',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
repeatSubmit: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅更新模板文件
|
||||||
|
export function updateContractTemplateFile(id, file) {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
return request({
|
||||||
|
url: `/api/v1/contract-template/${id}/file`,
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
repeatSubmit: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除合同模板(支持批量)
|
||||||
|
export function deleteContractTemplates(ids) {
|
||||||
|
return request({
|
||||||
|
url: '/api/v1/contract-template',
|
||||||
|
method: 'delete',
|
||||||
|
data: {
|
||||||
|
ids
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -86,25 +86,7 @@ export const constantRoutes = [
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// ==================== 采购管理 ====================
|
// ==================== 采购管理 ====================
|
||||||
{
|
|
||||||
path: '/purchase',
|
|
||||||
component: Layout,
|
|
||||||
redirect: 'noRedirect',
|
|
||||||
alwaysShow: true,
|
|
||||||
name: 'Purchase',
|
|
||||||
meta: {
|
|
||||||
title: '采购管理',
|
|
||||||
icon: 'shopping'
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'task-management',
|
|
||||||
component: () => import('@/views/purchase/task-management'),
|
|
||||||
name: 'PurchaseTaskManagement',
|
|
||||||
meta: { title: '采购任务管理', icon: 'list' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// {
|
// {
|
||||||
// path: '/purchase',
|
// path: '/purchase',
|
||||||
// component: Layout,
|
// component: Layout,
|
||||||
|
|
@ -117,6 +99,12 @@ export const constantRoutes = [
|
||||||
// },
|
// },
|
||||||
// children: [
|
// children: [
|
||||||
// {
|
// {
|
||||||
|
// path: 'task-management',
|
||||||
|
// component: () => import('@/views/purchase/task-management'),
|
||||||
|
// name: 'PurchaseTaskManagement',
|
||||||
|
// meta: { title: '采购任务管理', icon: 'list' }
|
||||||
|
// }
|
||||||
|
// {
|
||||||
// path: 'order-list',
|
// path: 'order-list',
|
||||||
// component: () => import('@/views/purchase/order-list'),
|
// component: () => import('@/views/purchase/order-list'),
|
||||||
// name: 'PurchaseOrderList',
|
// name: 'PurchaseOrderList',
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,14 @@
|
||||||
<el-card>
|
<el-card>
|
||||||
<div class="filter-toolbar">
|
<div class="filter-toolbar">
|
||||||
<el-form :model="queryParams" ref="queryForm" :inline="true">
|
<el-form :model="queryParams" ref="queryForm" :inline="true">
|
||||||
|
<el-form-item label="模板名称" prop="templateName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.templateName"
|
||||||
|
placeholder="请输入模板名称"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="合同类型" prop="contractType">
|
<el-form-item label="合同类型" prop="contractType">
|
||||||
<el-select v-model="queryParams.contractType" placeholder="全部" clearable style="width: 150px">
|
<el-select v-model="queryParams.contractType" placeholder="全部" clearable style="width: 150px">
|
||||||
<el-option label="全部" value="" />
|
<el-option label="全部" value="" />
|
||||||
|
|
@ -19,13 +27,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="templateList" border style="width: 100%; margin-top: 20px;">
|
<el-table v-loading="loading" :data="templateList" border style="width: 100%; margin-top: 20px;">
|
||||||
<el-table-column prop="contractCode" label="合同编号" />
|
<el-table-column prop="templateName" label="模板名称" />
|
||||||
<el-table-column prop="contractName" label="合同名称" />
|
|
||||||
<el-table-column prop="contractType" label="合同类型">
|
<el-table-column prop="contractType" label="合同类型">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ getContractTypeName(scope.row.contractType) }}
|
{{ getContractTypeName(scope.row.contractType) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="publishDate" label="发布日期" />
|
||||||
<el-table-column label="操作" width="280" fixed="right" align="center">
|
<el-table-column label="操作" width="280" fixed="right" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
<el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
|
@ -58,18 +66,10 @@
|
||||||
:rules="templateFormRules"
|
:rules="templateFormRules"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
>
|
>
|
||||||
<el-form-item label="合同编号" prop="contractCode">
|
<el-form-item label="模板名称" prop="templateName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="templateForm.contractCode"
|
v-model="templateForm.templateName"
|
||||||
placeholder="请输入合同编号"
|
placeholder="请输入模板名称"
|
||||||
:disabled="isEdit"
|
|
||||||
maxlength="50"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="合同名称" prop="contractName">
|
|
||||||
<el-input
|
|
||||||
v-model="templateForm.contractName"
|
|
||||||
placeholder="请输入合同名称"
|
|
||||||
maxlength="100"
|
maxlength="100"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
@ -83,14 +83,22 @@
|
||||||
<el-option label="销售合同" value="sales" />
|
<el-option label="销售合同" value="sales" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="发布日期" prop="publishDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="templateForm.publishDate"
|
||||||
|
type="date"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
placeholder="请选择发布日期"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="合同模板" prop="templateFile">
|
<el-form-item label="合同模板" prop="templateFile">
|
||||||
<el-upload
|
<el-upload
|
||||||
ref="formUploadRef"
|
ref="formUploadRef"
|
||||||
:file-list="formFileList"
|
:file-list="formFileList"
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
:on-change="handleFormFileChange"
|
@change="handleFormFileChange"
|
||||||
:on-remove="handleFormFileRemove"
|
@remove="handleFormFileRemove"
|
||||||
:limit="1"
|
|
||||||
accept=".doc,.docx,.xls,.xlsx,.pdf"
|
accept=".doc,.docx,.xls,.xlsx,.pdf"
|
||||||
>
|
>
|
||||||
<el-button type="primary">选择文件</el-button>
|
<el-button type="primary">选择文件</el-button>
|
||||||
|
|
@ -100,11 +108,15 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
<div v-if="templateForm.templateFile" style="margin-top: 10px;">
|
<div v-if="templateForm.templateFilePath && !templateForm.file" style="margin-top: 10px;">
|
||||||
<el-button link type="primary" size="small" @click="handlePreviewFile(templateForm.templateFile)">
|
<span style="margin-right: 10px;">当前文件:</span>
|
||||||
{{ templateForm.templateFile.name }}
|
<el-button link type="primary" size="small" @click="handlePreviewFile(templateForm.templateFilePath)">
|
||||||
|
{{ getFileName(templateForm.templateFilePath) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="templateForm.file" style="margin-top: 10px; color: #409eff;">
|
||||||
|
<span>已选择新文件:{{ templateForm.file.name }}</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|
@ -123,6 +135,7 @@
|
||||||
<script setup name="ContractTemplate">
|
<script setup name="ContractTemplate">
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { listContractTemplates, createContractTemplate, updateContractTemplate, deleteContractTemplates } from '@/api/contractTemplate'
|
||||||
import Pagination from '@/components/Pagination'
|
import Pagination from '@/components/Pagination'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
@ -138,28 +151,41 @@ const formFileList = ref([])
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
templateName: '',
|
||||||
contractType: ''
|
contractType: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const templateForm = reactive({
|
const templateForm = reactive({
|
||||||
id: null,
|
id: null,
|
||||||
contractCode: '',
|
templateName: '',
|
||||||
contractName: '',
|
|
||||||
contractType: '',
|
contractType: '',
|
||||||
templateFile: null
|
publishDate: '',
|
||||||
|
templateFile: '',
|
||||||
|
file: null,
|
||||||
|
templateFilePath: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const validateTemplateFile = (rule, value, callback) => {
|
||||||
|
if (!templateForm.file && !templateForm.templateFilePath) {
|
||||||
|
callback(new Error('请上传合同模板文件'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const templateFormRules = {
|
const templateFormRules = {
|
||||||
contractCode: [
|
templateName: [
|
||||||
{ required: true, message: '请输入合同编号', trigger: 'blur' },
|
{ required: true, message: '请输入模板名称', trigger: 'blur' },
|
||||||
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
contractName: [
|
|
||||||
{ required: true, message: '请输入合同名称', trigger: 'blur' },
|
|
||||||
{ min: 2, max: 100, message: '长度在 2 到 100 个字符', trigger: 'blur' }
|
{ min: 2, max: 100, message: '长度在 2 到 100 个字符', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
contractType: [
|
contractType: [
|
||||||
{ required: true, message: '请选择合同类型', trigger: 'change' }
|
{ required: true, message: '请选择合同类型', trigger: 'change' }
|
||||||
|
],
|
||||||
|
publishDate: [
|
||||||
|
{ required: true, message: '请选择发布日期', trigger: 'change' }
|
||||||
|
],
|
||||||
|
templateFile: [
|
||||||
|
{ required: true, validator: validateTemplateFile, trigger: 'change' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,60 +202,58 @@ const getContractTypeName = (type) => {
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const mockData = JSON.parse(localStorage.getItem('mock_contract_templates') || '[]')
|
const res = await listContractTemplates(queryParams)
|
||||||
// 过滤掉贸易合同类型的数据,只保留采购合同和销售合同
|
const pageData = res.data || {}
|
||||||
let filtered = mockData.filter(item => {
|
const list = pageData.list || []
|
||||||
// 移除贸易合同类型的数据
|
total.value = pageData.count || 0
|
||||||
if (item.contractType === 'trade') return false
|
|
||||||
// 根据查询条件过滤
|
|
||||||
if (queryParams.contractType && item.contractType !== queryParams.contractType) return false
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
// 如果有被过滤掉的贸易合同数据,更新localStorage
|
templateList.value = list.map(item => ({
|
||||||
if (filtered.length !== mockData.length) {
|
...item,
|
||||||
localStorage.setItem('mock_contract_templates', JSON.stringify(filtered))
|
publishDate: formatPublishDate(item.publishDate)
|
||||||
}
|
}))
|
||||||
|
|
||||||
const start = (queryParams.pageNum - 1) * queryParams.pageSize
|
|
||||||
const end = start + queryParams.pageSize
|
|
||||||
templateList.value = filtered.slice(start, end)
|
|
||||||
total.value = filtered.length
|
|
||||||
|
|
||||||
if (filtered.length === 0) {
|
|
||||||
generateMockData()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('获取合同模板列表失败:' + error.message)
|
ElMessage.error('获取合同模板列表失败:' + (error.message || '未知错误'))
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateMockData = () => {
|
// 将后端返回的时间(例如 2026-01-13T00:00:00+08:00)
|
||||||
setTimeout(() => {
|
// 格式化为 YYYY-MM-DD,效果等价于 moment(value).format('YYYY-MM-DD')
|
||||||
const contractTypes = ['purchase', 'sales']
|
const formatPublishDate = (value) => {
|
||||||
const contractTypeNames = {
|
if (!value) return ''
|
||||||
'purchase': '采购合同',
|
// 如果本身已经是 YYYY-MM-DD 形式,直接返回
|
||||||
'sales': '销售合同'
|
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
const newTemplates = []
|
// 优先截取前 10 位(2026-01-13)
|
||||||
const now = Date.now()
|
if (value.length >= 10) {
|
||||||
for (let i = 1; i <= 10; i++) {
|
return value.substring(0, 10)
|
||||||
const contractType = contractTypes[Math.floor(Math.random() * contractTypes.length)]
|
|
||||||
newTemplates.push({
|
|
||||||
id: i,
|
|
||||||
contractCode: 'HT' + String(i).padStart(4, '0'),
|
|
||||||
contractName: contractTypeNames[contractType] + '模板_' + i,
|
|
||||||
contractType: contractType,
|
|
||||||
templateFile: i <= 3 ? { name: `合同模板_${i}.docx`, url: '#', type: 'file' } : null,
|
|
||||||
createTime: new Date(now - i * 30 * 86400000).toISOString(),
|
|
||||||
updateTime: new Date(now - i * 30 * 86400000).toISOString()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
localStorage.setItem('mock_contract_templates', JSON.stringify(newTemplates))
|
// 兜底:尝试用 Date 解析再转成 YYYY-MM-DD
|
||||||
getList()
|
const d = new Date(value)
|
||||||
}, 10)
|
if (!isNaN(d.getTime())) {
|
||||||
|
const y = d.getFullYear()
|
||||||
|
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
|
return `${y}-${m}-${day}`
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFileName = (path) => {
|
||||||
|
if (!path) return ''
|
||||||
|
const parts = path.split('/')
|
||||||
|
return parts[parts.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFileUrl = (path) => {
|
||||||
|
if (!path) return ''
|
||||||
|
if (/^https?:\/\//.test(path)) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
const base = import.meta.env.VITE_APP_BASE_API || ''
|
||||||
|
return `${base.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleQuery = () => {
|
const handleQuery = () => {
|
||||||
|
|
@ -238,29 +262,14 @@ const handleQuery = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetQuery = () => {
|
const resetQuery = () => {
|
||||||
|
queryParams.templateName = ''
|
||||||
queryParams.contractType = ''
|
queryParams.contractType = ''
|
||||||
handleQuery()
|
handleQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateContractCode = () => {
|
|
||||||
const mockData = JSON.parse(localStorage.getItem('mock_contract_templates') || '[]')
|
|
||||||
let maxCode = 0
|
|
||||||
mockData.forEach(item => {
|
|
||||||
const match = item.contractCode.match(/^HT(\d+)$/)
|
|
||||||
if (match) {
|
|
||||||
const num = parseInt(match[1])
|
|
||||||
if (num > maxCode) {
|
|
||||||
maxCode = num
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return 'HT' + String(maxCode + 1).padStart(4, '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
isEdit.value = false
|
isEdit.value = false
|
||||||
resetForm()
|
resetForm()
|
||||||
templateForm.contractCode = generateContractCode()
|
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,17 +277,19 @@ const handleEdit = (row) => {
|
||||||
isEdit.value = true
|
isEdit.value = true
|
||||||
Object.assign(templateForm, {
|
Object.assign(templateForm, {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
contractCode: row.contractCode,
|
templateName: row.templateName,
|
||||||
contractName: row.contractName,
|
|
||||||
contractType: row.contractType,
|
contractType: row.contractType,
|
||||||
templateFile: row.templateFile || null
|
publishDate: formatPublishDate(row.publishDate),
|
||||||
|
templateFile: row.templateFile ? '1' : '',
|
||||||
|
file: null,
|
||||||
|
templateFilePath: row.templateFile || ''
|
||||||
})
|
})
|
||||||
|
|
||||||
// 设置文件列表
|
// 设置文件列表
|
||||||
if (row.templateFile) {
|
if (row.templateFile) {
|
||||||
formFileList.value = [{
|
formFileList.value = [{
|
||||||
name: row.templateFile.name,
|
name: getFileName(row.templateFile),
|
||||||
url: row.templateFile.url
|
url: getFileUrl(row.templateFile)
|
||||||
}]
|
}]
|
||||||
} else {
|
} else {
|
||||||
formFileList.value = []
|
formFileList.value = []
|
||||||
|
|
@ -288,10 +299,10 @@ const handleEdit = (row) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownload = (row) => {
|
const handleDownload = (row) => {
|
||||||
if (row.templateFile && row.templateFile.url && row.templateFile.url !== '#') {
|
if (row.templateFile) {
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.href = row.templateFile.url
|
link.href = getFileUrl(row.templateFile)
|
||||||
link.download = row.templateFile.name
|
link.download = getFileName(row.templateFile)
|
||||||
link.click()
|
link.click()
|
||||||
ElMessage.success('下载成功')
|
ElMessage.success('下载成功')
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -304,57 +315,54 @@ const handleDelete = (row) => {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(() => {
|
}).then(async () => {
|
||||||
const templates = JSON.parse(localStorage.getItem('mock_contract_templates') || '[]')
|
try {
|
||||||
const filtered = templates.filter(t => t.id != row.id)
|
await deleteContractTemplates([row.id])
|
||||||
localStorage.setItem('mock_contract_templates', JSON.stringify(filtered))
|
|
||||||
ElMessage.success('删除成功')
|
ElMessage.success('删除成功')
|
||||||
getList()
|
getList()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('删除失败:' + (error.message || '未知错误'))
|
||||||
|
}
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFormFileChange = (file) => {
|
const handleFormFileChange = (uploadFile, uploadFiles) => {
|
||||||
|
const rawFile = uploadFile?.raw
|
||||||
|
|
||||||
// 检查文件大小(10MB)
|
// 检查文件大小(10MB)
|
||||||
const maxSize = 10 * 1024 * 1024
|
const maxSize = 10 * 1024 * 1024
|
||||||
if (file.raw && file.raw.size > maxSize) {
|
if (rawFile && rawFile.size > maxSize) {
|
||||||
ElMessage.warning('文件大小不能超过 10MB')
|
ElMessage.warning('文件大小不能超过 10MB')
|
||||||
formUploadRef.value?.handleRemove(file)
|
formUploadRef.value?.clearFiles()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取文件并转换为 base64
|
// 每次选择都只保留当前这一个文件,覆盖之前的
|
||||||
if (file.raw) {
|
formFileList.value = uploadFile ? [uploadFile] : []
|
||||||
const reader = new FileReader()
|
|
||||||
reader.onload = (e) => {
|
|
||||||
const fileType = file.raw.type
|
|
||||||
let type = 'file'
|
|
||||||
if (fileType.includes('pdf')) {
|
|
||||||
type = 'pdf'
|
|
||||||
} else if (fileType.includes('word') || fileType.includes('document')) {
|
|
||||||
type = 'doc'
|
|
||||||
} else if (fileType.includes('sheet') || fileType.includes('excel')) {
|
|
||||||
type = 'xls'
|
|
||||||
}
|
|
||||||
|
|
||||||
templateForm.templateFile = {
|
// 直接保存原始文件,用于表单提交
|
||||||
name: file.name,
|
if (rawFile) {
|
||||||
url: e.target.result,
|
templateForm.file = rawFile
|
||||||
type: type,
|
templateForm.templateFile = '1'
|
||||||
size: file.raw.size
|
// 编辑模式下重新选择文件时,清除旧文件路径的展示
|
||||||
|
if (isEdit.value) {
|
||||||
|
templateForm.templateFilePath = ''
|
||||||
}
|
}
|
||||||
}
|
templateFormRef.value?.validateField('templateFile')
|
||||||
reader.readAsDataURL(file.raw)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFormFileRemove = () => {
|
const handleFormFileRemove = () => {
|
||||||
templateForm.templateFile = null
|
templateForm.file = null
|
||||||
|
templateForm.templateFilePath = ''
|
||||||
|
templateForm.templateFile = ''
|
||||||
formFileList.value = []
|
formFileList.value = []
|
||||||
|
templateFormRef.value?.validateField('templateFile')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePreviewFile = (file) => {
|
const handlePreviewFile = (file) => {
|
||||||
if (file && file.url && file.url !== '#') {
|
if (file) {
|
||||||
window.open(file.url, '_blank')
|
window.open(getFileUrl(file), '_blank')
|
||||||
} else {
|
} else {
|
||||||
ElMessage.warning('文件不存在')
|
ElMessage.warning('文件不存在')
|
||||||
}
|
}
|
||||||
|
|
@ -363,10 +371,12 @@ const handlePreviewFile = (file) => {
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
Object.assign(templateForm, {
|
Object.assign(templateForm, {
|
||||||
id: null,
|
id: null,
|
||||||
contractCode: '',
|
templateName: '',
|
||||||
contractName: '',
|
|
||||||
contractType: '',
|
contractType: '',
|
||||||
templateFile: null
|
publishDate: '',
|
||||||
|
templateFile: '',
|
||||||
|
file: null,
|
||||||
|
templateFilePath: ''
|
||||||
})
|
})
|
||||||
formFileList.value = []
|
formFileList.value = []
|
||||||
templateFormRef.value?.clearValidate()
|
templateFormRef.value?.clearValidate()
|
||||||
|
|
@ -380,65 +390,36 @@ const handleSubmit = async () => {
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
|
|
||||||
submitLoading.value = true
|
submitLoading.value = true
|
||||||
|
;(async () => {
|
||||||
try {
|
try {
|
||||||
const mockData = JSON.parse(localStorage.getItem('mock_contract_templates') || '[]')
|
|
||||||
const now = new Date().toISOString()
|
|
||||||
|
|
||||||
if (isEdit.value) {
|
if (isEdit.value) {
|
||||||
// 编辑
|
// 编辑:一个接口同时更新基本信息和(可选)文件
|
||||||
const index = mockData.findIndex(item => item.id === templateForm.id)
|
await updateContractTemplate({
|
||||||
if (index !== -1) {
|
id: templateForm.id,
|
||||||
// 检查合同编号是否重复(排除当前编辑的项)
|
|
||||||
const codeExists = mockData.find(item =>
|
|
||||||
item.id !== templateForm.id &&
|
|
||||||
item.contractCode === templateForm.contractCode
|
|
||||||
)
|
|
||||||
if (codeExists) {
|
|
||||||
ElMessage.warning('合同编号已存在')
|
|
||||||
submitLoading.value = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mockData[index] = {
|
|
||||||
...mockData[index],
|
|
||||||
contractCode: templateForm.contractCode,
|
|
||||||
contractName: templateForm.contractName,
|
|
||||||
contractType: templateForm.contractType,
|
contractType: templateForm.contractType,
|
||||||
templateFile: templateForm.templateFile,
|
templateName: templateForm.templateName,
|
||||||
updateTime: now
|
publishDate: templateForm.publishDate,
|
||||||
}
|
file: templateForm.file || undefined
|
||||||
}
|
})
|
||||||
} else {
|
} else {
|
||||||
// 新增
|
// 新增:必须上传文件
|
||||||
// 检查合同编号是否重复
|
await createContractTemplate({
|
||||||
const codeExists = mockData.find(item => item.contractCode === templateForm.contractCode)
|
|
||||||
if (codeExists) {
|
|
||||||
ElMessage.warning('合同编号已存在')
|
|
||||||
submitLoading.value = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1
|
|
||||||
mockData.push({
|
|
||||||
id: newId,
|
|
||||||
contractCode: templateForm.contractCode,
|
|
||||||
contractName: templateForm.contractName,
|
|
||||||
contractType: templateForm.contractType,
|
contractType: templateForm.contractType,
|
||||||
templateFile: templateForm.templateFile,
|
templateName: templateForm.templateName,
|
||||||
createTime: now,
|
publishDate: templateForm.publishDate,
|
||||||
updateTime: now
|
file: templateForm.file
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem('mock_contract_templates', JSON.stringify(mockData))
|
|
||||||
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
|
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
getList()
|
getList()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('操作失败:' + error.message)
|
ElMessage.error('操作失败:' + (error.message || '未知错误'))
|
||||||
} finally {
|
} finally {
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
}
|
}
|
||||||
|
})()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue