chore: remove prototype files and unused UI assets
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -5,23 +5,19 @@
|
||||
v-for="metric in kpiMetrics"
|
||||
:key="metric.label"
|
||||
class="kpi-card panel"
|
||||
:style="{ '--accent': metric.accent }"
|
||||
:style="{ '--accent': metric.accent, '--delay': `${metric.delay}ms` }"
|
||||
>
|
||||
<div class="kpi-top">
|
||||
<div class="kpi-icon">
|
||||
<i :class="metric.icon"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ metric.label }}</p>
|
||||
<strong>{{ metric.displayValue }}</strong>
|
||||
</div>
|
||||
<div class="kpi-head">
|
||||
<span class="kpi-icon"><i :class="metric.icon"></i></span>
|
||||
<span class="kpi-label">{{ metric.label }}</span>
|
||||
</div>
|
||||
<div class="kpi-bottom" :class="metric.trend">
|
||||
<span>
|
||||
<i :class="metric.trend === 'down' ? 'pi pi-arrow-down' : 'pi pi-arrow-up'"></i>
|
||||
<strong class="kpi-value">{{ metric.displayValue }}</strong>
|
||||
<div class="kpi-trend">
|
||||
<span class="kpi-badge" :class="metric.trend">
|
||||
<i :class="metric.trend === 'down' ? 'mdi mdi-arrow-down' : 'mdi mdi-arrow-up'"></i>
|
||||
{{ metric.changeText }}
|
||||
</span>
|
||||
<small>{{ metric.delta }}</small>
|
||||
<span class="kpi-delta">{{ metric.delta }}</span>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
@@ -29,7 +25,7 @@
|
||||
<div class="content-grid top-grid">
|
||||
<article class="panel dashboard-card trend-panel">
|
||||
<div class="card-head">
|
||||
<h3>报销申请与审批趋势 <i class="pi pi-info-circle"></i></h3>
|
||||
<h3>报销申请与审批趋势 <i class="mdi mdi-information-outline"></i></h3>
|
||||
<select v-model="activeTrendRange" class="card-select" aria-label="趋势时间范围">
|
||||
<option v-for="range in trendRanges" :key="range">{{ range }}</option>
|
||||
</select>
|
||||
@@ -45,7 +41,7 @@
|
||||
|
||||
<article class="panel dashboard-card donut-panel">
|
||||
<div class="card-head">
|
||||
<h3>费用结构 <i class="pi pi-info-circle"></i></h3>
|
||||
<h3>费用结构 <i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
<DonutChart :items="spendLegend" center-value="¥361.6K" center-label="待处理金额" />
|
||||
<p class="panel-note">* 百分比为占待处理金额比例</p>
|
||||
@@ -53,7 +49,7 @@
|
||||
|
||||
<article class="panel dashboard-card donut-panel">
|
||||
<div class="card-head">
|
||||
<h3>风险异常分布 <i class="pi pi-info-circle"></i></h3>
|
||||
<h3>风险异常分布 <i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
<DonutChart :items="riskLegend" :center-value="`${riskTotal}`" center-label="异常预警单" />
|
||||
<p class="panel-note">* 近30天数据</p>
|
||||
@@ -63,7 +59,7 @@
|
||||
<div class="content-grid bottom-grid">
|
||||
<article class="panel dashboard-card rank-panel">
|
||||
<div class="card-head">
|
||||
<h3>部门报销排行(待处理金额) <i class="pi pi-info-circle"></i></h3>
|
||||
<h3>部门报销排行(待处理金额) <i class="mdi mdi-information-outline"></i></h3>
|
||||
<select v-model="activeDepartmentRange" class="card-select" aria-label="部门排行时间范围">
|
||||
<option v-for="range in departmentRangeOptions" :key="range">{{ range }}</option>
|
||||
</select>
|
||||
@@ -74,11 +70,16 @@
|
||||
|
||||
<article class="panel dashboard-card bottleneck-panel">
|
||||
<div class="card-head">
|
||||
<h3>审批瓶颈(平均处理时长) <i class="pi pi-info-circle"></i></h3>
|
||||
<h3>审批瓶颈(平均处理时长) <i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
|
||||
<div class="bottleneck-list">
|
||||
<div v-for="item in bottlenecks" :key="item.name" class="bottleneck-row">
|
||||
<div
|
||||
v-for="(item, index) in bottlenecks"
|
||||
:key="item.name"
|
||||
class="bottleneck-row"
|
||||
:style="{ '--delay': `${index * 70}ms` }"
|
||||
>
|
||||
<div class="reviewer">
|
||||
<div class="reviewer-avatar">{{ item.avatar }}</div>
|
||||
<div>
|
||||
@@ -93,12 +94,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="text-link">查看全部 <i class="pi pi-angle-right"></i></button>
|
||||
<button type="button" class="text-link">查看全部 <i class="mdi mdi-chevron-right"></i></button>
|
||||
</article>
|
||||
|
||||
<article class="panel dashboard-card budget-panel">
|
||||
<div class="card-head">
|
||||
<h3>预算执行率(本月) <i class="pi pi-info-circle"></i></h3>
|
||||
<h3>预算执行率(本月) <i class="mdi mdi-information-outline"></i></h3>
|
||||
</div>
|
||||
|
||||
<GaugeChart
|
||||
@@ -108,7 +109,7 @@
|
||||
:left="budgetSummary.left"
|
||||
/>
|
||||
|
||||
<button type="button" class="text-link">查看详情 <i class="pi pi-angle-right"></i></button>
|
||||
<button type="button" class="text-link">查看详情 <i class="mdi mdi-chevron-right"></i></button>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
@@ -163,18 +164,23 @@ const formatCompact = (value) => {
|
||||
|
||||
const formatCurrency = (value) => formatCompact(value)
|
||||
|
||||
const kpiMetrics = computed(() => metricBlueprints.map((metric) => {
|
||||
const formatMetricValue = (metric, value) => {
|
||||
if (metric.key === 'pendingAmount') return formatCurrency(Math.round(value))
|
||||
if (metric.key === 'avgSla') return `${value.toFixed(1)} ${metric.unit}`
|
||||
if (metric.unit === '%') return `${Math.round(value)} ${metric.unit}`
|
||||
if (metric.unit) return `${Math.round(value)} ${metric.unit}`
|
||||
return `${Math.round(value)}`
|
||||
}
|
||||
|
||||
const kpiMetrics = computed(() => metricBlueprints.map((metric, index) => {
|
||||
const rawValue = demoTotals[metric.key]
|
||||
const displayValue = metric.key === 'pendingAmount'
|
||||
? formatCurrency(rawValue)
|
||||
: metric.unit && !String(rawValue).endsWith(metric.unit)
|
||||
? `${rawValue} ${metric.unit}`
|
||||
: `${rawValue}`
|
||||
const displayValue = formatMetricValue(metric, rawValue)
|
||||
|
||||
return {
|
||||
...metric,
|
||||
displayValue,
|
||||
changeText: metric.change
|
||||
changeText: metric.change,
|
||||
delay: index * 55
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -222,86 +228,104 @@ const rankedDepartments = computed(() => {
|
||||
}
|
||||
|
||||
.kpi-card {
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
padding: 20px 20px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
border-left: 3px solid var(--accent);
|
||||
animation: dashboardItemIn 520ms var(--ease) both;
|
||||
animation-delay: var(--delay, 0ms);
|
||||
transition: box-shadow 200ms ease, transform 200ms ease;
|
||||
}
|
||||
|
||||
.kpi-top {
|
||||
flex: 1;
|
||||
.kpi-card:hover {
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.kpi-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.kpi-top > div {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
.kpi-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--accent) 10%, white);
|
||||
color: var(--accent);
|
||||
font-size: 18px;
|
||||
flex: 0 0 auto;
|
||||
animation: iconPop 560ms var(--ease) both;
|
||||
animation-delay: calc(var(--delay, 0ms) + 100ms);
|
||||
}
|
||||
|
||||
.kpi-top p {
|
||||
margin: 0 0 6px;
|
||||
.kpi-label {
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1.3;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.kpi-top strong {
|
||||
.kpi-value {
|
||||
display: block;
|
||||
color: #1e293b;
|
||||
font-size: clamp(18px, 1.6vw, 22px);
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
color: #0f172a;
|
||||
font-size: clamp(20px, 1.6vw, 26px);
|
||||
line-height: 1;
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 16px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.kpi-icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 10px;
|
||||
background: color-mix(in srgb, var(--accent) 10%, white);
|
||||
color: var(--accent);
|
||||
font-size: 20px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.kpi-bottom {
|
||||
.kpi-trend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.kpi-bottom span {
|
||||
.kpi-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
gap: 3px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.kpi-bottom.up span {
|
||||
.kpi-badge.up {
|
||||
background: rgba(239, 68, 68, 0.08);
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.kpi-bottom.down span {
|
||||
.kpi-badge.down {
|
||||
background: rgba(22, 163, 74, 0.08);
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.kpi-bottom small {
|
||||
.kpi-badge .mdi {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.kpi-delta {
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
@@ -313,8 +337,16 @@ const rankedDepartments = computed(() => {
|
||||
.dashboard-card {
|
||||
padding: 20px;
|
||||
transition: box-shadow 200ms ease, transform 200ms ease;
|
||||
animation: dashboardItemIn 560ms var(--ease) both;
|
||||
}
|
||||
|
||||
.top-grid .dashboard-card:nth-child(1) { animation-delay: 80ms; }
|
||||
.top-grid .dashboard-card:nth-child(2) { animation-delay: 150ms; }
|
||||
.top-grid .dashboard-card:nth-child(3) { animation-delay: 220ms; }
|
||||
.bottom-grid .dashboard-card:nth-child(1) { animation-delay: 290ms; }
|
||||
.bottom-grid .dashboard-card:nth-child(2) { animation-delay: 360ms; }
|
||||
.bottom-grid .dashboard-card:nth-child(3) { animation-delay: 430ms; }
|
||||
|
||||
.dashboard-card:hover {
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
|
||||
transform: translateY(-1px);
|
||||
@@ -346,7 +378,7 @@ const rankedDepartments = computed(() => {
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.card-head .pi {
|
||||
.card-head .mdi {
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
vertical-align: 1px;
|
||||
@@ -393,6 +425,8 @@ const rankedDepartments = computed(() => {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
animation: listRowIn 460ms var(--ease) both;
|
||||
animation-delay: var(--delay, 0ms);
|
||||
}
|
||||
|
||||
.reviewer {
|
||||
@@ -472,6 +506,51 @@ const rankedDepartments = computed(() => {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@keyframes dashboardItemIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(12px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes listRowIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes iconPop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(.82);
|
||||
}
|
||||
70% {
|
||||
opacity: 1;
|
||||
transform: scale(1.04);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.kpi-card,
|
||||
.dashboard-card,
|
||||
.bottleneck-row {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1320px) {
|
||||
.kpi-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user