后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL 校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计, 优化 agent 运行和编排执行链路,清理旧开发文档,前端新增 系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈 对话框和工作台日期选择器,优化报销创建和审批详情交互, 补充单元测试覆盖。
361 lines
13 KiB
Vue
361 lines
13 KiB
Vue
<template>
|
||
<ConfirmDialog
|
||
:open="riskRuleCreateOpen"
|
||
badge="自然语言规则"
|
||
badge-tone="info"
|
||
title="新建风险规则"
|
||
description="默认创建费用类风险规则。选择业务环节和费用领域后填写规则标题与自然语言描述,系统会根据评分模型自动计算风险分数和等级。"
|
||
cancel-text="取消"
|
||
confirm-text="开始生成"
|
||
busy-text="生成中..."
|
||
confirm-tone="primary"
|
||
confirm-icon="mdi mdi-auto-fix"
|
||
:busy="riskRuleCreateBusy"
|
||
:close-on-mask="!riskRuleCreateBusy"
|
||
@close="emit('close-risk-rule-create')"
|
||
@confirm="emit('submit-risk-rule-create')"
|
||
>
|
||
<div class="risk-rule-create-form">
|
||
<label>
|
||
<span>业务环节</span>
|
||
<EnterpriseSelect
|
||
v-model="riskRuleCreateForm.business_stage"
|
||
:options="riskRuleBusinessStageOptions"
|
||
:disabled="riskRuleCreateBusy"
|
||
/>
|
||
</label>
|
||
<label>
|
||
<span>费用领域</span>
|
||
<EnterpriseSelect
|
||
v-model="riskRuleCreateForm.expense_category"
|
||
:options="riskRuleExpenseCategoryOptions"
|
||
:disabled="riskRuleCreateBusy"
|
||
/>
|
||
</label>
|
||
<label>
|
||
<span>是否上传附件</span>
|
||
<EnterpriseSelect
|
||
v-model="riskRuleCreateForm.requires_attachment"
|
||
:options="riskRuleAttachmentOptions"
|
||
:disabled="riskRuleCreateBusy"
|
||
/>
|
||
</label>
|
||
<label class="span-2">
|
||
<span>规则标题</span>
|
||
<input
|
||
v-model="riskRuleCreateForm.rule_title"
|
||
:disabled="riskRuleCreateBusy"
|
||
maxlength="80"
|
||
placeholder="例如:差旅目的地与票据城市一致性校验"
|
||
/>
|
||
</label>
|
||
<label class="span-2">
|
||
<span>自然语言规则</span>
|
||
<textarea
|
||
v-model="riskRuleCreateForm.natural_language"
|
||
:disabled="riskRuleCreateBusy"
|
||
placeholder="例如:住宿城市必须出现在本次差旅行程城市中,否则提示高风险并要求补充说明。"
|
||
></textarea>
|
||
</label>
|
||
</div>
|
||
</ConfirmDialog>
|
||
|
||
<RiskRuleTestDialog
|
||
:open="riskRuleTestOpen"
|
||
:rule="selectedSkill"
|
||
@close="emit('close-risk-rule-test')"
|
||
@report-saved="emit('report-saved', $event)"
|
||
/>
|
||
|
||
<ConfirmDialog
|
||
:open="riskRuleEditOpen"
|
||
badge="规则维护"
|
||
badge-tone="info"
|
||
:title="riskRuleEditMode === 'revision' ? '创建修订版本' : '编辑风险规则'"
|
||
:description="riskRuleEditMode === 'revision' ? '已上线规则不会被直接覆盖,系统会先创建一个新的修订草稿。' : '未上线规则可以直接调整标题、费用领域、附件要求和自然语言描述。'"
|
||
cancel-text="取消"
|
||
:confirm-text="riskRuleEditMode === 'revision' ? '创建修订' : '保存草稿'"
|
||
busy-text="保存中..."
|
||
confirm-tone="primary"
|
||
confirm-icon="mdi mdi-content-save-outline"
|
||
:busy="riskRuleEditBusy"
|
||
:close-on-mask="!riskRuleEditBusy"
|
||
@close="emit('close-risk-rule-edit')"
|
||
@confirm="emit('submit-risk-rule-edit')"
|
||
>
|
||
<div class="risk-rule-create-form">
|
||
<label>
|
||
<span>费用领域</span>
|
||
<EnterpriseSelect
|
||
v-model="riskRuleEditForm.expense_category"
|
||
:options="riskRuleExpenseCategoryOptions"
|
||
:disabled="riskRuleEditBusy"
|
||
/>
|
||
</label>
|
||
<label>
|
||
<span>是否上传附件</span>
|
||
<EnterpriseSelect
|
||
v-model="riskRuleEditForm.requires_attachment"
|
||
:options="riskRuleAttachmentOptions"
|
||
:disabled="riskRuleEditBusy"
|
||
/>
|
||
</label>
|
||
<label class="span-2">
|
||
<span>规则标题</span>
|
||
<input
|
||
v-model="riskRuleEditForm.rule_title"
|
||
:disabled="riskRuleEditBusy"
|
||
maxlength="80"
|
||
placeholder="例如:差旅目的地与票据城市一致性校验"
|
||
/>
|
||
</label>
|
||
<label class="span-2">
|
||
<span>自然语言规则</span>
|
||
<textarea
|
||
v-model="riskRuleEditForm.natural_language"
|
||
:disabled="riskRuleEditBusy"
|
||
placeholder="请用自然语言描述风险判断流程、字段、例外条件和处理动作。"
|
||
></textarea>
|
||
</label>
|
||
<label v-if="riskRuleEditMode === 'revision'" class="span-2">
|
||
<span>修订原因</span>
|
||
<textarea
|
||
v-model="riskRuleEditForm.change_reason"
|
||
:disabled="riskRuleEditBusy"
|
||
placeholder="请说明本次修订要解决的规则问题或业务变化。"
|
||
></textarea>
|
||
</label>
|
||
</div>
|
||
</ConfirmDialog>
|
||
|
||
<ConfirmDialog
|
||
:open="riskRuleDeleteOpen"
|
||
badge="删除规则"
|
||
badge-tone="danger"
|
||
title="删除未发布风险规则"
|
||
description="该操作会删除规则草稿、版本记录和关联 JSON 文件。只有从未发布过的规则允许删除。"
|
||
cancel-text="取消"
|
||
confirm-text="确认删除"
|
||
busy-text="删除中..."
|
||
confirm-tone="danger"
|
||
confirm-icon="mdi mdi-delete-outline"
|
||
:busy="actionState === 'delete-risk-rule'"
|
||
@close="emit('close-delete-risk-rule')"
|
||
@confirm="emit('delete-selected-risk-rule')"
|
||
>
|
||
<div class="risk-rule-action-confirm">
|
||
<span>规则名称</span>
|
||
<strong>{{ selectedSkill?.name }}</strong>
|
||
</div>
|
||
</ConfirmDialog>
|
||
|
||
<ConfirmDialog
|
||
:open="riskRuleReturnOpen"
|
||
badge="回退规则"
|
||
badge-tone="warning"
|
||
title="回退风险规则"
|
||
description="回退后规则会回到草稿状态,编写人需要根据原因重新调整并测试。"
|
||
cancel-text="取消"
|
||
confirm-text="确认回退"
|
||
busy-text="回退中..."
|
||
confirm-tone="warning"
|
||
confirm-icon="mdi mdi-keyboard-return"
|
||
:busy="actionState === 'return-risk-rule'"
|
||
@close="emit('close-return-risk-rule')"
|
||
@confirm="emit('return-selected-risk-rule')"
|
||
>
|
||
<label class="risk-rule-action-note">
|
||
<span>回退原因</span>
|
||
<textarea
|
||
v-model="returnNoteModel"
|
||
rows="4"
|
||
:disabled="actionState === 'return-risk-rule'"
|
||
placeholder="请说明需要编写人调整的规则问题"
|
||
></textarea>
|
||
</label>
|
||
</ConfirmDialog>
|
||
|
||
<ConfirmDialog
|
||
:open="riskRulePublishOpen"
|
||
badge="发布上线"
|
||
badge-tone="info"
|
||
title="发布风险规则"
|
||
description="发布后该规则会进入真实业务风险扫描,只加载正式上线规则。"
|
||
cancel-text="取消"
|
||
confirm-text="确认发布"
|
||
busy-text="发布中..."
|
||
confirm-tone="primary"
|
||
confirm-icon="mdi mdi-rocket-launch-outline"
|
||
:busy="actionState === 'publish-risk-rule'"
|
||
@close="emit('close-publish-risk-rule')"
|
||
@confirm="emit('publish-selected-risk-rule')"
|
||
>
|
||
<div class="risk-rule-action-confirm">
|
||
<span>测试状态</span>
|
||
<strong>{{ riskRuleTestPassed ? '已确认通过' : '未确认通过' }}</strong>
|
||
</div>
|
||
</ConfirmDialog>
|
||
|
||
<ConfirmDialog
|
||
:open="Boolean(versionSwitchTarget)"
|
||
badge="切换版本"
|
||
badge-tone="info"
|
||
title="切换规则版本"
|
||
description="切换后编辑器只会替换当前展示内容,不会直接回滚后端当前版本。"
|
||
cancel-text="取消"
|
||
confirm-text="确认切换"
|
||
busy-text="切换中..."
|
||
confirm-tone="primary"
|
||
confirm-icon="mdi mdi-swap-horizontal"
|
||
@close="emit('cancel-version-switch')"
|
||
@confirm="emit('confirm-version-switch')"
|
||
>
|
||
<div class="version-modal-summary">
|
||
<div>
|
||
<span>当前展示版本</span>
|
||
<strong>{{ selectedSkill?.displayVersion }}</strong>
|
||
</div>
|
||
<i class="mdi mdi-arrow-right"></i>
|
||
<div>
|
||
<span>目标版本</span>
|
||
<strong>{{ versionSwitchTarget?.version }}</strong>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="versionSwitchTarget" class="version-modal-note">
|
||
<strong>{{ versionSwitchTarget.note }}</strong>
|
||
<span>{{ versionSwitchTarget.time }}</span>
|
||
</div>
|
||
</ConfirmDialog>
|
||
|
||
<ConfirmDialog
|
||
:open="reviewSubmitOpen"
|
||
badge="提交审核"
|
||
badge-tone="info"
|
||
title="提交规则版本审核"
|
||
description="请先确认本次送审采用的版本号,并选择负责审核的高级管理员。若填写新的版本号,系统会将当前工作稿固化为该版本后再送审。"
|
||
cancel-text="取消"
|
||
confirm-text="确认提交"
|
||
busy-text="提交中..."
|
||
confirm-tone="primary"
|
||
confirm-icon="mdi mdi-send-outline"
|
||
:busy="actionState === 'review-pending'"
|
||
@close="emit('close-submit-review')"
|
||
@confirm="emit('submit-selected-rule-for-review')"
|
||
>
|
||
<div class="review-submit-form">
|
||
<label>
|
||
<span>送审版本号</span>
|
||
<input
|
||
v-model="reviewSubmitVersionModel"
|
||
type="text"
|
||
placeholder="例如:v1.1.0"
|
||
:disabled="actionState === 'review-pending'"
|
||
/>
|
||
</label>
|
||
<label>
|
||
<span>审核人</span>
|
||
<EnterpriseSelect
|
||
v-model="reviewSubmitReviewerModel"
|
||
:options="reviewSubmitReviewerOptions"
|
||
:placeholder="reviewSubmitReviewerLoading ? '加载审核人中...' : '请选择高级管理员'"
|
||
:disabled="reviewSubmitReviewerLoading || actionState === 'review-pending'"
|
||
/>
|
||
</label>
|
||
<p
|
||
v-if="!reviewSubmitReviewerLoading && !hasReviewSubmitReviewers"
|
||
class="review-submit-hint"
|
||
>
|
||
当前没有可选的高级管理员,请先在员工管理中配置具备管理员角色的员工。
|
||
</p>
|
||
<div v-if="selectedSkillUsesJsonRisk" class="review-submit-test-state">
|
||
<span>测试确认</span>
|
||
<strong :class="{ passed: riskRuleTestPassed }">
|
||
{{ riskRuleTestPassed ? '当前版本已通过测试确认' : '当前版本尚未确认测试通过' }}
|
||
</strong>
|
||
<p>只有保存测试报告的风险规则,才能提交给高级财务人员审核。</p>
|
||
</div>
|
||
</div>
|
||
</ConfirmDialog>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed } from 'vue'
|
||
import ConfirmDialog from '../shared/ConfirmDialog.vue'
|
||
import EnterpriseSelect from '../shared/EnterpriseSelect.vue'
|
||
import RiskRuleTestDialog from '../shared/RiskRuleTestDialog.vue'
|
||
|
||
defineOptions({
|
||
name: 'AuditRuleDialogs'
|
||
})
|
||
|
||
const props = defineProps({
|
||
selectedSkill: { type: Object, default: null },
|
||
versionSwitchTarget: { type: Object, default: null },
|
||
actionState: { type: String, default: '' },
|
||
riskRuleCreateOpen: { type: Boolean, default: false },
|
||
riskRuleCreateBusy: { type: Boolean, default: false },
|
||
riskRuleCreateForm: { type: Object, required: true },
|
||
riskRuleBusinessStageOptions: { type: Array, default: () => [] },
|
||
riskRuleExpenseCategoryOptions: { type: Array, default: () => [] },
|
||
riskRuleAttachmentOptions: { type: Array, default: () => [] },
|
||
riskRuleTestOpen: { type: Boolean, default: false },
|
||
riskRuleEditOpen: { type: Boolean, default: false },
|
||
riskRuleEditMode: { type: String, default: 'draft' },
|
||
riskRuleEditForm: { type: Object, default: () => ({}) },
|
||
riskRuleEditBusy: { type: Boolean, default: false },
|
||
riskRuleDeleteOpen: { type: Boolean, default: false },
|
||
riskRuleReturnOpen: { type: Boolean, default: false },
|
||
riskRulePublishOpen: { type: Boolean, default: false },
|
||
riskRuleReturnNote: { type: String, default: '' },
|
||
riskRuleTestPassed: { type: Boolean, default: false },
|
||
reviewSubmitOpen: { type: Boolean, default: false },
|
||
reviewSubmitVersion: { type: String, default: '' },
|
||
reviewSubmitReviewer: { type: String, default: '' },
|
||
reviewSubmitReviewerLoading: { type: Boolean, default: false },
|
||
reviewSubmitReviewerOptions: { type: Array, default: () => [] },
|
||
hasReviewSubmitReviewers: { type: Boolean, default: false },
|
||
selectedSkillUsesJsonRisk: { type: Boolean, default: false }
|
||
})
|
||
|
||
const emit = defineEmits([
|
||
'update:riskRuleReturnNote',
|
||
'update:reviewSubmitVersion',
|
||
'update:reviewSubmitReviewer',
|
||
'close-risk-rule-create',
|
||
'submit-risk-rule-create',
|
||
'close-risk-rule-test',
|
||
'report-saved',
|
||
'close-risk-rule-edit',
|
||
'submit-risk-rule-edit',
|
||
'close-delete-risk-rule',
|
||
'delete-selected-risk-rule',
|
||
'close-return-risk-rule',
|
||
'return-selected-risk-rule',
|
||
'close-publish-risk-rule',
|
||
'publish-selected-risk-rule',
|
||
'cancel-version-switch',
|
||
'confirm-version-switch',
|
||
'close-submit-review',
|
||
'submit-selected-rule-for-review'
|
||
])
|
||
|
||
const returnNoteModel = computed({
|
||
get: () => props.riskRuleReturnNote,
|
||
set: (value) => emit('update:riskRuleReturnNote', value)
|
||
})
|
||
|
||
const reviewSubmitVersionModel = computed({
|
||
get: () => props.reviewSubmitVersion,
|
||
set: (value) => emit('update:reviewSubmitVersion', value)
|
||
})
|
||
|
||
const reviewSubmitReviewerModel = computed({
|
||
get: () => props.reviewSubmitReviewer,
|
||
set: (value) => emit('update:reviewSubmitReviewer', value)
|
||
})
|
||
</script>
|
||
|
||
<style scoped src="../../assets/styles/views/audit-view.css"></style>
|
||
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>
|