lion 1 year ago
commit 82feb14ef7

@ -19,9 +19,10 @@
"core-js": "3.6.5",
"docx": "^9.4.1",
"docx-parser": "^0.2.1",
"docx-preview": "^0.1.14",
"docx2html": "^1.3.2",
"docxtemplater": "^3.61.1",
"echarts": "^5.0.0",
"echarts": "^5.6.0",
"element-ui": "2.13.2",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",

@ -0,0 +1,35 @@
import request from "@/utils/request";
// 新增支付表格模版.
export function saveContractForm(data) {
return request({
method: 'post',
url: '/api/admin/contract-template/save',
data
})
}
// 获取支付表格模版列表.
export function getContractFormList(params) {
return request({
method: 'get',
url: '/api/admin/contract-template/index',
params
})
}
// 删除支付表格模版.
export function deleteContractForm(id) {
return request({
method: 'get',
url: `/api/admin/contract-template/destroy?id=${id}`
})
}
// 获取支付表格模版详情.
export function getContractFormDetail(id) {
return request({
method: 'get',
url: `/api/admin/contract-template/show?id=${id}`
})
}

@ -1,17 +1,27 @@
<template>
<div style="padding: 0 20px;">
<lx-header icon="md-apps" style="margin-bottom: 10px; border: 0px; margin-top: 15px" text="新增支付表格">
<lx-header icon="md-apps" style="margin-bottom: 10px; border: 0px; margin-top: 15px" :text="headerTitle">
</lx-header>
<div class="content-wrapper">
<!-- 左侧面板 -->
<div class="left-panel">
<!-- 表格名称输入区 -->
<div class="form-name-container">
<el-input
v-model="formName"
placeholder="请输入表格名称"
clearable
style="width: 100%"
></el-input>
</div>
<!-- 预览区 -->
<div>
<div class="panel-header">
<h5 class="panel-title">预览区</h5>
<div class="action-buttons">
<el-button size="small" @click="handleRefresh"></el-button>
<el-button size="small" @click="showPreviewModal = true">放大</el-button>
</div>
</div>
<div class="preview-container" v-if="formatType === 'html'" v-html="previewContent"></div>
@ -136,20 +146,60 @@
</div>
</div>
</el-drawer>
<!-- 预览模态窗口 -->
<el-dialog
title="预览"
:visible.sync="showPreviewModal"
width="80%"
:fullscreen="true"
:modal-append-to-body="true"
:append-to-body="true"
custom-class="preview-modal"
>
<div class="modal-preview-container" v-if="formatType === 'html'" v-html="previewContent"></div>
<div class="modal-preview-container" v-else>
<iframe
v-if="docxUrl"
:src="docxUrl"
frameborder="0"
style="width: 100%; height: 100%;"
></iframe>
<div v-else class="no-preview">
请上传文档进行预览
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import mammoth from 'mammoth';
import { saveContractForm, getContractFormDetail } from '@/api/businessConfig/businessConfig';
export default {
name: 'AddPayForm',
data() {
return {
formId: null, // ID
isEditMode: false, //
formName: '', //
formatType: 'html',
codeContent: '',
previewContent: '', //
showEditDrawer: false,
showPreviewModal: false, //
currentTemplateIndex: 0,
editForm: {
name: '',
label: '',
type: 'text',
options: '',
},
fieldList: [],
fieldMetadata: {},
docxUrl: null,
templateFile: null,
templates: [
{
name: '资金划拨审批单',
@ -160,14 +210,14 @@ export default {
<table style="width: 100%; border-collapse: collapse; font-family: SimSun, serif;">
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; width: 25%;">项目名称</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; width: 25%;">{{projectName}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; text-align: right;" colspan="2">本次为第<span style="display: inline-block; width: 30px; border-bottom: 1px solid #000; text-align: center;">{{paymentTimes}}</span>次付款</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; width: 25%;">\${projectName}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; text-align: right;" colspan="2">本次为第<span style="display: inline-block; width: 30px; border-bottom: 1px solid #000; text-align: center;">\${paymentTimes}</span>次付款</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">承包商/供货商</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{contractor}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${contractor}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">合同服务时间</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{serviceTime}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${serviceTime}</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; background-color: #f5f5f5; border: 1px solid #000;" colspan="2">付款情形</td>
@ -177,61 +227,64 @@ export default {
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; border: 1px solid #000; width: 10%;">A</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">合同金额</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{contractAmount}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{contractRemark}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${contractAmount}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${contractRemark}</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; border: 1px solid #000;">B</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">审计金额</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{auditAmount}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{auditRemark}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${auditAmount}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${auditRemark}</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; border: 1px solid #000;">C</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">前期累计已付款</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{previousPayment}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{previousPaymentRemark}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${previousPayment}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${previousPaymentRemark}</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; border: 1px solid #000;">D</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">本期拟款</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{currentPayment}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{currentPaymentRemark}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${currentPayment}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${currentPaymentRemark}</td>
</tr>
<tr style="background-color: #f5f5f5;">
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; border: 1px solid #000;">E</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">本期应付款</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{currentDuePayment}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${currentDuePayment}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<label style="margin-right: 10px;"><input type="checkbox"> 预付款</label>
<label style="margin-right: 10px;"><input type="checkbox"> 进度款</label>
<label><input type="checkbox"> 结算款</label>
</div>
<div style="display: flex; justify-content: flex-start;">
<label style="margin-right: 20px;"><input type="checkbox"> 质保金</label>
<label><input type="checkbox"> <span style="text-decoration: underline;">&nbsp;&nbsp;&nbsp;&nbsp;</span>(其他)</label>
<div style="display: none;">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<label style="margin-right: 10px;"><input type="checkbox"> 预付款</label>
<label style="margin-right: 10px;"><input type="checkbox"> 进度款</label>
<label><input type="checkbox"> 结算款</label>
</div>
<div style="display: flex; justify-content: flex-start;">
<label style="margin-right: 20px;"><input type="checkbox"> 质保金</label>
<label><input type="checkbox"> <span style="text-decoration: underline;">&nbsp;&nbsp;&nbsp;&nbsp;</span>(其他)</label>
</div>
</div>
<div>\${paymentType}</div>
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; border: 1px solid #000;">F</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">累计支付</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{totalPaid}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{totalPaidRemark}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${totalPaid}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${totalPaidRemark}</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; border: 1px solid #000;">G</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">累计拟款</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{totalPlanned}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{totalPlannedRemark}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${totalPlanned}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${totalPlannedRemark}</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; text-align: center; border: 1px solid #000;">H</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">质保金</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">{{warranty}}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">\${warranty}</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000;">
<span style="text-decoration: underline;">&nbsp;&nbsp;&nbsp;</span>%质保期<span style="text-decoration: underline;">&nbsp;&nbsp;&nbsp;</span>需审计的以审计价为计费依据
<span style="text-decoration: underline;">\${percent}</span>%质保期<span style="text-decoration: underline;">\${zbYear}</span>需审计的以审计价为计费依据
</td>
</tr>
<tr>
@ -240,15 +293,32 @@ export default {
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; height: 100px; vertical-align: top;">
<div>业务科室</div>
<div style="margin-top: 60px;">经办人</div>
<div>&nbsp;&nbsp;\${department}</div>
<div style="margin-top: 20px;">经办人</div>
<div>&nbsp;&nbsp;\${handler}</div>
</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; vertical-align: top;">
<div>业务科室负责人</div>
<div>&nbsp;&nbsp;\${departmentHead}</div>
</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; vertical-align: top;" colspan="2">
<div>业务科室分管领导</div>
<div>&nbsp;&nbsp;\${departmentLeader}</div>
</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; vertical-align: top;">业务科室负责人</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; vertical-align: top;" colspan="2">业务科室分管领导</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; height: 100px; vertical-align: top;">财务审计科</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; vertical-align: top;">财务审计科分管领导</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; vertical-align: top;" colspan="2">单位负责人</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; height: 100px; vertical-align: top;">
<div>财务审计科</div>
<div>&nbsp;&nbsp;\${financeDepartment}</div>
</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; vertical-align: top;">
<div>财务审计科分管领导</div>
<div>&nbsp;&nbsp;\${financeLeader}</div>
</td>
<td style="padding: 8px 12px; font-size: 16px; line-height: 1.5; border: 1px solid #000; vertical-align: top;" colspan="2">
<div>单位负责人</div>
<div>&nbsp;&nbsp;\${unitLeader}</div>
</td>
</tr>
</table>
</div>
@ -771,26 +841,26 @@ export default {
</div>`
}
],
editForm: {
name: '测试字段',
label: '测试标签',
type: 'text',
options: '',
required: false,
placeholder: ''
},
fieldList: [],
previewContent: '',
fieldMetadata: {},
docxContent: '',
docxUrl: ''
}
},
mounted() {
this.loadTemplate(this.currentTemplateIndex);
this.$nextTick(() => {
this.handleRefresh();
});
computed: {
headerTitle() {
return this.isEditMode ? '编辑支付表格' : '新增支付表格'
}
},
created() {
// URLID
const id = this.$route.query.id
if (id) {
this.formId = id
this.isEditMode = true
this.fetchFormData(id)
} else {
// 使
this.initializeNewForm()
}
},
watch: {
currentTemplateIndex: {
@ -829,7 +899,8 @@ export default {
handleRefresh() {
let processedContent = this.codeContent;
const variableRegex = /\{\{([^}]+)\}\}/g;
// ${xxx}
const variableRegex = /\${([^}]+)}/g;
const matches = this.codeContent.matchAll(variableRegex);
const variables = new Set();
@ -839,7 +910,11 @@ export default {
this.updateFieldMetadata(Array.from(variables));
processedContent = processedContent.replace(/\{\{[^}]+\}\}/g, '');
//
processedContent = processedContent.replace(/\${[^}]+}/g, '');
// display:none 使
processedContent = processedContent.replace(/display:\s*none/g, 'display: block');
this.previewContent = processedContent;
},
@ -938,9 +1013,40 @@ export default {
handleCancel() {
this.$router.back();
},
handleSave() {
this.$message.success('保存成功');
this.$router.push('/business-config/pay-form');
async handleSave() {
if (!this.formName) {
this.$message.error('请输入表格名称')
return
}
try {
const params = {
name: this.formName,
type: this.formatType === 'html' ? 1 : 2,
template: this.formatType === 'html' ? this.codeContent : null,
//
link_type: 1, //
status: 1, //
}
// ID
if (this.isEditMode) {
params.id = this.formId
}
const res = await saveContractForm(params)
if (res.errcode !== undefined) {
this.$message.error(res.errmsg || '保存失败')
return
}
this.$message.success('保存成功')
//
this.$router.push('/businessConfig/payFormConfig')
} catch (error) {
console.error('保存失败:', error)
this.$message.error('保存失败')
}
},
updateFieldMetadata(variables) {
const updatedMetadata = {};
@ -980,8 +1086,8 @@ export default {
console.log('尝试只打开抽屉');
},
extractVariables(text) {
// 使
const regex = /\{\{([^}]+)\}\}/g;
// ${xxx}
const regex = /\${([^}]+)}/g;
const matches = text.matchAll(regex);
const variables = new Set();
@ -998,6 +1104,55 @@ export default {
console.log('提取到的变量:', Array.from(variables));
return Array.from(variables);
},
//
async fetchFormData(id) {
try {
const res = await getContractFormDetail(id)
if (res.errcode !== undefined) {
this.$message.error(res.errmsg || '获取表单数据失败')
return
}
//
const formData = res
this.formName = formData.name || ''
this.formatType = formData.type == 1 ? 'html' : 'docx'
//
if (this.formatType === 'html') {
this.codeContent = formData.template || ''
//
this.extractAndUpdateFields()
} else if (this.formatType === 'docx' && formData.template_url) {
this.docxUrl = formData.template_url
}
//
this.handleRefresh()
} catch (error) {
console.error('获取表单数据失败:', error)
this.$message.error('获取表单数据失败')
}
},
//
initializeNewForm() {
// 使
if (this.templates.length > 0) {
this.codeContent = this.templates[0].content
this.formName = this.templates[0].name
//
this.extractAndUpdateFields()
//
this.handleRefresh()
}
},
//
extractAndUpdateFields() {
const variables = this.extractVariables(this.codeContent)
this.updateFieldMetadata(variables)
}
},
// URL
@ -1016,6 +1171,7 @@ export default {
margin: 0 -20px;
padding: 0 20px;
margin-bottom: 60px;
overflow-y: auto; //
}
.left-panel {
@ -1024,6 +1180,28 @@ export default {
padding-right: 20px;
display: flex;
flex-direction: column;
overflow-y: auto; //
}
.form-name-container {
margin-bottom: 20px;
padding: 10px;
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 4px;
:deep(.el-input__inner) {
height: 40px;
line-height: 40px;
&:hover {
border-color: #c0c4cc;
}
&:focus {
border-color: #409EFF;
}
}
}
.right-panel {
@ -1214,6 +1392,39 @@ export default {
margin-bottom: 20px;
background: white;
}
.preview-modal {
:deep(.el-dialog__body) {
padding: 0;
height: calc(100vh - 54px);
}
}
.modal-preview-container {
height: 100%;
overflow-y: auto;
padding: 20px;
background: white;
:deep(table) {
width: 100%;
border-collapse: collapse;
}
:deep(td) {
border: 1px solid #000;
padding: 8px 12px;
}
}
.no-preview {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
color: #909399;
font-size: 14px;
}
</style>

File diff suppressed because it is too large Load Diff

@ -15,9 +15,8 @@
<span style="padding: 0 6px;word-break: keep-all;">状态</span>
<el-select v-model="searchForm.status" placeholder="所有状态" style="width: 180px">
<el-option label="所有状态" value=""></el-option>
<el-option label="草稿" value="draft"></el-option>
<el-option label="已发布" value="published"></el-option>
<el-option label="已禁用" value="disabled"></el-option>
<el-option label="已启用" value="1"></el-option>
<el-option label="已禁用" value="0"></el-option>
</el-select>
</div>
@ -25,8 +24,8 @@
<span style="padding: 0 6px;word-break: keep-all;">类型</span>
<el-select v-model="searchForm.type" placeholder="所有类型" style="width: 180px">
<el-option label="所有类型" value=""></el-option>
<el-option label="HTML" value="html"></el-option>
<el-option label="DOCX" value="docx"></el-option>
<el-option label="HTML" :value="1"></el-option>
<el-option label="DOCX" :value="2"></el-option>
</el-select>
</div>
@ -47,7 +46,11 @@
<div class="table-container">
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="name" label="表格名称"></el-table-column>
<el-table-column prop="type" label="类型"></el-table-column>
<el-table-column prop="type" label="类型">
<template #default="scope">
{{ getTypeText(scope.row.type) }}
</template>
</el-table-column>
<el-table-column label="状态">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)" effect="plain">
@ -56,19 +59,30 @@
</template>
</el-table-column>
<el-table-column prop="scenes" label="已使用场景"></el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column prop="updateTime" label="更新时间"></el-table-column>
<el-table-column label="操作" width="250">
<el-table-column prop="created_at" label="创建时间"></el-table-column>
<el-table-column prop="updated_at" label="更新时间"></el-table-column>
<el-table-column label="操作" width="280">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)"></el-button>
<el-button size="small" @click="handlePreview(scope.row)"></el-button>
<el-button
size="small"
:type="scope.row.status === 'disabled' ? 'success' : 'danger'"
@click="handleStatusChange(scope.row)"
>
{{ scope.row.status === 'disabled' ? '启用' : '禁用' }}
</el-button>
<div class="operation-buttons">
<el-button size="small" type="primary" plain @click="handleEdit(scope.row)"></el-button>
<el-button size="small" type="info" plain @click="handlePreview(scope.row)"></el-button>
<el-button
size="small"
:type="scope.row.status === 0 ? 'success' : 'warning'"
plain
@click="handleStatusChange(scope.row)"
>
{{ scope.row.status === 0 ? '启用' : '禁用' }}
</el-button>
<el-button
size="small"
type="danger"
plain
@click="handleDelete(scope.row)"
>
删除
</el-button>
</div>
</template>
</el-table-column>
</el-table>
@ -85,10 +99,42 @@
</el-pagination>
</div>
</div>
<!-- 添加预览模态框 -->
<el-dialog
title="模板预览"
:visible.sync="previewModalVisible"
width="80%"
:before-close="closePreviewModal"
top="5vh"
class="template-preview-dialog"
>
<div class="preview-header">
<h3>{{ currentTemplate.name }}</h3>
</div>
<div class="preview-container">
<!-- 预览HTML内容 -->
<div v-if="currentTemplate.type == 1 && currentTemplate.template"
class="html-preview"
v-html="formatHtmlForPreview(currentTemplate.template)"></div>
<!-- 预览DOCX内容 -->
<div v-else-if="currentTemplate.type == 2" class="docx-preview">
<el-empty description="DOCX预览功能正在开发中"></el-empty>
</div>
<!-- 无可预览内容 -->
<div v-else class="empty-preview">
<el-empty description="无预览内容"></el-empty>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { getContractFormList, deleteContractForm, saveContractForm } from '@/api/businessConfig/businessConfig'
export default {
name: 'PayFormConfig',
data() {
@ -98,43 +144,45 @@ export default {
status: '',
type: ''
},
tableData: [
{
name: '项目采购支付表格',
type: 'HTML',
status: 'published',
scenes: '项目采购、设备采购',
createTime: '2024-03-01 10:00',
updateTime: '2024-03-05 15:30'
},
{
name: '差旅报销表格',
type: 'DOCX',
status: 'draft',
scenes: '差旅报销、交通费报销',
createTime: '2024-03-02 14:20',
updateTime: '2024-03-02 14:20'
},
{
name: '会议费用报销表格',
type: 'DOCX',
status: 'disabled',
scenes: '会议费用、培训费用',
createTime: '2024-02-28 09:15',
updateTime: '2024-03-03 11:45'
}
],
total: 30,
currentPage: 1
tableData: [],
total: 0,
currentPage: 1,
pageSize: 10,
previewModalVisible: false,
currentTemplate: {}
}
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
try {
const params = {
page: this.currentPage,
page_size: this.pageSize,
keyword: this.searchForm.name,
status: this.searchForm.status,
type: this.searchForm.type
}
const res = await getContractFormList(params)
if (res.errcode !== undefined) {
this.$message.error(res.errmsg || '获取数据失败')
return
}
this.tableData = res.data || []
this.total = res.total || 0
} catch (error) {
console.error('获取数据失败:', error)
this.$message.error('获取数据失败')
}
},
handleCreate() {
this.$router.push('/businessConfig/AddPayForm')
this.$router.push('/businessConfig/EditPayForm')
},
handleSearch() {
//
console.log('Search with:', this.searchForm)
async handleSearch() {
this.currentPage = 1
await this.fetchData()
},
resetSearch() {
this.searchForm = {
@ -142,46 +190,120 @@ export default {
status: '',
type: ''
}
this.currentPage = 1
this.fetchData()
},
handleEdit(row) {
this.$router.push(`/payment-form-config/edit/${row.id}`)
this.$router.push(`/businessConfig/EditPayForm?id=${row.id}`)
},
handlePreview(row) {
//
this.$message.info(`预览:${row.name}`)
this.currentTemplate = {...row}
this.previewModalVisible = true
},
closePreviewModal() {
this.previewModalVisible = false
this.currentTemplate = {}
},
handleStatusChange(row) {
const action = row.status === 'disabled' ? '启用' : '禁用'
const status = row.status === 0 ? 1 : 0
const action = status === 1 ? '启用' : '禁用'
this.$confirm(`确定要${action}该表格吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//
this.$message.success(`${action}成功`)
}).then(async () => {
try {
const params = {
id: row.id,
status: status,
//
name: row.name,
type: row.type,
link_type: row.link_type
}
const res = await saveContractForm(params)
if (res.errcode !== undefined) {
this.$message.error(res.errmsg || `${action}失败`)
return
}
this.$message.success(`${action}成功`)
this.fetchData() //
} catch (error) {
console.error(`${action}失败:`, error)
this.$message.error(`${action}失败`)
}
}).catch(() => {
this.$message.info('已取消操作')
})
},
handlePageChange(page) {
async handlePageChange(page) {
this.currentPage = page
//
await this.fetchData()
},
getStatusType(status) {
const types = {
published: 'success',
draft: 'info',
disabled: 'danger'
1: 'success', //
0: 'danger' //
}
return types[status] || 'info'
},
getStatusText(status) {
const texts = {
published: '已发布',
draft: '草稿',
disabled: '已禁用'
1: '已启用',
0: '已禁用'
}
return texts[status] || status
return texts[status] || '--'
},
getTypeText(type) {
const types = {
1: 'HTML',
2: 'DOCX'
}
return types[type] || type
},
formatDate(dateStr) {
if (!dateStr) return '--'
return dateStr
},
handleDelete(row) {
this.$confirm('确定要删除该表格吗?删除后不可恢复。', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const res = await deleteContractForm(row.id)
if (res.errcode !== undefined) {
this.$message.error(res.errmsg || '删除失败')
return
}
this.$message.success('删除成功')
this.fetchData() //
} catch (error) {
console.error('删除失败:', error)
this.$message.error('删除失败')
}
}).catch(() => {
this.$message.info('已取消删除')
})
},
// HTML
formatHtmlForPreview(html) {
if (!html) return ''
//
let processedHtml = html.replace(/\${[^}]+}/g, '')
// display:none display:block
processedHtml = processedHtml.replace(/display:\s*none/g, 'display: block')
return processedHtml
},
// DOCXURL
getFullDocUrl(relativeUrl) {
//
const baseUrl = window.location.origin
// URL
return `${baseUrl}${relativeUrl}`
}
}
}
@ -250,8 +372,88 @@ export default {
margin-top: 20px;
}
:deep(.el-button + .el-button) {
margin-left: 8px;
.operation-buttons {
display: flex;
flex-wrap: wrap;
gap: 5px;
.el-button {
margin-left: 0 !important;
margin-right: 0;
}
}
.preview-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #ebeef5;
h3 {
margin: 0;
font-size: 18px;
color: #303133;
}
}
.preview-container {
background-color: #fff;
padding: 20px;
min-height: 500px;
max-height: 70vh;
overflow-y: auto;
border: 1px solid #ebeef5;
border-radius: 4px;
:deep(*) {
max-width: 100%;
}
}
.html-preview {
background-color: white;
padding: 15px;
border: 1px solid #dcdfe6;
border-radius: 4px;
min-height: 500px;
:deep(table) {
border-collapse: collapse;
width: 100%;
max-width: 100%;
}
:deep(td), :deep(th) {
border: 1px solid #000;
padding: 8px;
}
}
.docx-preview {
display: flex;
justify-content: center;
align-items: center;
height: 400px;
}
.empty-preview {
display: flex;
justify-content: center;
align-items: center;
height: 400px;
}
.template-preview-dialog {
:deep(.el-dialog__body) {
padding: 20px;
}
:deep(.el-dialog__header) {
padding: 15px 20px;
border-bottom: 1px solid #f0f0f0;
}
}
</style>

@ -1,222 +1,193 @@
<template>
<div style="padding: 0 20px;">
<lx-header icon="md-apps" style="margin-bottom: 10px; border: 0px; margin-top: 15px" text="支付表格列表">
<lx-header icon="md-apps" style="margin-bottom: 10px; border: 0px; margin-top: 15px" text="付款计划">
<div slot="content"></div>
<slot>
<!-- 搜索区域 -->
<div class="selects">
<div>
<span style="padding: 0 6px;word-break: keep-all;">表格名称</span>
<el-input v-model="searchForm.name" placeholder="请输入表格名称" style="width: 180px" />
<span style="padding: 0 6px;word-break: keep-all;">
付款计划日期
</span>
<DatePicker :value="[select.start,select.end]" placeholder="请选择日期" placement="bottom-start"
style="width: 200px" type="daterange" @on-change="datePick"></DatePicker>
</div>
<div>
<span style="padding: 0 6px;word-break: keep-all;">状态</span>
<el-select v-model="searchForm.status" placeholder="所有状态" style="width: 180px">
<el-option label="所有状态" value=""></el-option>
<el-option label="草稿" value="draft"></el-option>
<el-option label="已发布" value="published"></el-option>
<el-option label="已禁用" value="disabled"></el-option>
</el-select>
<span style="padding: 0 6px;word-break: keep-all;">
关键字
</span>
<Input v-model="select.keyword" placeholder="请输入关键字" style="width: 180px"></Input>
</div>
<div>
<span style="padding: 0 6px;word-break: keep-all;">类型</span>
<el-select v-model="searchForm.type" placeholder="所有类型" style="width: 180px">
<el-option label="所有类型" value=""></el-option>
<el-option label="HTML" value="html"></el-option>
<el-option label="DOCX" value="docx"></el-option>
</el-select>
</div>
<el-button style="margin-left: 10px" @click="resetSearch"></el-button>
<el-button style="margin-left: 10px" type="primary" @click="handleSearch"></el-button>
<el-button style="margin-left: 10px" type="primary" @click="handleCreate"></el-button>
<Button style="margin-left: 10px" type="primary"
@click="select={showDate:'',start:'',end:'',pageIndex:1,keyword:''}">重置
</Button>
<Button style="margin-left: 10px" type="primary" @click="getSignPlan"></Button>
</div>
</slot>
</lx-header>
<!-- 表格区域 -->
<xy-table :list="tableData" :table-item="tableColumns">
<template #operation="{ row }">
<el-button size="small" @click="handleEdit(row)"></el-button>
<el-button size="small" @click="handlePreview(row)"></el-button>
<el-button
size="small"
:type="row.status === 'disabled' ? 'success' : 'danger'"
@click="handleStatusChange(row)"
>
{{ row.status === 'disabled' ? '启用' : '禁用' }}
</el-button>
<xy-table :list="list" :table-item="table" @delete="deleteContractSign"
@editor="(row)=>{$refs['detailContractSign'].planId = row.id;$refs['detailContractSign'].isShow = true}">
<template v-slot:btns v-if="type==0">
</template>
</xy-table>
<!-- 分页 -->
<div style="display: flex;justify-content: flex-end;">
<el-pagination
background
layout="total, sizes, prev, pager, next"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
@current-change="handlePageChange"
@size-change="handleSizeChange"
/>
<Page :total="total" @on-change="pageChange" show-elevator show-sizer @on-page-size-change="pageSizeChange" />
</div>
<detailContractSign ref="detailContractSign" @editorSuccess="getSignPlan"></detailContractSign>
</div>
</template>
<script>
export default {
name: 'PayFormConfig',
data() {
return {
searchForm: {
name: '',
status: '',
type: ''
},
tableData: [
{
name: '项目采购支付表格',
type: 'HTML',
status: 'published',
scenes: '项目采购、设备采购',
createTime: '2024-03-01 10:00',
updateTime: '2024-03-05 15:30'
},
{
name: '差旅报销表格',
type: 'DOCX',
status: 'draft',
scenes: '差旅报销、交通费报销',
createTime: '2024-03-02 14:20',
updateTime: '2024-03-02 14:20'
},
{
name: '会议费用报销表格',
type: 'DOCX',
status: 'disabled',
scenes: '会议费用、培训费用',
createTime: '2024-02-28 09:15',
updateTime: '2024-03-03 11:45'
}
],
tableColumns: [
{
prop: 'name',
label: '表格名称',
width: 170,
align: 'left'
},
{
prop: 'type',
label: '类型',
width: 100
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row) => this.getStatusText(row.status)
},
{
prop: 'scenes',
label: '已使用场景',
minWidth: 180,
align: 'left'
},
{
prop: 'createTime',
label: '创建时间',
width: 160
},
{
prop: 'updateTime',
label: '更新时间',
width: 160
},
{
prop: 'operation',
label: '操作',
width: 250,
slot: true
}
],
total: 30,
currentPage: 1,
pageSize: 10
}
},
methods: {
handleCreate() {
this.$router.push('/payment-form-config/create')
},
handleSearch() {
console.log('Search with:', this.searchForm)
import {
getContractSign,
delContractSign
} from "@/api/contractSign/contractSign"
import {
parseTime
} from "@/utils"
import {
Message
} from "element-ui";
import detailContractSign from "@/views/contract/components/detailContractSign";
export default {
components: {
detailContractSign
},
resetSearch() {
this.searchForm = {
name: '',
status: '',
type: ''
data() {
return {
select: {
start: `${new Date().getFullYear()}-${new Date().getMonth() + 1}-${new Date().getDate()}`,
end: `${new Date().getFullYear()}-${new Date().getMonth() + 2}-${new Date().getDate()}`,
pageIndex: 1,
keyword: '',
is_auth: 1,
},
total: 0,
list: [],
table: [{
prop: 'contract.name',
label: '项目名称',
width: 170,
align: 'left',
fixed: 'left'
},
{
prop: 'money',
label: '计划付款金额(元)',
align: 'right',
width: 170,
formatter: (v1, v2, value) => {
return Number(value).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
}
},
{
prop: 'date',
label: '计划付款日期',
width: 180
},
{
prop: 'content',
label: '内容',
minWidth: 180,
align: 'left'
},
{
prop: 'contract.created_at',
label: '合同签订日期',
width: 180,
formatter: (v1, v2, value) => {
return parseTime(new Date(value), '{y}-{m}-{d}')
}
},
{
prop: 'contract.supply',
label: '受款单位',
width: 140
},
{
prop: 'admin.name',
label: '经办人',
width: 140
},
{
prop: 'department.name',
label: '经办科室',
width: 140
},
{
prop: 'created_at',
label: '创建信息',
width: 160,
formatter: (v1, v2, value) => {
return parseTime(new Date(value), '{y}-{m}-{d}')
}
}
],
}
},
handleEdit(row) {
this.$router.push(`/payment-form-config/edit/${row.id}`)
},
handlePreview(row) {
this.$message.info(`预览:${row.name}`)
},
handleStatusChange(row) {
const action = row.status === 'disabled' ? '启用' : '禁用'
this.$confirm(`确定要${action}该表格吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$message.success(`${action}成功`)
}).catch(() => {
this.$message.info('已取消操作')
})
},
handlePageChange(page) {
this.currentPage = page
methods: {
pageSizeChange(e) {
this.select.pageSize = e;
this.select.pageIndex = 1;
this.getSignPlan();
},
async getSignPlan() {
const res = await getContractSign({
page_size: this.select.pageSize,
page: this.select.pageIndex,
keyword: this.select.keyword,
start_date: this.select.start,
end_date: this.select.end,
is_auth: this.select.is_auth
})
this.total = res.total
this.list = res.data
},
deleteContractSign(row) {
delContractSign({
id: row.id
}).then(res => {
this.getSignPlan()
Message({
type: 'success',
message: "操作成功"
})
})
},
datePick(e) {
this.select.start = e[0]
this.select.end = e[1]
},
pageChange(e) {
this.select.pageIndex = e
this.getSignPlan()
},
},
handleSizeChange(size) {
this.pageSize = size
this.currentPage = 1
mounted() {
this.getSignPlan()
},
getStatusText(status) {
const texts = {
published: '已发布',
draft: '草稿',
disabled: '已禁用'
}
return texts[status] || status
created() {
let type = parseInt(this.$route.path.split("_")[1]);
this.type = this.select.is_auth = type;
}
}
}
</script>
<style lang="scss" scoped>
.selects {
display: flex;
flex-wrap: wrap;
align-items: center;
& > div {
margin-bottom: 6px;
.selects {
display: flex;
align-items: center;
}
}
:deep(.el-button + .el-button) {
margin-left: 8px;
}
flex-wrap: wrap;
:deep(.el-table) {
margin-top: 20px;
}
&>div {
margin-bottom: 6px;
}
}
</style>

Loading…
Cancel
Save