1、审计内容里的操作功能优化

2、审计内容9增加一个表的内容生成
3、添加取证单弹窗
This commit is contained in:
2026-01-13 12:19:08 +08:00
parent d574975c97
commit d8b4cba12d
15 changed files with 2365 additions and 1381 deletions

View File

@@ -0,0 +1,543 @@
<template>
<a-modal
:visible="visible"
title="审计取证单预览"
:width="1100"
:footer="null"
@cancel="handleCancel"
destroy-on-close
>
<div class="evidence-actions">
<a-space>
<a-button @click="resetFields">重置内容</a-button>
<a-button type="primary" @click="printEvidence">打印/导出</a-button>
</a-space>
<div class="action-tip">可直接在表格中编辑打印即可生成与效果图一致的取证单</div>
</div>
<div class="evidence-container">
<div class="evidence-sheet" ref="printArea">
<div class="sheet-title">审计取证单</div>
<div class="sheet-meta">
<div class="meta-left">
<span>案引号</span>
<input
v-model="form.caseIndex"
class="inline-input long"
placeholder="填写案件编号"
/>
</div>
<div class="meta-right">
<span></span>
<input v-model="form.pageIndex" class="inline-input small" />
<span></span>
<input v-model="form.pageTotal" class="inline-input small" />
<span></span>
</div>
</div>
<table class="sheet-table">
<colgroup>
<col style="width: 18%" />
<col style="width: 32%" />
<col style="width: 18%" />
<col style="width: 32%" />
</colgroup>
<tbody>
<tr>
<th>项目名称</th>
<td colspan="3">
<textarea
v-model="form.projectName"
class="cell-input single"
placeholder="填写项目名称"
></textarea>
</td>
</tr>
<tr>
<th>被审计调查单位或个人</th>
<td colspan="3">
<textarea
v-model="form.auditedTarget"
class="cell-input single"
placeholder="填写被审计(调查)单位或个人"
></textarea>
</td>
</tr>
<tr>
<th>审计调查事项</th>
<td colspan="3">
<textarea
v-model="form.auditMatter"
class="cell-input single"
placeholder="填写审计(调查)事项"
></textarea>
</td>
</tr>
<tr>
<th>摘要提示</th>
<td colspan="3" class="summary-cell">
<div class="summary-title">
审计调查事项摘要包括标题审计记录审计发现定性依据
</div>
<div class="summary-hint">
1. 标题突出核心问题采用观点性语句可作为审计内容审计目标的结论性描述
</div>
<div class="summary-hint">
2. 审计记录客观记录审计核查的具体事实时间地点主体行为数据等避免主观评价
</div>
<div class="summary-hint">
3. 审计发现基于审计记录提出具体问题性质先事实后定性避免夸大
</div>
</td>
</tr>
<tr>
<th>标题</th>
<td colspan="3">
<textarea
v-model="form.summaryTitle"
class="cell-input"
placeholder="填写核心标题或要点"
></textarea>
</td>
</tr>
<tr>
<th>审计记录</th>
<td colspan="3">
<textarea
v-model="form.auditRecord"
class="cell-input tall"
placeholder="客观记录审计核查的过程与事实"
></textarea>
</td>
</tr>
<tr>
<th>审计发现</th>
<td colspan="3">
<textarea
v-model="form.auditFinding"
class="cell-input tall"
placeholder="写明审计发现的事实、性质及影响"
></textarea>
</td>
</tr>
<tr>
<th>定性依据</th>
<td colspan="3">
<textarea
v-model="form.evidenceBasis"
class="cell-input"
placeholder="引用法规、制度或合同条款作为依据"
></textarea>
</td>
</tr>
<tr>
<th>处理</th>
<td colspan="3">
<textarea
v-model="form.handling"
class="cell-input"
placeholder="拟采取的处理措施"
></textarea>
</td>
</tr>
<tr>
<th>建议</th>
<td colspan="3">
<textarea
v-model="form.suggestion"
class="cell-input"
placeholder="提出改进或整改建议"
></textarea>
</td>
</tr>
<tr>
<th>附件</th>
<td colspan="3">
<textarea
v-model="form.attachment"
class="cell-input"
placeholder="列示随附的证明材料"
></textarea>
</td>
</tr>
<tr>
<th>审计人员</th>
<td colspan="2">
<input
v-model="form.auditors"
class="cell-input input-line"
placeholder="填写审计人员"
/>
</td>
<th>编制日期</th>
<td>
<input
v-model="form.compileDate"
class="cell-input input-line"
placeholder="YYYY-MM-DD"
/>
</td>
</tr>
<tr>
<th>证据提供单位或个人意见</th>
<td colspan="2">
<textarea
v-model="form.providerOpinion"
class="cell-input tall"
placeholder="盖章并填写确认意见"
></textarea>
</td>
<td class="date-cell">
<div class="date-label">日期</div>
<input
v-model="form.providerDate"
class="cell-input input-line"
placeholder="YYYY-MM-DD"
/>
</td>
</tr>
</tbody>
</table>
<div class="footer-note">
<div class="note-row">
附件
<input
v-model="form.attachmentPages"
class="inline-input small"
placeholder="页数"
/>
</div>
<div class="note">
说明1. 请你单位在
<input
v-model="form.feedbackDeadline"
class="inline-input long"
placeholder="填写反馈期限"
/>
前反馈意见逾期未反馈视为无异议
</div>
<div class="note">
2. 证据提供单位意见栏如填写不下可另附说明
</div>
</div>
</div>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { PropType, reactive, ref, watch } from 'vue';
import { message } from 'ant-design-vue';
type BaseInfo = {
caseIndex?: string;
projectName?: string;
auditedTarget?: string;
auditMatter?: string;
};
const props = defineProps({
visible: {
type: Boolean,
default: false
},
baseInfo: {
type: Object as PropType<BaseInfo>,
default: () => ({})
},
selectedRows: {
type: Array as PropType<any[]>,
default: () => []
}
});
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
}>();
const printArea = ref<HTMLElement | null>(null);
const defaultForm = () => ({
caseIndex: '',
pageIndex: '1',
pageTotal: '1',
projectName: '',
auditedTarget: '',
auditMatter: '',
summaryTitle: '',
auditRecord: '',
auditFinding: '',
evidenceBasis: '',
handling: '',
suggestion: '',
attachment: '',
auditors: '',
compileDate: '',
providerOpinion: '',
providerDate: '',
attachmentPages: '',
feedbackDeadline: ''
});
const form = reactive(defaultForm());
const applyBaseInfo = () => {
Object.assign(form, defaultForm(), props.baseInfo || {});
if (props.selectedRows && props.selectedRows.length > 0) {
const firstRow: any = props.selectedRows[0] || {};
form.summaryTitle =
firstRow.title || firstRow.name || form.summaryTitle || '';
form.auditRecord =
firstRow.auditRecord ||
firstRow.auditContent ||
firstRow.content ||
form.auditRecord ||
'';
form.auditFinding =
firstRow.auditFinding || firstRow.testResult || form.auditFinding || '';
}
if (!form.compileDate) {
const now = new Date();
form.compileDate = `${now.getFullYear()}-${String(
now.getMonth() + 1
).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
}
};
watch(
() => props.visible,
(visible) => {
if (visible) {
applyBaseInfo();
}
}
);
watch(
() => props.baseInfo,
() => {
if (props.visible) {
applyBaseInfo();
}
},
{ deep: true }
);
const handleCancel = () => {
emit('update:visible', false);
};
const resetFields = () => {
applyBaseInfo();
message.success('内容已重置');
};
const printEvidence = () => {
const area = printArea.value;
if (!area) {
message.warning('暂无可导出的内容');
return;
}
const newWindow = window.open('', '_blank');
if (!newWindow) {
message.error('浏览器阻止了弹窗,请允许弹窗后重试');
return;
}
const style = `
* { box-sizing: border-box; }
body { margin: 0; padding: 24px; font-family: 'Songti SC', 'SimSun', serif; }
.sheet-title { text-align: center; font-size: 28px; letter-spacing: 4px; margin-bottom: 12px; }
.sheet-meta { display: flex; justify-content: space-between; font-size: 14px; margin-bottom: 8px; }
.inline-input { border: none; border-bottom: 1px solid #000; min-width: 60px; padding: 2px 4px; font-size: 14px; }
.inline-input.small { width: 48px; text-align: center; }
.inline-input.long { min-width: 180px; }
.sheet-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.sheet-table th, .sheet-table td { border: 1px solid #000; padding: 8px 10px; vertical-align: top; font-size: 14px; }
.sheet-table th { background: #fafafa; font-weight: 600; text-align: center; }
.cell-input { width: 100%; border: none; resize: vertical; min-height: 34px; font-size: 14px; line-height: 1.5; font-family: inherit; }
.cell-input:focus { outline: none; }
.cell-input.single { min-height: 32px; }
.cell-input.tall { min-height: 120px; }
.input-line { height: 32px; }
.summary-title { font-weight: 700; margin-bottom: 4px; }
.summary-hint { color: #555; line-height: 1.5; margin-bottom: 2px; }
.summary-cell { background: #fcfcfc; }
.date-cell { display: flex; flex-direction: column; gap: 6px; }
.date-label { font-weight: 600; }
.footer-note { margin-top: 12px; font-size: 14px; line-height: 1.6; }
.note-row { margin-bottom: 4px; }
.note input { vertical-align: middle; }
`;
newWindow.document.write(`
<html>
<head>
<title>审计取证单</title>
<style>${style}</style>
</head>
<body>${area.innerHTML}</body>
</html>
`);
newWindow.document.close();
newWindow.focus();
newWindow.print();
};
</script>
<style scoped>
.evidence-actions {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.action-tip {
color: #666;
font-size: 13px;
}
.evidence-container {
max-height: 70vh;
overflow: auto;
background: #f5f5f5;
padding: 12px;
}
.evidence-sheet {
background: #fff;
padding: 16px 20px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.sheet-title {
text-align: center;
font-size: 28px;
letter-spacing: 4px;
margin-bottom: 12px;
font-weight: 600;
}
.sheet-meta {
display: flex;
justify-content: space-between;
font-size: 14px;
margin-bottom: 10px;
}
.inline-input {
border: none;
border-bottom: 1px solid #000;
min-width: 60px;
padding: 2px 4px;
font-size: 14px;
}
.inline-input.small {
width: 48px;
text-align: center;
}
.inline-input.long {
min-width: 180px;
}
.sheet-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.sheet-table th,
.sheet-table td {
border: 1px solid #000;
padding: 8px 10px;
vertical-align: top;
font-size: 14px;
}
.sheet-table th {
background: #fafafa;
font-weight: 600;
text-align: center;
}
.cell-input {
width: 100%;
border: none;
resize: vertical;
min-height: 34px;
font-size: 14px;
line-height: 1.5;
font-family: inherit;
}
.cell-input:focus {
outline: none;
}
.cell-input.single {
min-height: 32px;
}
.cell-input.tall {
min-height: 120px;
}
.input-line {
height: 32px;
}
.summary-cell {
background: #fcfcfc;
}
.summary-title {
font-weight: 700;
margin-bottom: 4px;
}
.summary-hint {
color: #555;
line-height: 1.5;
margin-bottom: 2px;
}
.date-cell {
display: flex;
flex-direction: column;
gap: 6px;
}
.date-label {
font-weight: 600;
}
.footer-note {
margin-top: 12px;
font-size: 14px;
line-height: 1.6;
}
.note-row {
margin-bottom: 4px;
}
</style>

View File

@@ -17,8 +17,11 @@ export function initNavigationItems() {
mode: 'table',
showFileSelect: true, // 所有项都显示文件选择
data: [],
extraData: [],
currentTableIndex: 0, // 当前选中的表格索引
columns: null,
extraTableTitle: config?.extraTableTitle || null,
extraColumns: config?.extraColumns || [],
tableOptions: config?.options || [],
count: index + 1,
tableType: config?.type || `auditContent${index + 1}`,

View File

@@ -68,12 +68,12 @@ export const partyConductColumns = [
width: 200,
// ellipsis: true
},
{
title: '操作',
key: 'action',
align: 'center',
width: 100
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 100
// }
];
export default {

View File

@@ -81,12 +81,12 @@ export const auditHistoryColumns = [
width: 200,
// ellipsis: true
},
{
title: '操作',
key: 'action',
align: 'center',
width: 100
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 100
// }
];
export default {

View File

@@ -429,12 +429,12 @@ export const eightRegColumns = [
width: 200,
ellipsis: true
},
{
title: '操作',
key: 'action',
align: 'center',
width: 140
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 140
// }
];
export default {

View File

@@ -48,12 +48,12 @@ export const strategyAuditColumns = [
align: 'center',
// ellipsis: true
},
{
title: '操作',
key: 'action',
width: 100,
align: 'center'
}
// {
// title: '操作',
// key: 'action',
// width: 100,
// align: 'center'
// }
];
export default {

View File

@@ -126,12 +126,12 @@ export const decisionTableColumns = [
}
]
},
{
title: '操作',
key: 'action',
align: 'center',
width: 140
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 140
// }
];
export default {

View File

@@ -164,12 +164,12 @@ export const budgetExecutionColumns = [
width: 200,
ellipsis: true
},
{
title: '操作',
key: 'action',
align: 'center',
width: 100
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 100
// }
];
export default {

View File

@@ -105,12 +105,12 @@ export const stateAssetsManageColumns = [
width: 200,
ellipsis: true
},
{
title: '操作',
key: 'action',
align: 'center',
width: 100
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 100
// }
];
export default {

View File

@@ -41,12 +41,12 @@ export const investmentSituationColumns = [
width: 200,
// ellipsis: true
},
{
title: '操作',
key: 'action',
align: 'center',
width: 100
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 100
// }
];
// 删除不再需要的单独列配置

View File

@@ -70,12 +70,12 @@ export const internalControlTestColumns = [
width: 200,
// ellipsis: true
},
{
title: '操作',
key: 'action',
align: 'center',
width: 100
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 100
// }
];
export default {

View File

@@ -42,12 +42,12 @@ export const personnelEstablishmentColumns = [
align: 'center',
width: 200,
},
{
title: '操作',
key: 'action',
align: 'center',
width: 100
}
// {
// title: '操作',
// key: 'action',
// align: 'center',
// width: 100
// }
];
export default {

View File

@@ -0,0 +1,60 @@
export const benefitEstablishmentColumns = [
{
title: '序号',
dataIndex: 'index',
key: 'index',
width: 80,
align: 'center'
},
{
title: '凭证号',
dataIndex: 'voucher',
key: 'voucher',
align: 'center'
},
{
title: '支出日期',
dataIndex: 'expenditureDate',
key: 'expenditureDate',
align: 'center'
},
{
title: '用途',
dataIndex: 'usage',
key: 'usage',
align: 'center'
},
{
title: '收款方',
dataIndex: 'payee',
key: 'payee',
align: 'center'
},
{
title: '金额(元)',
dataIndex: 'amount',
key: 'amount',
align: 'center'
},
{
title: '归属部门/人员',
dataIndex: 'belongTo',
key: 'belongTo',
align: 'center'
},
{
title: '款项性质说明',
dataIndex: 'natureDesc',
key: 'natureDesc',
align: 'center'
},
{
title: '违规说明',
dataIndex: 'violationDesc',
key: 'violationDesc',
align: 'center'
}
];
export default benefitEstablishmentColumns;

View File

@@ -1,3 +1,5 @@
import table9ExtraColumns from './table9ExtraColumns';
export const tableConfigs = {
// 审计内容1表格配置
auditContent1: {
@@ -5,9 +7,22 @@ export const tableConfigs = {
title: '贯彻决策部署情况',
options: [
// { title: '贯彻决策部署', value: 'default1', columns: () => import('./table1Columns').then(m => m.default1Columns) },
{ title: '领导班子名单', value: 'leaderList', columns: () => import('./table1Columns').then(m => m.leaderListColumns) },
{ title: '决策支出表', value: 'expense', columns: () => import('./table1Columns').then(m => m.expenseColumns) },
{ title: '八项规定', value: 'eightReg', columns: () => import('./table1Columns').then(m => m.eightRegColumns) }
{
title: '领导班子名单',
value: 'leaderList',
columns: () =>
import('./table1Columns').then((m) => m.leaderListColumns)
},
{
title: '决策支出表',
value: 'expense',
columns: () => import('./table1Columns').then((m) => m.expenseColumns)
},
{
title: '八项规定',
value: 'eightReg',
columns: () => import('./table1Columns').then((m) => m.eightRegColumns)
}
],
// 接口映射
interfaceMap: {
@@ -22,7 +37,12 @@ export const tableConfigs = {
type: 'auditContent2',
title: '单位发展战略执行',
options: [
{ title: '单位发展战略执行', value: 'strategyAudit', columns: () => import('./table2Columns').then(m => m.strategyAuditColumns) }
{
title: '单位发展战略执行',
value: 'strategyAudit',
columns: () =>
import('./table2Columns').then((m) => m.strategyAuditColumns)
}
],
interfaceMap: {
strategyAudit: '/api/ai/auditContent2/generateStrategyAuditTable'
@@ -33,8 +53,17 @@ export const tableConfigs = {
type: 'auditContent3',
title: '重大经济决策调查',
options: [
{ title: '重大经济决策调查表', value: 'decisionTable', columns: () => import('./table3Columns').then(m => m.decisionTableColumns) },
{ title: '三重一大', value: 'tripleOne', columns: () => import('./table3Columns').then(m => m.tripleOneColumns) }
{
title: '重大经济决策调查表',
value: 'decisionTable',
columns: () =>
import('./table3Columns').then((m) => m.decisionTableColumns)
},
{
title: '三重一大',
value: 'tripleOne',
columns: () => import('./table3Columns').then((m) => m.tripleOneColumns)
}
],
interfaceMap: {
tripleOne: '/api/ai/auditContent3/generateTripleOneTable',
@@ -46,7 +75,11 @@ export const tableConfigs = {
type: 'auditContent4',
title: '目标完成情况',
options: [
{ title: '目标责任完成情况表', value: 'target', columns: () => import('./table4Columns').then(m => m.columns0) }
{
title: '目标责任完成情况表',
value: 'target',
columns: () => import('./table4Columns').then((m) => m.columns0)
}
],
interfaceMap: {
target: '/api/ai/auditContent4/generateTargetTable'
@@ -57,8 +90,18 @@ export const tableConfigs = {
type: 'auditContent5',
title: '财务管理情况',
options: [
{ title: '预算管理审计', value: 'budgetManage', columns: () => import('./table5Columns').then(m => m.budgetManageColumns) },
{ title: '预算执行情况审计', value: 'budgetExecution', columns: () => import('./table5Columns').then(m => m.budgetExecutionColumns) }
{
title: '预算管理审计',
value: 'budgetManage',
columns: () =>
import('./table5Columns').then((m) => m.budgetManageColumns)
},
{
title: '预算执行情况审计',
value: 'budgetExecution',
columns: () =>
import('./table5Columns').then((m) => m.budgetExecutionColumns)
}
],
interfaceMap: {
budgetManage: '/api/ai/auditContent5/generateBudgetManageTable',
@@ -70,7 +113,12 @@ export const tableConfigs = {
type: 'auditContent6',
title: '国资管理情况',
options: [
{ title: '国有资产管理审计', value: 'assets', columns: () => import('./table6Columns').then(m => m.stateAssetsManageColumns) }
{
title: '国有资产管理审计',
value: 'assets',
columns: () =>
import('./table6Columns').then((m) => m.stateAssetsManageColumns)
}
],
interfaceMap: {
assets: '/api/ai/auditContent6/generateAssetsTable'
@@ -80,17 +128,28 @@ export const tableConfigs = {
type: 'auditContent7',
title: '重大投资情况',
options: [
{ title: '重大投资情况审计', value: 'investmentSituation', columns: () => import('./table7Columns').then(m => m.investmentSituationColumns) },
{
title: '重大投资情况审计',
value: 'investmentSituation',
columns: () =>
import('./table7Columns').then((m) => m.investmentSituationColumns)
}
],
interfaceMap: {
investmentSituation: '/api/ai/auditContent7/generateInvestmentSituationTable'
investmentSituation:
'/api/ai/auditContent7/generateInvestmentSituationTable'
}
},
auditContent8: {
type: 'auditContent8',
title: '治理结构与内部控制',
options: [
{ title: '内部控制测试', value: 'internalControl', columns: () => import('./table8Columns').then(m => m.internalControlTestColumns) }
{
title: '内部控制测试',
value: 'internalControl',
columns: () =>
import('./table8Columns').then((m) => m.internalControlTestColumns)
}
],
interfaceMap: {
internalControl: '/api/ai/auditContent8/generateInternalControlTable'
@@ -100,8 +159,15 @@ export const tableConfigs = {
type: 'auditContent9',
title: '人员编制管理',
options: [
{ title: '人员编制管理审计', value: 'personnel', columns: () => import('./table9Columns').then(m => m.personnelEstablishmentColumns) }
{
title: '人员编制管理审计',
value: 'personnel',
columns: () =>
import('./table9Columns').then((m) => m.personnelEstablishmentColumns)
}
],
extraTableTitle: '福利费超范围支出明细清单',
extraColumns: table9ExtraColumns,
interfaceMap: {
personnel: '/api/ai/auditContent9/generatePersonnelTable'
}
@@ -110,7 +176,12 @@ export const tableConfigs = {
type: 'auditContent10',
title: '廉政情况',
options: [
{ title: '党风廉政建设责任制审计', value: 'partyConduct', columns: () => import('./table10Columns').then(m => m.partyConductColumns) }
{
title: '党风廉政建设责任制审计',
value: 'partyConduct',
columns: () =>
import('./table10Columns').then((m) => m.partyConductColumns)
}
],
interfaceMap: {
partyConduct: '/api/ai/auditContent10/generatePartyConductTable'
@@ -120,7 +191,12 @@ export const tableConfigs = {
type: 'auditContent11',
title: '历史审计问题整改',
options: [
{ title: '历史审计问题整改', value: 'history', columns: () => import('./table11Columns').then(m => m.auditHistoryColumns) }
{
title: '历史审计问题整改',
value: 'history',
columns: () =>
import('./table11Columns').then((m) => m.auditHistoryColumns)
}
],
interfaceMap: {
history: '/api/ai/auditContent11/generateHistoryTable'
@@ -145,7 +221,9 @@ export function getAllTableConfigs() {
// 通过接口名称查找表格配置
export function findTableConfigByInterface(interfaceName: string) {
for (const [key, config] of Object.entries(tableConfigs)) {
for (const [tableValue, tableInterface] of Object.entries(config.interfaceMap || {})) {
for (const [tableValue, tableInterface] of Object.entries(
config.interfaceMap || {}
)) {
if (tableInterface === interfaceName) {
return {
sectionType: key,
@@ -162,7 +240,7 @@ export function findTableConfigByInterface(interfaceName: string) {
function parseWorkPaperIndex(workPaperIndex) {
if (!workPaperIndex || !Array.isArray(workPaperIndex)) return [];
return workPaperIndex.map(item => {
return workPaperIndex.map((item) => {
if (item && typeof item === 'object') {
return {
fileId: item.fileId || item.file_id || '',
@@ -192,41 +270,48 @@ function parseWorkPaperIndex(workPaperIndex) {
// 通用数据映射函数
export function createDataMapper(type: string) {
const mappers = {
default1: (data) => data.map((item, index) => ({
default1: (data) =>
data.map((item, index) => ({
key: index,
index: index + 1,
...item,
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex)
})),
leaderList: (data) => data.map((item, index) => ({
leaderList: (data) =>
data.map((item, index) => ({
key: index,
...item,
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex)
})),
expense: (data) => data.map((item, index) => ({
expense: (data) =>
data.map((item, index) => ({
key: index,
...item,
finalStatementAmount: formatAmount(item.finalStatementAmount),
initialBudgetAmount: formatAmount(item.initialBudgetAmount),
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex)
})),
eightReg: (data) => data.map((item, index) => ({
eightReg: (data) =>
data.map((item, index) => ({
key: index,
...item,
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex)
})),
strategyAudit: (data) => data.map((item, index) => ({
strategyAudit: (data) =>
data.map((item, index) => ({
key: index,
index: index + 1,
...item,
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex)
})),
tripleOne: (data) => data.map((item, index) => ({
tripleOne: (data) =>
data.map((item, index) => ({
key: index,
...item,
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex)
})),
decisionTable: (data) => data.map((item, index) => ({
decisionTable: (data) =>
data.map((item, index) => ({
key: index,
index: index + 1,
goods: item.executionEffect?.good || item.goods || '否',
@@ -235,7 +320,8 @@ export function createDataMapper(type: string) {
...item,
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex)
})),
budgetManage: (data) => data.map((item, index) => ({
budgetManage: (data) =>
data.map((item, index) => ({
key: index,
index: index + 1,
...item,
@@ -246,14 +332,17 @@ export function createDataMapper(type: string) {
initialBudget: formatAmount(item.indicatorSourceInitialBudget),
additionalBudget: formatAmount(item.indicatorSourceAdditionalBudget),
indicatorUseTotal: formatAmount(item.indicatorUseTotal),
appropriationSubtotal: formatAmount(item.indicatorUseAppropriationSubtotal),
appropriationSubtotal: formatAmount(
item.indicatorUseAppropriationSubtotal
),
appropriation: formatAmount(item.indicatorUseAppropriation),
salaryPayment: formatAmount(item.indicatorUseSalaryPayment),
govProcurement: formatAmount(item.indicatorUseGovProcurement),
indicatorBalance: formatAmount(item.indicatorBalance),
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex),
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex)
})),
investmentSituation: (data) => data.map((item, index) => ({
investmentSituation: (data) =>
data.map((item, index) => ({
key: index,
index: index + 1,
category: item.category || '未分类',
@@ -264,7 +353,8 @@ export function createDataMapper(type: string) {
...item
})),
// 其他类型的映射...
default: (data) => data.map((item, index) => ({
default: (data) =>
data.map((item, index) => ({
key: index,
index: index + 1,
...item,
@@ -280,7 +370,9 @@ function formatAmount(amount) {
if (!amount) return '0.00';
if (typeof amount === 'string' && amount.includes(',')) return amount;
const num = parseFloat(amount);
return isNaN(num) ? '0.00' : num.toLocaleString('zh-CN', {
return isNaN(num)
? '0.00'
: num.toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
@@ -289,30 +381,66 @@ function formatAmount(amount) {
// 生成接口映射
export const apiMethodMap = {
// 审计内容1
default1: { generate: 'generateDefault1Table', export: 'exportDefault1Table' },
leaderList: { generate: 'generateLeaderListTable', export: 'exportLeaderListTable' },
default1: {
generate: 'generateDefault1Table',
export: 'exportDefault1Table'
},
leaderList: {
generate: 'generateLeaderListTable',
export: 'exportLeaderListTable'
},
expense: { generate: 'generateExpenseTable', export: 'exportExpenseTable' },
eightReg: { generate: 'generateEightRegTable', export: 'exportEightRegTable' },
eightReg: {
generate: 'generateEightRegTable',
export: 'exportEightRegTable'
},
// 审计内容2
strategyAudit: { generate: 'generateStrategyAuditTable', export: 'exportStrategyAuditTable' },
strategyAudit: {
generate: 'generateStrategyAuditTable',
export: 'exportStrategyAuditTable'
},
// 审计内容3
tripleOne: { generate: 'generateTripleOneTable', export: 'exportTripleOneTable' },
decisionTable: { generate: 'generateDecisionTable', export: 'exportDecisionTable' },
tripleOne: {
generate: 'generateTripleOneTable',
export: 'exportTripleOneTable'
},
decisionTable: {
generate: 'generateDecisionTable',
export: 'exportDecisionTable'
},
// 审计内容4
target: { generate: 'generateTargetTable', export: 'exportTargetTable' },
// 审计内容5
budgetManage: { generate: 'generateBudgetManageTable', export: 'exportBudgetManageTable' },
budgetExecution: { generate: 'generateBudgetExecutionTable', export: 'exportBudgetExecutionTable' },
budgetManage: {
generate: 'generateBudgetManageTable',
export: 'exportBudgetManageTable'
},
budgetExecution: {
generate: 'generateBudgetExecutionTable',
export: 'exportBudgetExecutionTable'
},
// 审计内容6
assets: { generate: 'generateAssetsTable', export: 'exportAssetsTable' },
// 审计内容7
investmentSituation: { generate: 'generateInvestmentSituationTable', export: 'exportInvestmentSituationTable' },
investmentSituation: {
generate: 'generateInvestmentSituationTable',
export: 'exportInvestmentSituationTable'
},
// 审计内容8
internalControl: { generate: 'generateInternalControlTable', export: 'exportInternalControlTable' },
internalControl: {
generate: 'generateInternalControlTable',
export: 'exportInternalControlTable'
},
// 审计内容9
personnel: { generate: 'generatePersonnelTable', export: 'exportPersonnelTable' },
personnel: {
generate: 'generatePersonnelTable',
export: 'exportPersonnelTable'
},
// 审计内容10
partyConduct: { generate: 'generatePartyConductTable', export: 'exportPartyConductTable' },
partyConduct: {
generate: 'generatePartyConductTable',
export: 'exportPartyConductTable'
},
// 审计内容11
history: { generate: 'generateHistoryTable', export: 'exportHistoryTable' },
// 默认

View File

@@ -13,13 +13,22 @@
<!-- 基本信息卡片 -->
<a-card title="基本信息" style="margin-bottom: 20px" :bordered="false">
<a-descriptions>
<a-descriptions-item label="公司名称" :labelStyle="{ width: '90px', color: '#808080' }">
<a-descriptions-item
label="公司名称"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<span @click="copyText(form.name)">{{ form.name }}</span>
</a-descriptions-item>
<a-descriptions-item label="被审计人" :labelStyle="{ width: '90px', color: '#808080' }">
<a-descriptions-item
label="被审计人"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<span>{{ form.nickname || '暂无' }}</span>
</a-descriptions-item>
<a-descriptions-item label="审计时间" :labelStyle="{ width: '90px', color: '#808080' }">
<a-descriptions-item
label="审计时间"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<span>{{ form.expirationTime }}</span>
</a-descriptions-item>
</a-descriptions>
@@ -115,7 +124,9 @@
<div
class="progress-fill"
:style="{
width: `${((currentSection + 1) / navigationItems.length) * 100}%`
width: `${
((currentSection + 1) / navigationItems.length) * 100
}%`
}"
></div>
</div>
@@ -155,11 +166,10 @@
<template #extra>
<a-space>
<!-- 所有表格都显示导出按钮 -->
<template v-if="item.mode === 'table' && item.tableOptions.length > 0">
<a-button
danger
@click="clearCurrentTable(index)"
<template
v-if="item.mode === 'table' && item.tableOptions.length > 0"
>
<a-button danger @click="clearCurrentTable(index)">
清空表格
</a-button>
<a-button
@@ -196,8 +206,16 @@
</template>
<!-- 通用表格切换器 - 所有表格都显示 -->
<div class="flex justify-between items-center mb-4" v-if="item.mode === 'table'">
<div
class="flex justify-between items-center mb-4"
v-if="item.mode === 'table'"
>
<div class="flex justify-start items-center">
<a-space>
<a-button type="primary">编辑</a-button>
<a-button type="primary" danger>删除</a-button>
<a-button>导出</a-button>
<a-button @click="openEvidenceModal(index)">导出取证单</a-button>
<template v-if="item.tableOptions.length > 0">
<div class="flex items-center">
<span class="mr-2">切换表格</span>
@@ -223,14 +241,16 @@
条数据
</span>
</template>
</a-space>
</div>
<!-- 所有表格都显示历史记录 -->
<a @click="openHistory(index)" class="cursor-pointer">历史记录</a>
</div>
<!-- 表格内容 -->
<template v-if="item.mode === 'table' && item.columns">
<a-table
v-if="item.mode === 'table' && item.columns"
:row-selection="getRowSelection(index)"
:columns="item.columns"
:scroll="{ y: 500, x: 1000 }"
:pagination="false"
@@ -239,19 +259,38 @@
>
<template #bodyCell="{ column, record }">
<!-- 操作列 -->
<template v-if="column.key === 'action'">
<a-space>
<a-button type="link" size="small" @click="editRow(record, index)">编辑</a-button>
<a-button type="link" size="small" danger @click="deleteRow(record, index)">删除</a-button>
</a-space>
</template>
<!-- <template v-if="column.key === 'action'">-->
<!-- <a-space>-->
<!-- <a-button-->
<!-- type="link"-->
<!-- size="small"-->
<!-- @click="editRow(record, index)"-->
<!-- >编辑</a-button-->
<!-- >-->
<!-- <a-button-->
<!-- type="link"-->
<!-- size="small"-->
<!-- danger-->
<!-- @click="deleteRow(record, index)"-->
<!-- >删除</a-button-->
<!-- >-->
<!-- </a-space>-->
<!-- </template>-->
<!-- 测试结果列 -->
<template v-if="column.key === 'testResult' || column.key === 'result'">
<span class="text-green-400" v-if="record[column.key] === '通过'">
<template
v-if="column.key === 'testResult' || column.key === 'result'"
>
<span
class="text-green-400"
v-if="record[column.key] === '通过'"
>
通过
</span>
<span class="text-red-400" v-else-if="record[column.key] === '不通过'">
<span
class="text-red-400"
v-else-if="record[column.key] === '不通过'"
>
不通过
</span>
<span class="text-gray-400" v-else>
@@ -261,14 +300,24 @@
<!-- 工作底稿索引列 -->
<template v-if="column.key === 'workPaperIndex'">
<div v-if="record.workPaperIndex && record.workPaperIndex.length > 0">
<div
v-if="
record.workPaperIndex && record.workPaperIndex.length > 0
"
>
<div
@click="openDoc(fileItem)"
v-for="(fileItem, fileIndex) in record.workPaperIndex"
:key="fileIndex"
>
<img
src="@/assets/word.png"
style="width: 20px; height: 20px; vertical-align: middle; margin-right: 4px;"
style="
width: 20px;
height: 20px;
vertical-align: middle;
margin-right: 4px;
"
alt=""
/>
@@ -279,7 +328,7 @@
target="_blank"
@click.stop="handleFileDownload(fileItem, $event)"
class="file-link cursor-pointer text-wrap"
style="color: #1890ff; text-decoration: none;"
style="color: #1890ff; text-decoration: none"
>
{{ fileItem.fileName || '未命名文件' }}
</a>
@@ -304,13 +353,21 @@
style="width: 20px; height: 20px"
alt=""
/>
<span class="cursor-pointer text-wrap">{{ fileItem }}</span>
<span class="cursor-pointer text-wrap">{{
fileItem
}}</span>
</div>
</div>
</template>
<!-- 执行效果相关列 -->
<template v-if="column.key === 'goods' || column.key === 'normal' || column.key === 'bad'">
<template
v-if="
column.key === 'goods' ||
column.key === 'normal' ||
column.key === 'bad'
"
>
<span v-if="record[column.key] === '是'">✅</span>
<span v-else>❌</span>
</template>
@@ -330,7 +387,16 @@
</template>
</template>
</a-table>
<p v-if="item.extraTableTitle" class="font-bold my-2">{{ item.extraTableTitle }}</p>
<a-table
v-if="item.extraColumns && item.extraColumns.length"
:columns="item.extraColumns"
:scroll="{ y: 500, x: 1000 }"
:pagination="false"
bordered
:data-source="item.extraData"
></a-table>
</template>
<!-- 单选模式 -->
<template v-else-if="item.mode === 'radio'">
<div
@@ -423,35 +489,42 @@
:fields="editingFields"
@save="saveEdit"
/>
<EvidenceModal
v-model:visible="evidenceModalVisible"
:base-info="evidenceBaseInfo"
:selected-rows="evidenceSelectedRows"
/>
</a-drawer>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, onMounted, onUnmounted, computed } from 'vue';
import { Form, message, Modal } from 'ant-design-vue';
import { Form, message, Modal, TableProps } from 'ant-design-vue';
import { assignObject } from 'ele-admin-pro';
import { copyText } from '@/utils/common';
import type { PwlProject } from '@/api/pwl/pwlProject/model';
import {
RedoOutlined,
RobotOutlined,
UngroupOutlined,
QuestionCircleOutlined,
DownloadOutlined,
UploadOutlined,
FolderOpenOutlined,
SaveOutlined
} from '@ant-design/icons-vue';
import { generateAuditReport, downloadAuditReport } from '@/api/ai/auditReport';
import { generateAuditReport } from '@/api/ai/auditReport';
import { getPwlProjectLibraryByIds } from '@/api/pwl/pwlProjectLibrary';
import { addAiHistory } from '@/api/ai/aiHistory';
import * as auditContentApi from '@/api/ai/auditContent';
import FileModal from '@/views/pwl/pwlProject/components/components/FileModal.vue';
import HistoryModal from '@/views/pwl/pwlProject/components/components/HistoryModal.vue';
import EvidenceModal from '@/views/pwl/pwlProject/components/components/EvidenceModal.vue';
import EditModal from './EditModal.vue';
import useNavigationItems from './data/navigationItems';
import { tableConfigs, getTableConfig, createDataMapper, apiMethodMap, findTableConfigByInterface } from './data/tableCommon';
import {
tableConfigs,
getTableConfig,
createDataMapper,
apiMethodMap,
findTableConfigByInterface
} from './data/tableCommon';
// 工具函数
import {
@@ -509,25 +582,56 @@ const checkedDirKeys = ref<(string | number)[]>([]);
const lastSelectedDirKeys = ref<(string | number)[]>([]);
const lastSelectedFileKeys = ref<(string | number)[]>([]);
// ========== 取证单相关 ==========
const evidenceModalVisible = ref(false);
const evidenceBaseInfo = reactive({
caseIndex: '',
projectName: '',
auditedTarget: '',
auditMatter: ''
});
const evidenceSelectedRows = ref<any[]>([]);
// ========== 表格选择 ==========
const selectedRowsMap = reactive<Record<number, any[]>>({});
const rowSelections = reactive<Record<number, TableProps['rowSelection']>>(
{}
);
const getRowSelection = (sectionIndex: number): TableProps['rowSelection'] => {
if (!rowSelections[sectionIndex]) {
rowSelections[sectionIndex] = {
onChange: (selectedRowKeys: string[], selectedRows: any[]) => {
selectedRowsMap[sectionIndex] = selectedRows;
}
};
}
return rowSelections[sectionIndex];
};
// ========== 历史记录相关 ==========
const showHistory = ref(false);
const currentInterfaceName = ref('');
// ========== 三重一大数据 ==========
const tripleOneData = ref(null);
const tripleOneData = ref<any>(null);
// ========== 国资管理数据 ==========
const stateAssetsData = ref(null);
const stateAssetsData = ref<any>(null);
// ========== 表格数据存储 ==========
const tableData = reactive({});
// ========== 统一的表格生成数据存储 ==========
const tableGenerationData = reactive<Record<string, {
const tableGenerationData = reactive<
Record<
string,
{
requestData?: any;
responseData?: any;
interfaceName?: string;
timestamp?: number;
}>>({});
}
>
>({});
// ========== 表单数据 ==========
const form = reactive<PwlProject>({
@@ -538,22 +642,24 @@ const { resetFields } = useForm(form);
// ========== 计算属性 ==========
const hasTripleOneData = computed(() => {
const section = navigationItems.value[2];
const section: any = navigationItems.value[2];
return section.data && section.data.length > 0;
});
// ========== 工具函数 ==========
const getTableKey = (sectionIndex: number) => {
const section = navigationItems.value[sectionIndex];
if (!section || !section.tableOptions || section.tableOptions.length === 0) return '';
const section: any = navigationItems.value[sectionIndex];
if (!section || !section.tableOptions || section.tableOptions.length === 0)
return '';
const currentTable = section.tableOptions[section.currentTableIndex];
if (!currentTable) return '';
return `${section.tableType}_${currentTable.value}`;
};
const getTableInfo = (sectionIndex: number) => {
const section = navigationItems.value[sectionIndex];
if (!section || !section.tableOptions || section.tableOptions.length === 0) return null;
const section: any = navigationItems.value[sectionIndex];
if (!section || !section.tableOptions || section.tableOptions.length === 0)
return null;
const currentTable = section.tableOptions[section.currentTableIndex];
if (!currentTable) return null;
@@ -573,8 +679,11 @@ const updateVisible = (value: boolean) => {
};
/* 表格切换处理 */
const handleTableChange = async (sectionIndex: number, tableIndex: number) => {
const section = navigationItems.value[sectionIndex];
const handleTableChange = async (
sectionIndex: number,
tableIndex: number
) => {
const section: any = navigationItems.value[sectionIndex];
if (!section || !section.tableOptions[tableIndex]) return;
section.currentTableIndex = tableIndex;
@@ -609,14 +718,43 @@ const openDocSelect = (sectionIndex: number) => {
checkedDirKeys.value = [...lastSelectedDirKeys.value];
selectedFileKeys.value = [...lastSelectedFileKeys.value];
selectedDocList.value = lastSelectedDirKeys.value.map(key => key.toString());
selectedFileList.value = lastSelectedFileKeys.value.map(key => key.toString());
selectedDocList.value = lastSelectedDirKeys.value.map((key) =>
key.toString()
);
selectedFileList.value = lastSelectedFileKeys.value.map((key) =>
key.toString()
);
fileModal.value.open(tableInfo.tableKey);
};
/* 打开取证单预览弹窗 */
const openEvidenceModal = (sectionIndex: number) => {
const section: any = navigationItems.value[sectionIndex];
const tableInfo = getTableInfo(sectionIndex);
evidenceBaseInfo.caseIndex = (form as any).code || props.data?.code || '';
evidenceBaseInfo.projectName = form.name || props.data?.name || '';
evidenceBaseInfo.auditedTarget =
(form as any).nickname || (props.data as any)?.nickname || '';
const baseMatter = section?.title || section?.name || '';
const tableTitle = tableInfo?.currentTable?.title || '';
evidenceBaseInfo.auditMatter = tableTitle
? `${baseMatter ? `${baseMatter} - ` : ''}${tableTitle}`
: baseMatter;
evidenceSelectedRows.value = selectedRowsMap[sectionIndex] || [];
evidenceModalVisible.value = true;
};
/* 保存表格生成数据 */
const saveTableGenerationData = (tableKey: string, requestData: any, responseData: any, tableValue?: string) => {
const saveTableGenerationData = (
tableKey: string,
requestData: any,
responseData: any,
tableValue?: string
) => {
// 根据tableKey找到对应的表格配置获取正确的接口名称
let correctInterfaceName = tableKey;
@@ -625,11 +763,15 @@ const saveTableGenerationData = (tableKey: string, requestData: any, responseDat
// 查找对应的表格配置
const sectionKeys = Object.keys(tableConfigs);
const sectionIndex = sectionKeys.findIndex(key => key === sectionType);
const sectionIndex = sectionKeys.findIndex((key) => key === sectionType);
if (sectionIndex !== -1) {
const tableConfig = getTableConfig(sectionIndex);
if (tableConfig && tableConfig.interfaceMap && tableConfig.interfaceMap[value || tableValue]) {
if (
tableConfig &&
tableConfig.interfaceMap &&
tableConfig.interfaceMap[value || tableValue]
) {
correctInterfaceName = tableConfig.interfaceMap[value || tableValue];
}
}
@@ -666,20 +808,28 @@ const generateTableContent = async (sectionIndex: number) => {
kbIds: props.data?.kbId || '',
libraryIds: props.data?.libraryIds || '',
projectLibrary: props.data?.projectLibrary || '',
history: section.data?.length > 0 ? JSON.stringify(section.data, null, 2) : '',
history:
section.data?.length > 0 ? JSON.stringify(section.data, null, 2) : '',
suggestion: section.suggestion || '',
docList: checkedDirKeys.value,
fileList: selectedFileKeys.value,
// 重大经济决策调查表需要三重一大数据
...(currentTable.value === 'decisionTable' ? { data: tripleOneData.value } : {}),
...(currentTable.value === 'decisionTable'
? { data: tripleOneData.value }
: {}),
// 预算管理审计表需要国资管理数据
...(currentTable.value === 'budgetManage' ? { data: stateAssetsData.value } : {}),
...(currentTable.value === 'budgetManage'
? { data: stateAssetsData.value }
: {}),
// 预算执行情况审计表需要预算管理审计的数据
...(currentTable.value === 'budgetExecution' ? { data: tableData['auditContent5_budgetManage'] } : {}),
...(currentTable.value === 'budgetExecution'
? { data: tableData['auditContent5_budgetManage'] }
: {})
};
// 获取对应的生成方法
const apiMethod = apiMethodMap[currentTable.value] || apiMethodMap.default;
const apiMethod =
apiMethodMap[currentTable.value] || apiMethodMap.default;
const generateMethod = auditContentApi[apiMethod.generate];
if (!generateMethod) {
@@ -690,7 +840,12 @@ const generateTableContent = async (sectionIndex: number) => {
if (result.code === 0 && result.data?.success) {
// 保存表格生成数据
saveTableGenerationData(tableKey, requestData, result.data, currentTable.value);
saveTableGenerationData(
tableKey,
requestData,
result.data,
currentTable.value
);
// 使用对应的数据映射函数
const dataMapper = createDataMapper(currentTable.value);
@@ -709,7 +864,9 @@ const generateTableContent = async (sectionIndex: number) => {
section.data = mappedData;
section.suggestion = '';
message.success(`成功生成 ${mappedData.length}${currentTable.title}记录`);
message.success(
`成功生成 ${mappedData.length} 条${currentTable.title}记录`
);
} else {
throw new Error(result.data?.error || result.message || '生成失败');
}
@@ -735,7 +892,8 @@ const handleExportTable = async (sectionIndex: number) => {
// 使用正确的公司名称和审计时间
const companyName = form.name || props.data?.name || '未知公司';
const auditTime = form.expirationTime || props.data?.expirationTime || '未知时间';
const auditTime =
form.expirationTime || props.data?.expirationTime || '未知时间';
// 设置导出状态
exportStates[tableKey] = true;
@@ -748,7 +906,8 @@ const handleExportTable = async (sectionIndex: number) => {
};
// 获取对应的导出方法
const apiMethod = apiMethodMap[currentTable.value] || apiMethodMap.default;
const apiMethod =
apiMethodMap[currentTable.value] || apiMethodMap.default;
const exportMethod = auditContentApi[apiMethod.export];
if (!exportMethod) {
@@ -760,7 +919,10 @@ const handleExportTable = async (sectionIndex: number) => {
const url = window.URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `${currentTable.title}_${companyName}.xlsx`);
link.setAttribute(
'download',
`${currentTable.title}_${companyName}.xlsx`
);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
@@ -803,7 +965,7 @@ const handleGenerateAll = async () => {
/* AI生成内容 */
const generateContent = async (sectionIndex: number, childIndex?: number) => {
const section = navigationItems.value[sectionIndex];
const section: any = navigationItems.value[sectionIndex];
if (section.mode === 'table') {
await generateTableContent(sectionIndex);
@@ -813,8 +975,11 @@ const generateContent = async (sectionIndex: number, childIndex?: number) => {
};
/* 默认内容生成方法 */
const generateDefaultContent = async (sectionIndex: number, childIndex?: number) => {
const section = navigationItems.value[sectionIndex];
const generateDefaultContent = async (
sectionIndex: number,
childIndex?: number
) => {
const section: any = navigationItems.value[sectionIndex];
const contextData = await buildExportData();
section.generating = true;
@@ -843,7 +1008,8 @@ const openHistory = (sectionIndex: number) => {
// 通过表格配置获取接口名称
const tableConfig = getTableConfig(sectionIndex);
if (tableConfig && tableConfig.interfaceMap) {
currentInterfaceName.value = tableConfig.interfaceMap[currentTable.value] || '';
currentInterfaceName.value =
tableConfig.interfaceMap[currentTable.value] || '';
}
}
@@ -868,14 +1034,22 @@ const handleHistorySelect = (record: any) => {
}
// 找到对应的章节索引
const sectionIndex = Object.keys(tableConfigs).findIndex(key => key === tableConfig.sectionType);
const sectionIndex = Object.keys(tableConfigs).findIndex(
(key) => key === tableConfig.sectionType
);
if (sectionIndex === -1) {
message.warning('未找到对应的章节');
return;
}
// 应用历史数据
applyHistoryData(sectionIndex, responseData.data, tableConfig.tableValue, record.requestData, record.interfaceName);
applyHistoryData(
sectionIndex,
responseData.data,
tableConfig.tableValue,
record.requestData,
record.interfaceName
);
message.success('已应用选择的历史记录');
} catch (error: any) {
console.error('解析历史记录数据失败:', error);
@@ -884,15 +1058,23 @@ const handleHistorySelect = (record: any) => {
};
/* 应用历史数据 */
const applyHistoryData = async (sectionIndex: number, data: any[], tableValue: string, requestDataStr?: string, interfaceName?: string) => {
const section = navigationItems.value[sectionIndex];
const applyHistoryData = async (
sectionIndex: number,
data: any[],
tableValue: string,
requestDataStr?: string,
interfaceName?: string
) => {
const section: any = navigationItems.value[sectionIndex];
if (!section) return;
const tableConfig = getTableConfig(sectionIndex);
if (!tableConfig) return;
// 找到对应的表格选项索引
const tableOptionIndex = section.tableOptions.findIndex(option => option.value === tableValue);
const tableOptionIndex = section.tableOptions.findIndex(
(option) => option.value === tableValue
);
if (tableOptionIndex === -1) return;
// 解析请求数据
@@ -940,11 +1122,12 @@ const applyHistoryData = async (sectionIndex: number, data: any[], tableValue: s
/* 编辑行 */
const editRow = (record: any, sectionIndex: number) => {
const section = navigationItems.value[sectionIndex];
const section: any = navigationItems.value[sectionIndex];
if (!section || !section.columns) return;
// 获取可编辑的字段(排除操作列)
editingFields.value = section.columns.filter(col =>
editingFields.value = section.columns.filter(
(col) =>
col.key !== 'action' &&
col.dataIndex !== 'key' &&
col.dataIndex !== 'index' &&
@@ -966,10 +1149,12 @@ const editRow = (record: any, sectionIndex: number) => {
/* 保存编辑 */
const saveEdit = (updatedRecord: any) => {
const section = navigationItems.value[editingSectionIndex.value];
const section: any = navigationItems.value[editingSectionIndex.value];
if (!section || !section.data) return;
const index = section.data.findIndex((item: any) => item.key === updatedRecord.key);
const index = section.data.findIndex(
(item: any) => item.key === updatedRecord.key
);
if (index !== -1) {
// 保留原有数据的其他字段
const originalRecord = section.data[index];
@@ -978,10 +1163,15 @@ const saveEdit = (updatedRecord: any) => {
// 更新对应的响应数据
if (editingTableKey.value && tableGenerationData[editingTableKey.value]) {
// 获取当前的响应数据
const responseData = tableGenerationData[editingTableKey.value].responseData;
const responseData =
tableGenerationData[editingTableKey.value].responseData;
// 如果响应数据存在且包含data数组
if (responseData && responseData.data && Array.isArray(responseData.data)) {
if (
responseData &&
responseData.data &&
Array.isArray(responseData.data)
) {
// 找到对应的原始数据索引
const dataIndex = (() => {
const hasValidId =
@@ -990,7 +1180,9 @@ const saveEdit = (updatedRecord: any) => {
updatedRecord.id !== '';
if (hasValidId) {
const byId = responseData.data.findIndex((item: any) => item.id === updatedRecord.id);
const byId = responseData.data.findIndex(
(item: any) => item.id === updatedRecord.id
);
if (byId !== -1) return byId;
}
@@ -1003,10 +1195,12 @@ const saveEdit = (updatedRecord: any) => {
return updatedRecord.key;
}
const hasKey = updatedRecord.key !== undefined && updatedRecord.key !== null;
const hasKey =
updatedRecord.key !== undefined && updatedRecord.key !== null;
if (hasKey) {
const byKey = responseData.data.findIndex(
(item: any) => item.key !== undefined && item.key === updatedRecord.key
(item: any) =>
item.key !== undefined && item.key === updatedRecord.key
);
if (byKey !== -1) return byKey;
}
@@ -1025,15 +1219,19 @@ const saveEdit = (updatedRecord: any) => {
};
// 更新tableGenerationData中的响应数据
tableGenerationData[editingTableKey.value].responseData = responseData;
tableGenerationData[editingTableKey.value].responseData =
responseData;
} else {
// 如果没找到对应的原始数据,直接将编辑后的数据添加到响应数据中
// 这可能发生在手动添加行的情况下
responseData.data.push((() => {
responseData.data.push(
(() => {
const { workPaperIndex, key, index, ...rest } = updatedRecord;
return rest;
})());
tableGenerationData[editingTableKey.value].responseData = responseData;
})()
);
tableGenerationData[editingTableKey.value].responseData =
responseData;
}
}
}
@@ -1047,21 +1245,36 @@ const saveEdit = (updatedRecord: any) => {
/* 删除行 */
const deleteRow = (record: any, sectionIndex: number) => {
const section = navigationItems.value[sectionIndex];
const section: any = navigationItems.value[sectionIndex];
if (!section || !section.data) return;
const uiIndex = section.data.findIndex((item: any) => item.key === record.key);
const uiIndex = section.data.findIndex(
(item: any) => item.key === record.key
);
if (uiIndex === -1) return;
// 同步删除原始 responseData删除前就计算 rawIndex避免 key 重排导致错位)
const tableInfo = getTableInfo(sectionIndex);
if (tableInfo && tableInfo.tableKey && tableGenerationData[tableInfo.tableKey]) {
if (
tableInfo &&
tableInfo.tableKey &&
tableGenerationData[tableInfo.tableKey]
) {
const responseData = tableGenerationData[tableInfo.tableKey].responseData;
if (responseData && responseData.data && Array.isArray(responseData.data)) {
if (
responseData &&
responseData.data &&
Array.isArray(responseData.data)
) {
const rawIndex = (() => {
const hasValidId = record?.id !== undefined && record?.id !== null && record?.id !== '';
const hasValidId =
record?.id !== undefined &&
record?.id !== null &&
record?.id !== '';
if (hasValidId) {
const byId = responseData.data.findIndex((item: any) => item.id === record.id);
const byId = responseData.data.findIndex(
(item: any) => item.id === record.id
);
if (byId !== -1) return byId;
}
@@ -1076,12 +1289,15 @@ const deleteRow = (record: any, sectionIndex: number) => {
const hasKey = record?.key !== undefined && record?.key !== null;
if (hasKey) {
const byKey = responseData.data.findIndex((item: any) => item.key !== undefined && item.key === record.key);
const byKey = responseData.data.findIndex(
(item: any) => item.key !== undefined && item.key === record.key
);
if (byKey !== -1) return byKey;
}
// 最后兜底:使用 UI 索引
if (uiIndex >= 0 && uiIndex < responseData.data.length) return uiIndex;
if (uiIndex >= 0 && uiIndex < responseData.data.length)
return uiIndex;
return -1;
})();
@@ -1139,7 +1355,11 @@ const saveCurrentTable = async (sectionIndex: number) => {
const tableConfig = getTableConfig(sectionIndex);
let correctInterfaceName = generationData.interfaceName; // 默认为当前值
if (tableConfig && tableConfig.interfaceMap && tableConfig.interfaceMap[currentTable.value]) {
if (
tableConfig &&
tableConfig.interfaceMap &&
tableConfig.interfaceMap[currentTable.value]
) {
correctInterfaceName = tableConfig.interfaceMap[currentTable.value];
}
@@ -1152,7 +1372,8 @@ const saveCurrentTable = async (sectionIndex: number) => {
const workPaperIndex = row?.workPaperIndex;
if (!Array.isArray(workPaperIndex)) return row;
const normalized = workPaperIndex.map((item: any) => {
const normalized = workPaperIndex
.map((item: any) => {
if (typeof item === 'string') return item;
if (item && typeof item === 'object') {
const fileId = item.fileId || item.file_id || '';
@@ -1161,7 +1382,8 @@ const saveCurrentTable = async (sectionIndex: number) => {
return `${fileId}||${fileName}||${fileUrl}`;
}
return '';
}).filter((s: string) => s && s.trim() !== '');
})
.filter((s: string) => s && s.trim() !== '');
return {
...row,
@@ -1175,8 +1397,10 @@ const saveCurrentTable = async (sectionIndex: number) => {
const aiHistory = {
projectId: props.data?.id,
interfaceName: correctInterfaceName, // 使用正确的接口名称
requestData: generationData.requestData ? JSON.stringify(generationData.requestData) : '',
responseData: JSON.stringify(normalizedResponseData),
requestData: generationData.requestData
? JSON.stringify(generationData.requestData)
: '',
responseData: JSON.stringify(normalizedResponseData)
// 其他字段后端会自动设置
};
@@ -1192,8 +1416,14 @@ const saveCurrentTable = async (sectionIndex: number) => {
};
const clearCurrentTable = (sectionIndex: number) => {
const section = navigationItems.value[sectionIndex];
if (!section || section.mode !== 'table' || !section.tableOptions || section.tableOptions.length === 0) return;
const section: any = navigationItems.value[sectionIndex];
if (
!section ||
section.mode !== 'table' ||
!section.tableOptions ||
section.tableOptions.length === 0
)
return;
Modal.confirm({
title: '确认清空表格?',
@@ -1213,8 +1443,10 @@ const clearCurrentTable = (sectionIndex: number) => {
tableGenerationData[tableKey].responseData = next;
}
if (exportStates[tableKey] !== undefined) delete exportStates[tableKey];
if (savingStates[tableKey] !== undefined) delete savingStates[tableKey];
if (exportStates[tableKey] !== undefined)
delete exportStates[tableKey];
if (savingStates[tableKey] !== undefined)
delete savingStates[tableKey];
if (option.value === 'tripleOne') {
tripleOneData.value = null;
@@ -1230,12 +1462,24 @@ const clearCurrentTable = (sectionIndex: number) => {
});
};
const openDoc = (fileItem) => {
console.log(fileItem);
// window.open(
// `http://view.officeapps.live.com/op/view.aspx?src=文件地址`,
// '_blank'
// );
};
/* 键盘快捷键处理 */
const handleKeydown = (event: KeyboardEvent) => {
if (!props.visible) return;
// Ctrl/Cmd + 数字键 1-9 快速跳转
if ((event.ctrlKey || event.metaKey) && event.key >= '1' && event.key <= '9') {
if (
(event.ctrlKey || event.metaKey) &&
event.key >= '1' &&
event.key <= '9'
) {
event.preventDefault();
const index = parseInt(event.key) - 1;
if (index < navigationItems.value.length) {
@@ -1290,10 +1534,14 @@ watch(
assignObject(form, props.data);
// 获取知识库并拼接 kbIds
if (props.data.libraryIds?.length > 0) {
if (props.data.libraryIds && props.data.libraryIds?.length > 0) {
try {
const result = await getPwlProjectLibraryByIds(props.data.libraryIds);
const kbIds = result.map((lib) => lib.kbId).filter((kbId) => kbId);
const result = await getPwlProjectLibraryByIds(
props.data.libraryIds
);
const kbIds = result
.map((lib) => lib.kbId)
.filter((kbId) => kbId);
if (form.kbId) {
kbIds.unshift(form.kbId);
}
@@ -1311,9 +1559,10 @@ watch(
// 初始化所有表格的列配置
for (let i = 0; i < navigationItems.value.length; i++) {
const section = navigationItems.value[i];
const section: any = navigationItems.value[i];
if (section.tableOptions.length > 0) {
const tableOption = section.tableOptions[section.currentTableIndex];
const tableOption =
section.tableOptions[section.currentTableIndex];
if (typeof tableOption.columns === 'function') {
try {
const module = await tableOption.columns();
@@ -1335,12 +1584,13 @@ watch(
} else {
resetFields();
combinedKbIds.value = '';
evidenceModalVisible.value = false;
// 清空表格数据
Object.keys(tableData).forEach(key => {
Object.keys(tableData).forEach((key) => {
delete tableData[key];
});
// 清空表格生成数据
Object.keys(tableGenerationData).forEach(key => {
Object.keys(tableGenerationData).forEach((key) => {
delete tableGenerationData[key];
});
// 清空特殊数据