feat(pwl): 新增生成报告功能
- 添加生成报告弹窗组件 - 实现报告内容的 AI 生成逻辑 - 优化用户界面,增加导航和快捷键功能
This commit is contained in:
758
src/views/pwl/pwlProject/components/report.vue
Normal file
758
src/views/pwl/pwlProject/components/report.vue
Normal file
@@ -0,0 +1,758 @@
|
|||||||
|
<!-- 用户编辑弹窗 -->
|
||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
:width="`80%`"
|
||||||
|
:visible="visible"
|
||||||
|
:confirm-loading="loading"
|
||||||
|
:maxable="maxAble"
|
||||||
|
title="AI审计方案生成器"
|
||||||
|
:body-style="{ paddingBottom: '8px', background: '#f3f3f3' }"
|
||||||
|
@update:visible="updateVisible"
|
||||||
|
:maskClosable="false"
|
||||||
|
:footer="null"
|
||||||
|
@ok="save"
|
||||||
|
>
|
||||||
|
<a-card title="基本信息" style="margin-bottom: 20px" :bordered="false">
|
||||||
|
<a-descriptions>
|
||||||
|
<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' }"
|
||||||
|
>
|
||||||
|
<span>{{ form.nickname || '暂无' }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item
|
||||||
|
label="审计时间"
|
||||||
|
:labelStyle="{ width: '90px', color: '#808080' }"
|
||||||
|
>
|
||||||
|
<span>{{ form.expirationTime }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card style="margin-bottom: 20px; text-align: center; background: transparent" :bordered="false">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" size="large">
|
||||||
|
<template #icon>
|
||||||
|
<UngroupOutlined/>
|
||||||
|
</template>
|
||||||
|
生成全部方案
|
||||||
|
</a-button>
|
||||||
|
<a-button size="large">
|
||||||
|
<template #icon>
|
||||||
|
<DownloadOutlined/>
|
||||||
|
</template>
|
||||||
|
保存草稿
|
||||||
|
</a-button>
|
||||||
|
<a-button size="large">
|
||||||
|
<template #icon>
|
||||||
|
<RedoOutlined/>
|
||||||
|
</template>
|
||||||
|
加载草稿
|
||||||
|
</a-button>
|
||||||
|
<a-button size="large">
|
||||||
|
<template #icon>
|
||||||
|
<UploadOutlined/>
|
||||||
|
</template>
|
||||||
|
导出文本
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 快速导航 -->
|
||||||
|
<a-card style="margin-bottom: 20px" :bordered="false">
|
||||||
|
<template #title>
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||||||
|
<span>快速导航</span>
|
||||||
|
<a-tooltip title="快捷键:Ctrl+1~9 快速跳转,Ctrl+↑↓ 上下导航">
|
||||||
|
<QuestionCircleOutlined style="color: #999; cursor: help;"/>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="navigation-container">
|
||||||
|
<div class="nav-grid">
|
||||||
|
<a-button
|
||||||
|
v-for="(item, index) in navigationItems"
|
||||||
|
:key="index"
|
||||||
|
:type="currentSection === index ? 'primary' : 'default'"
|
||||||
|
size="small"
|
||||||
|
@click="scrollToSection(index)"
|
||||||
|
class="nav-button"
|
||||||
|
:class="{ 'active': currentSection === index }"
|
||||||
|
>
|
||||||
|
<span class="nav-number">{{ item.number }}</span>
|
||||||
|
<span class="nav-text">{{ item.name }}</span>
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 进度指示器 -->
|
||||||
|
<div class="progress-container">
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div
|
||||||
|
class="progress-fill"
|
||||||
|
:style="{ width: `${((currentSection + 1) / navigationItems.length) * 100}%` }"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div class="progress-text">
|
||||||
|
{{ currentSection + 1 }} / {{ navigationItems.length }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 审计方案内容 -->
|
||||||
|
<div class="audit-content">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in navigationItems"
|
||||||
|
:key="index"
|
||||||
|
:id="`section-${index}`"
|
||||||
|
class="audit-section"
|
||||||
|
>
|
||||||
|
<a-card
|
||||||
|
:title="`${item.number}、${item.name}`"
|
||||||
|
style="margin-bottom: 20px"
|
||||||
|
:bordered="false"
|
||||||
|
>
|
||||||
|
<template #extra>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
@click="generateContent(index)"
|
||||||
|
:loading="item.generating"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<RobotOutlined/>
|
||||||
|
</template>
|
||||||
|
AI生成
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="section-description">
|
||||||
|
{{ item.description }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="item.content"
|
||||||
|
:rows="item.rows || 8"
|
||||||
|
:placeholder="item.placeholder"
|
||||||
|
class="content-textarea"
|
||||||
|
style="margin-top: 16px"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 返回顶部按钮 -->
|
||||||
|
<a-back-top :target="getScrollContainer"/>
|
||||||
|
</a-drawer>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {ref, reactive, watch, onMounted, onUnmounted} from 'vue';
|
||||||
|
import {Form} from 'ant-design-vue';
|
||||||
|
import {assignObject} from 'ele-admin-pro';
|
||||||
|
import {copyText} from '@/utils/common';
|
||||||
|
import {PwlProject} from "@/api/pwl/pwlProject/model";
|
||||||
|
import {
|
||||||
|
UngroupOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
|
UploadOutlined,
|
||||||
|
RedoOutlined,
|
||||||
|
RobotOutlined,
|
||||||
|
QuestionCircleOutlined
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
// 弹窗是否打开
|
||||||
|
visible: boolean;
|
||||||
|
// 修改回显的数据
|
||||||
|
data?: PwlProject | null;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// 是否显示最大化切换按钮
|
||||||
|
const maxAble = ref(true);
|
||||||
|
// 当前选中的章节
|
||||||
|
const currentSection = ref(0);
|
||||||
|
|
||||||
|
// 九大审计项目导航配置
|
||||||
|
const navigationItems = ref([
|
||||||
|
{
|
||||||
|
number: '一',
|
||||||
|
name: '审计依据',
|
||||||
|
title: '1、审计依据',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入审计依据内容...',
|
||||||
|
content: `(一)中共中央办公厅、国务院办公厅《党政主要领导干部和国有企业领导人员经济责任审计规定》(2019年7月7日起实施);
|
||||||
|
|
||||||
|
(二)XXX有限公司经济责任审计管理办法》(XX(**)**号(待补充));
|
||||||
|
|
||||||
|
(三)国家有关法律、法规及相关文件;
|
||||||
|
|
||||||
|
(四)XXX有限公司下达的年度工作目标;
|
||||||
|
|
||||||
|
(五)XXX有限公司各项管理制度;
|
||||||
|
|
||||||
|
(六)双方签订的审计业务约定书。`,
|
||||||
|
rows: 10,
|
||||||
|
generating: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: '二',
|
||||||
|
name: '审计目标',
|
||||||
|
title: '2、审计目标',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入审计目标内容...',
|
||||||
|
content: `通过对XXX同志、XXX同志任职期间所负责公司资产、负债情况的真实性、合法性和效益性及其相关经济活动进行审计,查清其任职期间公司财经政策指导和财务收支工作目标完成情况,重大决策执行及交接情况,遵守国家财经法规情况及公司资产保值增值情况,分清其经济责任,评价其工作业绩,对其任职期间履行经济责任情况作出客观公正的评价。`,
|
||||||
|
rows: 6,
|
||||||
|
generating: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: '三',
|
||||||
|
name: '审计对象和范围',
|
||||||
|
title: '3、审计对象和范围',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入审计对象和范围内容...',
|
||||||
|
content: '',
|
||||||
|
rows: 8,
|
||||||
|
generating: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: '四',
|
||||||
|
name: '被审计单位基本情况',
|
||||||
|
title: '4、被审计单位基本情况',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入被审计单位基本情况内容...',
|
||||||
|
content: '',
|
||||||
|
rows: 10,
|
||||||
|
generating: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: '五',
|
||||||
|
name: '审计内容和重点及审计方法',
|
||||||
|
title: '5、审计内容和重点及审计方法',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入审计内容和重点及审计方法内容...',
|
||||||
|
content: '',
|
||||||
|
rows: 12,
|
||||||
|
generating: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: '六',
|
||||||
|
name: '重要风险的识别及应对',
|
||||||
|
title: '6、重要风险的识别及应对',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入重要风险的识别及应对内容...',
|
||||||
|
content: '',
|
||||||
|
rows: 10,
|
||||||
|
generating: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: '七',
|
||||||
|
name: '审计技术方法',
|
||||||
|
title: '7、审计技术方法',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入审计技术方法内容...',
|
||||||
|
content: '',
|
||||||
|
rows: 8,
|
||||||
|
generating: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: '八',
|
||||||
|
name: '工作步骤与时间安排',
|
||||||
|
title: '8、工作步骤与时间安排',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入工作步骤与时间安排内容...',
|
||||||
|
content: '',
|
||||||
|
rows: 10,
|
||||||
|
generating: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: '九',
|
||||||
|
name: '审计工作的组织实施',
|
||||||
|
title: '9、审计工作的组织实施',
|
||||||
|
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||||
|
placeholder: '请输入审计工作的组织实施内容...',
|
||||||
|
content: '',
|
||||||
|
rows: 8,
|
||||||
|
generating: false
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'done'): void;
|
||||||
|
(e: 'update:visible', visible: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// 订单信息
|
||||||
|
const form = reactive<PwlProject>({
|
||||||
|
// ID
|
||||||
|
id: undefined,
|
||||||
|
// 项目名称
|
||||||
|
name: undefined,
|
||||||
|
// 项目标识
|
||||||
|
code: undefined,
|
||||||
|
// 上级id, 0是顶级
|
||||||
|
parentId: undefined,
|
||||||
|
// 项目类型
|
||||||
|
type: undefined,
|
||||||
|
// 项目图标
|
||||||
|
image: undefined,
|
||||||
|
// 二维码
|
||||||
|
qrcode: undefined,
|
||||||
|
// 链接地址
|
||||||
|
url: undefined,
|
||||||
|
// 应用截图
|
||||||
|
images: undefined,
|
||||||
|
// 底稿情况
|
||||||
|
files: undefined,
|
||||||
|
// 应用介绍
|
||||||
|
content: undefined,
|
||||||
|
// 年末资产总额(万元)
|
||||||
|
totalAssets: undefined,
|
||||||
|
// 合同金额
|
||||||
|
contractPrice: undefined,
|
||||||
|
// 实收金额
|
||||||
|
payPrice: undefined,
|
||||||
|
// 软件定价
|
||||||
|
price: undefined,
|
||||||
|
// 是否推荐
|
||||||
|
recommend: undefined,
|
||||||
|
// 到期时间
|
||||||
|
expirationTime: undefined,
|
||||||
|
// 项目信息-开票单位/汇款人
|
||||||
|
itemName: undefined,
|
||||||
|
// 项目信息-年度
|
||||||
|
itemYear: undefined,
|
||||||
|
// 项目信息-类型
|
||||||
|
itemType: undefined,
|
||||||
|
// 项目信息-审计意见
|
||||||
|
itemOpinion: undefined,
|
||||||
|
// 到账信息-银行名称
|
||||||
|
bankName: undefined,
|
||||||
|
// 到账日期
|
||||||
|
bankPayTime: undefined,
|
||||||
|
// 到账金额
|
||||||
|
bankPrice: undefined,
|
||||||
|
// 发票类型
|
||||||
|
invoiceType: undefined,
|
||||||
|
// 开票日期
|
||||||
|
invoiceTime: undefined,
|
||||||
|
// 开票金额
|
||||||
|
invoicePrice: undefined,
|
||||||
|
// 报告份数
|
||||||
|
reportNum: undefined,
|
||||||
|
// 底稿人员
|
||||||
|
draftUserId: undefined,
|
||||||
|
// 底稿人员
|
||||||
|
draftUser: undefined,
|
||||||
|
// 参与成员
|
||||||
|
userIds: undefined,
|
||||||
|
users: undefined,
|
||||||
|
// 签字注会
|
||||||
|
signUserId: undefined,
|
||||||
|
signUser: undefined,
|
||||||
|
// 展业人员
|
||||||
|
saleUserId: undefined,
|
||||||
|
saleUser: undefined,
|
||||||
|
// 备注
|
||||||
|
comments: undefined,
|
||||||
|
// 排序(数字越小越靠前)
|
||||||
|
sortNumber: undefined,
|
||||||
|
// 状态, 0正常, 1冻结
|
||||||
|
status: undefined,
|
||||||
|
// 电子报告是否已完成
|
||||||
|
electron: undefined,
|
||||||
|
// 纸质报告是否已完成
|
||||||
|
paper: undefined,
|
||||||
|
// 是否删除, 0否, 1是
|
||||||
|
deleted: undefined,
|
||||||
|
// 创建人ID
|
||||||
|
userId: undefined,
|
||||||
|
// 创建人
|
||||||
|
realName: undefined,
|
||||||
|
// 租户id
|
||||||
|
tenantId: undefined,
|
||||||
|
// 创建时间
|
||||||
|
createTime: undefined,
|
||||||
|
// 修改时间
|
||||||
|
updateTime: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 请求状态
|
||||||
|
const loading = ref(true);
|
||||||
|
|
||||||
|
const {resetFields} = useForm(form);
|
||||||
|
|
||||||
|
/* 更新visible */
|
||||||
|
const updateVisible = (value: boolean) => {
|
||||||
|
emit('update:visible', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 滚动到指定章节 */
|
||||||
|
const scrollToSection = (index: number) => {
|
||||||
|
currentSection.value = index;
|
||||||
|
const element = document.getElementById(`section-${index}`);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 获取滚动容器 */
|
||||||
|
const getScrollContainer = () => {
|
||||||
|
return document.querySelector('.ant-modal-body');
|
||||||
|
};
|
||||||
|
|
||||||
|
/* AI生成内容 */
|
||||||
|
const generateContent = async (index: number) => {
|
||||||
|
const item = navigationItems.value[index];
|
||||||
|
item.generating = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 这里可以调用AI接口生成内容
|
||||||
|
// 模拟AI生成过程
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
|
||||||
|
// 根据不同章节生成不同的示例内容
|
||||||
|
const sampleContent = getSampleContent(index);
|
||||||
|
item.content = sampleContent;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('AI生成失败:', error);
|
||||||
|
} finally {
|
||||||
|
item.generating = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 获取示例内容 */
|
||||||
|
const getSampleContent = (index: number) => {
|
||||||
|
const samples = [
|
||||||
|
// 审计依据
|
||||||
|
`(一)中共中央办公厅、国务院办公厅《党政主要领导干部和国有企业领导人员经济责任审计规定》(2019年7月7日起实施);
|
||||||
|
|
||||||
|
(二)${form.name}经济责任审计管理办法》(相关文件编号);
|
||||||
|
|
||||||
|
(三)国家有关法律、法规及相关文件;
|
||||||
|
|
||||||
|
(四)${form.name}下达的年度工作目标;
|
||||||
|
|
||||||
|
(五)${form.name}各项管理制度;
|
||||||
|
|
||||||
|
(六)双方签订的审计业务约定书。`,
|
||||||
|
|
||||||
|
// 审计目标
|
||||||
|
`通过对${form.name}相关负责人任职期间所负责公司资产、负债情况的真实性、合法性和效益性及其相关经济活动进行审计,查清其任职期间公司财经政策指导和财务收支工作目标完成情况,重大决策执行及交接情况,遵守国家财经法规情况及公司资产保值增值情况,分清其经济责任,评价其工作业绩,对其任职期间履行经济责任情况作出客观公正的评价。`,
|
||||||
|
|
||||||
|
// 审计对象和范围
|
||||||
|
`审计对象:${form.name}及其相关负责人
|
||||||
|
审计范围:${form.itemYear || '相关年度'}年度财务收支情况、资产管理情况、重大经济决策情况等。
|
||||||
|
具体包括:
|
||||||
|
1. 财务收支的真实性、合法性;
|
||||||
|
2. 资产、负债、损益的真实性;
|
||||||
|
3. 重大经济决策的程序性和效果性;
|
||||||
|
4. 内部控制制度的建立和执行情况。`,
|
||||||
|
|
||||||
|
// 被审计单位基本情况
|
||||||
|
`${form.name}基本情况:
|
||||||
|
成立时间:[待补充]
|
||||||
|
注册资本:[待补充]
|
||||||
|
经营范围:[待补充]
|
||||||
|
组织架构:[待补充]
|
||||||
|
主要业务:[待补充]
|
||||||
|
年末资产总额:${form.totalAssets ? form.totalAssets + '万元' : '[待补充]'}`,
|
||||||
|
|
||||||
|
// 审计内容和重点及审计方法
|
||||||
|
`审计内容:
|
||||||
|
1. 财务收支审计
|
||||||
|
2. 资产负债审计
|
||||||
|
3. 经营管理审计
|
||||||
|
4. 内部控制审计
|
||||||
|
|
||||||
|
审计重点:
|
||||||
|
1. 重大经济决策的合规性
|
||||||
|
2. 资产保值增值情况
|
||||||
|
3. 财务管理制度执行情况
|
||||||
|
4. 风险控制措施有效性
|
||||||
|
|
||||||
|
审计方法:
|
||||||
|
1. 账项基础审计
|
||||||
|
2. 制度基础审计
|
||||||
|
3. 风险导向审计
|
||||||
|
4. 计算机辅助审计`,
|
||||||
|
|
||||||
|
// 重要风险的识别及应对
|
||||||
|
`重要风险识别:
|
||||||
|
1. 财务报告风险
|
||||||
|
2. 合规性风险
|
||||||
|
3. 经营风险
|
||||||
|
4. 信息系统风险
|
||||||
|
|
||||||
|
应对措施:
|
||||||
|
1. 加强内部控制测试
|
||||||
|
2. 扩大实质性程序范围
|
||||||
|
3. 增加专业判断和职业怀疑
|
||||||
|
4. 运用专家工作和外部确认`,
|
||||||
|
|
||||||
|
// 审计技术方法
|
||||||
|
`主要采用的审计技术方法:
|
||||||
|
1. 检查:检查记录或文件
|
||||||
|
2. 观察:观察流程或程序的执行
|
||||||
|
3. 询问:向相关人员询问
|
||||||
|
4. 函证:向第三方函证
|
||||||
|
5. 重新计算:重新计算相关数据
|
||||||
|
6. 重新执行:重新执行相关控制
|
||||||
|
7. 分析程序:分析财务和非财务信息`,
|
||||||
|
|
||||||
|
// 工作步骤与时间安排
|
||||||
|
`工作步骤:
|
||||||
|
第一阶段:审计准备(X天)
|
||||||
|
- 了解被审计单位情况
|
||||||
|
- 制定审计计划
|
||||||
|
- 组建审计组
|
||||||
|
|
||||||
|
第二阶段:现场审计(X天)
|
||||||
|
- 内部控制测试
|
||||||
|
- 实质性程序执行
|
||||||
|
- 获取审计证据
|
||||||
|
|
||||||
|
第三阶段:审计报告(X天)
|
||||||
|
- 汇总审计发现
|
||||||
|
- 撰写审计报告
|
||||||
|
- 与被审计单位沟通`,
|
||||||
|
|
||||||
|
// 审计工作的组织实施
|
||||||
|
`组织架构:
|
||||||
|
审计组长:[待指定]
|
||||||
|
审计组员:[待指定]
|
||||||
|
技术复核:[待指定]
|
||||||
|
|
||||||
|
实施要求:
|
||||||
|
1. 严格按照审计准则执行
|
||||||
|
2. 保持职业怀疑态度
|
||||||
|
3. 获取充分适当的审计证据
|
||||||
|
4. 及时与被审计单位沟通
|
||||||
|
5. 按时完成审计工作
|
||||||
|
6. 确保审计质量`
|
||||||
|
];
|
||||||
|
|
||||||
|
return samples[index] || '请输入相关内容...';
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 监听滚动位置更新当前章节 */
|
||||||
|
const handleScroll = () => {
|
||||||
|
const sections = navigationItems.value.map((_, index) =>
|
||||||
|
document.getElementById(`section-${index}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
const scrollContainer = getScrollContainer();
|
||||||
|
if (!scrollContainer) return;
|
||||||
|
|
||||||
|
const containerTop = scrollContainer.getBoundingClientRect().top;
|
||||||
|
|
||||||
|
for (let i = sections.length - 1; i >= 0; i--) {
|
||||||
|
const section = sections[i];
|
||||||
|
if (section) {
|
||||||
|
const sectionTop = section.getBoundingClientRect().top - containerTop;
|
||||||
|
if (sectionTop <= 100) { // 100px 的偏移量
|
||||||
|
currentSection.value = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 键盘快捷键处理 */
|
||||||
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
// 只在弹窗打开时处理快捷键
|
||||||
|
if (!props.visible) return;
|
||||||
|
|
||||||
|
// Ctrl/Cmd + 数字键 1-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) {
|
||||||
|
scrollToSection(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上下箭头键导航
|
||||||
|
if (event.key === 'ArrowUp' && event.ctrlKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
const prevIndex = Math.max(0, currentSection.value - 1);
|
||||||
|
scrollToSection(prevIndex);
|
||||||
|
} else if (event.key === 'ArrowDown' && event.ctrlKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
const nextIndex = Math.min(navigationItems.value.length - 1, currentSection.value + 1);
|
||||||
|
scrollToSection(nextIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 保存编辑 */
|
||||||
|
const save = () => {
|
||||||
|
};
|
||||||
|
|
||||||
|
// 组件挂载时添加滚动监听和键盘监听
|
||||||
|
onMounted(() => {
|
||||||
|
const scrollContainer = getScrollContainer();
|
||||||
|
if (scrollContainer) {
|
||||||
|
scrollContainer.addEventListener('scroll', handleScroll);
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', handleKeydown);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件卸载时移除监听
|
||||||
|
onUnmounted(() => {
|
||||||
|
const scrollContainer = getScrollContainer();
|
||||||
|
if (scrollContainer) {
|
||||||
|
scrollContainer.removeEventListener('scroll', handleScroll);
|
||||||
|
}
|
||||||
|
document.removeEventListener('keydown', handleKeydown);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(visible) => {
|
||||||
|
if (visible) {
|
||||||
|
if (props.data) {
|
||||||
|
loading.value = true;
|
||||||
|
assignObject(form, props.data);
|
||||||
|
// 重置到第一个章节
|
||||||
|
currentSection.value = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resetFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import * as MenuIcons from '@/layout/menu-icons';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PwlProjectInfo',
|
||||||
|
components: MenuIcons
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.navigation-container {
|
||||||
|
.nav-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 0 16px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.nav-number {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 8px;
|
||||||
|
min-width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-text {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
flex: 1;
|
||||||
|
height: 6px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
border-radius: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #52c41a);
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
min-width: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-content {
|
||||||
|
.audit-section {
|
||||||
|
scroll-margin-top: 20px;
|
||||||
|
|
||||||
|
.section-description {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
border-left: 4px solid #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-textarea {
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #1890ff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-card-head) {
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-card-body) {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-back-top) {
|
||||||
|
right: 30px;
|
||||||
|
bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'action'">
|
<template v-if="column.key === 'action'">
|
||||||
<a class="text-pink-500" @click="openEdit(record)">生成报告</a>
|
<a class="text-pink-500" @click="openReport(record)">生成报告</a>
|
||||||
<a-divider type="vertical"/>
|
<a-divider type="vertical"/>
|
||||||
<a @click="openEdit(record)">修改</a>
|
<a @click="openEdit(record)">修改</a>
|
||||||
<a-divider type="vertical"/>
|
<a-divider type="vertical"/>
|
||||||
@@ -85,7 +85,9 @@
|
|||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<!-- 编辑弹窗 -->
|
<!-- 编辑弹窗 -->
|
||||||
<PwlProjectEdit v-model:visible="showEdit" :data="current" @done="reload"/>
|
<Edit v-model:visible="showEdit" :data="current" @done="reload"/>
|
||||||
|
<!-- 生成报告 -->
|
||||||
|
<Report v-model:visible="showReport" :data="current" @done="reload"/>
|
||||||
</a-page-header>
|
</a-page-header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -100,7 +102,8 @@ import type {
|
|||||||
ColumnItem
|
ColumnItem
|
||||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||||
import Search from './components/search.vue';
|
import Search from './components/search.vue';
|
||||||
import PwlProjectEdit from './components/pwlProjectEdit.vue';
|
import Edit from './components/pwlProjectEdit.vue';
|
||||||
|
import Report from './components/report.vue';
|
||||||
import {pagePwlProject, removePwlProject, removeBatchPwlProject} from '@/api/pwl/pwlProject';
|
import {pagePwlProject, removePwlProject, removeBatchPwlProject} from '@/api/pwl/pwlProject';
|
||||||
import type {PwlProject, PwlProjectParam} from '@/api/pwl/pwlProject/model';
|
import type {PwlProject, PwlProjectParam} from '@/api/pwl/pwlProject/model';
|
||||||
import {getPageTitle} from "@/utils/common";
|
import {getPageTitle} from "@/utils/common";
|
||||||
@@ -115,6 +118,8 @@ const selection = ref<PwlProject[]>([]);
|
|||||||
const current = ref<PwlProject | null>(null);
|
const current = ref<PwlProject | null>(null);
|
||||||
// 是否显示编辑弹窗
|
// 是否显示编辑弹窗
|
||||||
const showEdit = ref(false);
|
const showEdit = ref(false);
|
||||||
|
// 是否显示报告弹窗
|
||||||
|
const showReport = ref(false);
|
||||||
// 是否显示批量移动弹窗
|
// 是否显示批量移动弹窗
|
||||||
const showMove = ref(false);
|
const showMove = ref(false);
|
||||||
// const draftUser = ref<string[]>([]);
|
// const draftUser = ref<string[]>([]);
|
||||||
@@ -380,6 +385,11 @@ const openEdit = (row?: PwlProject) => {
|
|||||||
showEdit.value = true;
|
showEdit.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openReport = (row?: PwlProject) => {
|
||||||
|
current.value = row ?? null;
|
||||||
|
showReport.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
/* 打开批量移动弹窗 */
|
/* 打开批量移动弹窗 */
|
||||||
const openMove = () => {
|
const openMove = () => {
|
||||||
showMove.value = true;
|
showMove.value = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user