feat: 报销审批流重构与管家计划全链路贯通
- 重构报销状态注册表、审批流路由与平台风险标记 - 完善管家意图规划器与模型计划构建器全链路 - 新增 OCR Worker 脚本、数据库会话管理与通知状态 - 优化文档中心、日志视图、预算中心与员工管理交互 - 增强工作台摘要、图标资源与全局主题样式 - 补充审批路由、状态注册、OCR 服务与管家规划器测试覆盖
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="picker-filter" :class="{ open: activeFilterPopover === id }">
|
||||
<div class="picker-filter document-filter" :class="{ open: activeFilterPopover === id }">
|
||||
<button
|
||||
class="picker-trigger"
|
||||
class="picker-trigger filter-btn"
|
||||
type="button"
|
||||
:aria-expanded="activeFilterPopover === id"
|
||||
aria-haspopup="dialog"
|
||||
aria-haspopup="listbox"
|
||||
@click="emit('toggle', id)"
|
||||
>
|
||||
<span class="picker-label">{{ label }}</span>
|
||||
@@ -12,28 +12,21 @@
|
||||
</button>
|
||||
<div
|
||||
v-if="activeFilterPopover === id"
|
||||
class="picker-popover"
|
||||
role="dialog"
|
||||
class="picker-popover document-filter-menu"
|
||||
role="listbox"
|
||||
:aria-label="title"
|
||||
>
|
||||
<header>
|
||||
<strong>{{ title }}</strong>
|
||||
<button type="button" :aria-label="closeLabel" @click="emit('close')">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</button>
|
||||
</header>
|
||||
<div class="picker-option-list">
|
||||
<button
|
||||
v-for="option in options"
|
||||
:key="option.value || `all-${id}`"
|
||||
type="button"
|
||||
class="picker-option"
|
||||
:class="{ active: selectedValue === option.value }"
|
||||
@click="emit('select', option.value)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-for="option in options"
|
||||
:key="option.value || `all-${id}`"
|
||||
type="button"
|
||||
role="option"
|
||||
:aria-selected="selectedValue === option.value"
|
||||
:class="{ active: selectedValue === option.value }"
|
||||
@click="emit('select', option.value)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -56,5 +49,4 @@ defineProps({
|
||||
const emit = defineEmits(['toggle', 'close', 'select'])
|
||||
</script>
|
||||
|
||||
<style scoped src="../../assets/styles/views/audit-view.css"></style>
|
||||
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>
|
||||
<style scoped src="../../assets/styles/components/document-list-shared.css"></style>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<article v-if="visible" class="detail-card panel run-products-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h3>本次任务产物</h3>
|
||||
<p>{{ productSubtitle }}</p>
|
||||
</div>
|
||||
<EnterpriseDetailCard
|
||||
v-if="visible"
|
||||
class="run-products-card"
|
||||
title="本次任务产物"
|
||||
:description="productSubtitle"
|
||||
>
|
||||
<template #actions>
|
||||
<span class="edit-badge">{{ productBadge }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="loading" class="run-product-state">
|
||||
<i class="mdi mdi-loading mdi-spin"></i>
|
||||
@@ -211,12 +212,13 @@
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</article>
|
||||
</EnterpriseDetailCard>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import EnterpriseDetailCard from '../shared/EnterpriseDetailCard.vue'
|
||||
import { fetchRunRiskObservations } from '../../services/riskObservations.js'
|
||||
import {
|
||||
extractWorkRecordToolSummary,
|
||||
|
||||
@@ -157,34 +157,26 @@
|
||||
</template>
|
||||
</EnterpriseListPage>
|
||||
|
||||
<!-- 详情视图 (全屏样式,参考 AuditJsonRiskRuleDetail) -->
|
||||
<div v-else key="detail" class="json-risk-editor-shell panel work-records-detail-stage">
|
||||
<div v-if="detailLoading" class="work-record-detail-state panel" style="min-height: 200px; display: grid; place-items: center; border: 0;">
|
||||
<TableLoadingState
|
||||
variant="panel"
|
||||
title="详情加载中"
|
||||
message="正在读取该次工作记录的完整执行信息"
|
||||
icon="mdi mdi-clipboard-text-search-outline"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else-if="detailError" class="work-record-detail-state error panel" style="min-height: 200px; display: grid; place-items: center; text-align: center; border: 0; color: #dc2626;">
|
||||
<i class="mdi mdi-alert-circle-outline" style="font-size: 32px; margin-bottom: 8px;"></i>
|
||||
<strong>工作记录详情加载失败</strong>
|
||||
<p>{{ detailError }}</p>
|
||||
<button class="minor-action" type="button" @click="reloadSelectedDetail" style="margin-top: 12px;">重新加载</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="json-risk-editor-body work-record-detail-shell">
|
||||
<section class="json-risk-main-stage work-record-detail-body inline-detail">
|
||||
<!-- 卡片1:基本信息 -->
|
||||
<article class="detail-card panel json-risk-summary-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h3>基本信息</h3>
|
||||
<p>此次运行的执行周期、触发来源、标识信息与最终状态。</p>
|
||||
</div>
|
||||
</div>
|
||||
<EnterpriseDetailPage
|
||||
v-else
|
||||
key="detail"
|
||||
variant="work-record-detail-page"
|
||||
actions-class="work-record-detail-actions"
|
||||
:loading="detailLoading"
|
||||
:error="detailError"
|
||||
error-title="工作记录详情加载失败"
|
||||
loading-title="详情加载中"
|
||||
loading-message="正在读取该次工作记录的完整执行信息"
|
||||
loading-icon="mdi mdi-clipboard-text-search-outline"
|
||||
back-label="返回工作记录列表"
|
||||
@back="closeWorkRecordDetail"
|
||||
>
|
||||
<template #main>
|
||||
<EnterpriseDetailCard
|
||||
class="work-record-detail-card work-record-summary-card"
|
||||
title="基本信息"
|
||||
description="此次运行的执行周期、触发来源、标识信息与最终状态。"
|
||||
>
|
||||
<div class="json-risk-meta-grid">
|
||||
<div class="json-risk-meta-item">
|
||||
<span class="json-risk-meta-label">Run ID</span>
|
||||
@@ -211,75 +203,63 @@
|
||||
<span class="json-risk-meta-value">{{ resolveWorkRecordStatusNote(selectedRunDetail) || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</EnterpriseDetailCard>
|
||||
|
||||
<!-- 卡片2:执行摘要 -->
|
||||
<article class="detail-card panel json-risk-description-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h3>执行摘要</h3>
|
||||
<p>本次数字员工工作流的执行内容与结果摘要。</p>
|
||||
</div>
|
||||
<EnterpriseDetailCard
|
||||
class="work-record-detail-card work-record-summary-copy-card"
|
||||
title="执行摘要"
|
||||
description="本次数字员工工作流的执行内容与结果摘要。"
|
||||
>
|
||||
<template #actions>
|
||||
<span class="edit-badge">{{ resolveWorkRecordSummaryMeta(selectedRunDetail) }}</span>
|
||||
</div>
|
||||
<p class="json-risk-description-text" style="padding: 0 12px 12px; margin: 0;">{{ selectedRunDetail.result_summary || '暂无执行摘要。' }}</p>
|
||||
<p v-if="selectedRunDetail.error_message" class="work-record-error-text" style="margin: 0 12px 12px; padding: 10px 12px; border: 1px solid #fecaca; border-radius: 4px; background: #fef2f2; color: #b91c1c;">
|
||||
</template>
|
||||
<p class="json-risk-description-text work-record-summary-text">{{ selectedRunDetail.result_summary || '暂无执行摘要。' }}</p>
|
||||
<p v-if="selectedRunDetail.error_message" class="work-record-error-text">
|
||||
{{ selectedRunDetail.error_message }}
|
||||
</p>
|
||||
</article>
|
||||
</p>
|
||||
</EnterpriseDetailCard>
|
||||
|
||||
<DigitalEmployeeRunProducts :run="selectedRunDetail" />
|
||||
<DigitalEmployeeRunProducts :run="selectedRunDetail" />
|
||||
</template>
|
||||
|
||||
<!-- 卡片3:工具调用 -->
|
||||
<article class="detail-card panel">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h3>工具调用</h3>
|
||||
<p>此任务在执行期间调用的外部系统/工具细节与执行状态。</p>
|
||||
</div>
|
||||
<template #side>
|
||||
<EnterpriseDetailCard
|
||||
class="work-record-detail-card"
|
||||
title="工具调用"
|
||||
description="此任务在执行期间调用的外部系统/工具细节与执行状态。"
|
||||
>
|
||||
<template #actions>
|
||||
<span class="edit-badge">{{ (selectedRunDetail.tool_calls || []).length }} 次调用</span>
|
||||
</div>
|
||||
<div v-if="(selectedRunDetail.tool_calls || []).length" class="work-record-tool-list" style="padding: 0 12px 12px; display: grid; gap: 8px;">
|
||||
<article v-for="toolCall in selectedRunDetail.tool_calls" :key="toolCall.id" style="display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; border: 1px solid #edf2f7; border-radius: 4px; background: #f8fafc;">
|
||||
<strong style="color: #0f172a; font-size: 13px;">{{ toolCall.tool_name }}</strong>
|
||||
<span style="color: #64748b; font-size: 12px;">{{ toolCall.tool_type || 'tool' }} · {{ toolCall.status || 'unknown' }}</span>
|
||||
</template>
|
||||
<div v-if="(selectedRunDetail.tool_calls || []).length" class="work-record-tool-list">
|
||||
<article v-for="toolCall in selectedRunDetail.tool_calls" :key="toolCall.id" class="work-record-tool-item">
|
||||
<strong>{{ toolCall.tool_name }}</strong>
|
||||
<span>{{ toolCall.tool_type || 'tool' }} · {{ toolCall.status || 'unknown' }}</span>
|
||||
</article>
|
||||
</div>
|
||||
<div v-else class="work-record-inline-empty" style="padding: 0 12px 12px; color: #94a3b8; font-size: 13px;">当前暂无工具调用明细。</div>
|
||||
</article>
|
||||
</div>
|
||||
<div v-else class="work-record-inline-empty">当前暂无工具调用明细。</div>
|
||||
</EnterpriseDetailCard>
|
||||
|
||||
<!-- 卡片4:执行上下文 -->
|
||||
<article class="detail-card panel">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h3>执行上下文</h3>
|
||||
<p>后台调度的运行时配置与状态信息(JSON 格式)。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 0 12px 12px;">
|
||||
<pre class="work-record-code-block" style="max-height: 320px; margin: 0; padding: 12px; overflow: auto; border: 1px solid #e2e8f0; border-radius: 4px; background: #0f172a; color: #e2e8f0; font-size: 12px; line-height: 1.55;">{{ formatJson(selectedRunDetail.route_json) }}</pre>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
<EnterpriseDetailCard
|
||||
class="work-record-detail-card"
|
||||
title="执行上下文"
|
||||
description="后台调度的运行时配置与状态信息(JSON 格式)。"
|
||||
>
|
||||
<pre class="work-record-code-block">{{ formatJson(selectedRunDetail.route_json) }}</pre>
|
||||
</EnterpriseDetailCard>
|
||||
</template>
|
||||
|
||||
<footer class="detail-actions">
|
||||
<button class="back-action" type="button" @click="closeWorkRecordDetail">
|
||||
<i class="mdi mdi-arrow-left"></i>
|
||||
<span>返回工作记录列表</span>
|
||||
<template #actions>
|
||||
<button class="minor-action" type="button" :disabled="!selectedRunDetail?.run_id" @click="openTraceCenter">
|
||||
<i class="mdi mdi-timeline-text-outline"></i>
|
||||
<span>查看 Trace</span>
|
||||
</button>
|
||||
<div class="detail-action-group">
|
||||
<button class="minor-action" type="button" :disabled="!selectedRunDetail?.run_id" @click="openTraceCenter">
|
||||
<i class="mdi mdi-timeline-text-outline"></i>
|
||||
<span>查看 Trace</span>
|
||||
</button>
|
||||
<button class="minor-action" type="button" :disabled="detailLoading" @click="reloadSelectedDetail">
|
||||
<i class="mdi mdi-refresh"></i>
|
||||
<span>{{ detailLoading ? '刷新中...' : '刷新详情' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<button class="minor-action" type="button" :disabled="detailLoading" @click="reloadSelectedDetail">
|
||||
<i class="mdi mdi-refresh"></i>
|
||||
<span>{{ detailLoading ? '刷新中...' : '刷新详情' }}</span>
|
||||
</button>
|
||||
</template>
|
||||
</EnterpriseDetailPage>
|
||||
</Transition>
|
||||
</section>
|
||||
</template>
|
||||
@@ -290,8 +270,9 @@ import { useRouter } from 'vue-router'
|
||||
|
||||
import AuditPickerFilter from './AuditPickerFilter.vue'
|
||||
import DigitalEmployeeRunProducts from './DigitalEmployeeRunProducts.vue'
|
||||
import EnterpriseDetailCard from '../shared/EnterpriseDetailCard.vue'
|
||||
import EnterpriseDetailPage from '../shared/EnterpriseDetailPage.vue'
|
||||
import EnterpriseListPage from '../shared/EnterpriseListPage.vue'
|
||||
import TableLoadingState from '../shared/TableLoadingState.vue'
|
||||
import { fetchAgentRunDetail, fetchAgentRuns } from '../../services/agentAssets.js'
|
||||
import { useToast } from '../../composables/useToast.js'
|
||||
import {
|
||||
@@ -650,103 +631,5 @@ onBeforeUnmount(() => {
|
||||
<style scoped src="../../assets/styles/views/audit-view.css"></style>
|
||||
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>
|
||||
|
||||
<style scoped>
|
||||
.digital-work-records {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.digital-employee-list-panel,
|
||||
.digital-work-records-list-stage {
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.digital-work-records-table {
|
||||
min-width: 1180px;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.digital-work-records-table .col-time { width: 14%; }
|
||||
.digital-work-records-table .col-module { width: 13%; }
|
||||
.digital-work-records-table .col-source { width: 10%; }
|
||||
.digital-work-records-table .col-status { width: 16%; }
|
||||
.digital-work-records-table .col-summary { width: 31%; }
|
||||
.digital-work-records-table .col-trace { width: 16%; }
|
||||
|
||||
.work-record-row {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.work-record-row:focus-visible {
|
||||
box-shadow: inset 0 0 0 2px rgba(58, 124, 165, 0.28);
|
||||
}
|
||||
|
||||
.work-record-summary-cell {
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.work-record-summary-cell strong,
|
||||
.work-record-summary-cell span,
|
||||
.work-record-summary-cell em {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.work-record-summary-cell strong {
|
||||
color: #0f172a;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.work-record-summary-cell span {
|
||||
margin-top: 4px;
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.work-record-summary-cell em {
|
||||
margin-top: 6px;
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.work-record-trace-cell {
|
||||
color: #2563eb !important;
|
||||
}
|
||||
|
||||
.work-records-detail-stage,
|
||||
.work-record-detail-shell {
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.digital-work-records :deep(.toolbar-actions .picker-filter),
|
||||
.digital-work-records :deep(.toolbar-actions .picker-trigger) {
|
||||
min-width: 148px;
|
||||
}
|
||||
|
||||
.digital-refresh-now {
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.digital-refresh-now .mdi {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.work-record-detail-body.inline-detail {
|
||||
padding: 0;
|
||||
overflow: visible;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
</style>
|
||||
<style scoped src="../../assets/styles/components/digital-employee-work-records.css"></style>
|
||||
<style scoped src="../../assets/styles/components/digital-employee-work-records-overrides.css"></style>
|
||||
|
||||
Reference in New Issue
Block a user