feat: 报销审批流重构与管家计划全链路贯通
- 重构报销状态注册表、审批流路由与平台风险标记 - 完善管家意图规划器与模型计划构建器全链路 - 新增 OCR Worker 脚本、数据库会话管理与通知状态 - 优化文档中心、日志视图、预算中心与员工管理交互 - 增强工作台摘要、图标资源与全局主题样式 - 补充审批路由、状态注册、OCR 服务与管家规划器测试覆盖
This commit is contained in:
57
web/src/components/shared/DocumentDropdownFilter.vue
Normal file
57
web/src/components/shared/DocumentDropdownFilter.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="picker-filter document-filter" :class="[rootClass, { open: isOpen }]">
|
||||
<button
|
||||
class="picker-trigger filter-btn"
|
||||
:class="triggerClass"
|
||||
type="button"
|
||||
:aria-expanded="isOpen"
|
||||
aria-haspopup="listbox"
|
||||
@click="emit('toggle', id)"
|
||||
>
|
||||
<i v-if="icon" :class="icon"></i>
|
||||
<span class="picker-label">{{ label }}</span>
|
||||
<i class="mdi mdi-chevron-down"></i>
|
||||
</button>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="picker-popover document-filter-menu"
|
||||
:class="menuClass"
|
||||
role="listbox"
|
||||
:aria-label="title || label"
|
||||
>
|
||||
<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>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
id: { type: String, required: true },
|
||||
title: { type: String, default: '' },
|
||||
activeFilterKey: { type: String, default: '' },
|
||||
label: { type: String, default: '' },
|
||||
options: { type: Array, default: () => [] },
|
||||
selectedValue: { type: [String, Number, Boolean], default: '' },
|
||||
rootClass: { type: [String, Array, Object], default: '' },
|
||||
triggerClass: { type: [String, Array, Object], default: '' },
|
||||
menuClass: { type: [String, Array, Object], default: '' },
|
||||
icon: { type: String, default: '' }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['toggle', 'select'])
|
||||
const isOpen = computed(() => props.activeFilterKey === props.id)
|
||||
</script>
|
||||
|
||||
<style scoped src="../../assets/styles/components/document-list-shared.css"></style>
|
||||
@@ -70,7 +70,7 @@ const props = defineProps({
|
||||
severityLabel: { type: String, default: '中风险' }
|
||||
})
|
||||
|
||||
const FONT = "Helvetica, Arial, sans-serif"
|
||||
const FONT = "-apple-system, BlinkMacSystemFont, 'SF Pro Text', 'PingFang SC', 'Segoe UI', Arial, sans-serif"
|
||||
const TEXT = '#0d0d0d'
|
||||
const MUTED = '#6e6e80'
|
||||
const NEUTRAL_LINE = '#cbd5e1'
|
||||
|
||||
@@ -39,13 +39,15 @@ const iconStyle = computed(() => iconMeta.value.style)
|
||||
|
||||
.workbench-list-icon__halo {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
left: 0;
|
||||
width: 3px;
|
||||
inset: 6px auto 6px -2px;
|
||||
width: 4px;
|
||||
border-radius: 2px;
|
||||
background: color-mix(in srgb, var(--icon-color, var(--theme-primary)) 78%, #ffffff);
|
||||
opacity: 0.72;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
color-mix(in srgb, var(--icon-color, var(--theme-primary)) 78%, #ffffff),
|
||||
color-mix(in srgb, var(--icon-color, var(--theme-primary)) 28%, #ffffff)
|
||||
);
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.workbench-list-icon__panel {
|
||||
@@ -57,18 +59,20 @@ const iconStyle = computed(() => iconMeta.value.style)
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
border: 1px solid color-mix(in srgb, var(--icon-color, var(--theme-primary)) 22%, var(--line, #e2e8f0));
|
||||
border: 1px solid color-mix(in srgb, var(--icon-color, var(--theme-primary)) 24%, var(--line, #e2e8f0));
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.82), rgba(255, 255, 255, 0.44)),
|
||||
radial-gradient(circle at 30% 20%, rgba(255, 255, 255, 0.98), rgba(255, 255, 255, 0) 48%),
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(255, 255, 255, 0.52)),
|
||||
linear-gradient(
|
||||
135deg,
|
||||
color-mix(in srgb, var(--icon-accent, var(--theme-primary-soft)) 64%, #fff) 0%,
|
||||
#fff 52%,
|
||||
color-mix(in srgb, var(--icon-color, var(--theme-primary)) 8%, var(--surface-soft, #f8fafc)) 100%
|
||||
color-mix(in srgb, var(--icon-accent, var(--theme-primary-soft)) 72%, #fff) 0%,
|
||||
#fff 46%,
|
||||
color-mix(in srgb, var(--icon-color, var(--theme-primary)) 12%, var(--surface-soft, #f8fafc)) 100%
|
||||
);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
||||
0 1px 2px rgba(15, 23, 42, 0.045);
|
||||
inset 0 -1px 0 color-mix(in srgb, var(--icon-color, var(--theme-primary)) 8%, rgba(255, 255, 255, 0.9)),
|
||||
0 8px 18px rgba(15, 23, 42, 0.055);
|
||||
}
|
||||
|
||||
.workbench-list-icon__shine {
|
||||
@@ -95,10 +99,37 @@ const iconStyle = computed(() => iconMeta.value.style)
|
||||
}
|
||||
|
||||
.workbench-list-icon--outline .workbench-list-icon__art :deep(.workbench-heroicon) {
|
||||
stroke-width: 1.65;
|
||||
stroke-width: 1.55;
|
||||
}
|
||||
|
||||
.workbench-list-icon__art :deep(.icon-fill) {
|
||||
fill: currentColor;
|
||||
stroke: none;
|
||||
opacity: 0.09;
|
||||
}
|
||||
|
||||
.workbench-list-icon__art :deep(.icon-accent) {
|
||||
opacity: 0.36;
|
||||
}
|
||||
|
||||
.workbench-list-icon__art :deep(.icon-muted) {
|
||||
opacity: 0.62;
|
||||
}
|
||||
|
||||
.workbench-list-icon--solid .workbench-list-icon__art :deep(.workbench-heroicon path) {
|
||||
opacity: 0.96;
|
||||
}
|
||||
|
||||
.workbench-list-icon__art :deep(.workbench-image-icon) {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
max-width: none;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
filter: drop-shadow(0 4px 8px rgba(15, 23, 42, 0.15));
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user