系统管理员首页完成

This commit is contained in:
2026-09-30 10:29:46 +08:00
parent 3f6b16f8be
commit bcfc167905
7 changed files with 333 additions and 277 deletions
+211 -211
View File
@@ -102,221 +102,221 @@ onMounted(() => {
</script>
<template>
<NSpin :show="loading" size="small">
<NSpace vertical :size="16">
<NSpin :show="loading" size="small">
<!-- 我的工作台 -->
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
<div class="flex flex-wrap items-center gap-2">
<p class="text-sm font-bold text-slate-800">全厂变更数据中心</p>
<p class="text-[11px] text-slate-400 mt-[1px]">时间相关数据统一在此查询 · 统计时段</p>
<NSelect :value="dateRange" :options="DATE_RANGE_OPTIONS" size="tiny" class="w-28" :on-update:value="timeChange" />
<NButton ghost size="small" class="ml-auto text-xs" :disabled="btnLoading" @click="exportData">导出</NButton>
<!-- 我的工作台 -->
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
<div class="flex flex-wrap items-center gap-2">
<p class="text-sm font-bold text-slate-800">全厂变更数据中心</p>
<p class="text-[11px] text-slate-400 mt-[1px]">时间相关数据统一在此查询 · 统计时段</p>
<NSelect :value="dateRange" :options="DATE_RANGE_OPTIONS" size="tiny" class="w-28" :on-update:value="timeChange" />
<NButton ghost size="small" class="ml-auto text-xs" :disabled="btnLoading" @click="exportData">导出</NButton>
</div>
</NCard>
<!-- 卡片数据 -->
<NGrid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16"
:style="{
'--theme-color': themeStore.themeColor,
'--theme-bg': getColorWithOpacity(themeStore.themeColor, 0.2),
'--warning-bg': getColorWithOpacity(themeStore.otherColor.warning, 0.2),
'--warning-color': themeStore.otherColor.warning,
'--success-color': themeStore.otherColor.success,
'--success-bg': getColorWithOpacity(themeStore.otherColor.success, 0.2),
'--error-color': themeStore.otherColor.error,
'--error-bg': getColorWithOpacity(themeStore.otherColor.error, 0.2),
}"
>
<NGi>
<div
class="h-100% flex-1 flex items-center gap-3 bg-white px-16px py-12px cursor-pointer transition hover:-translate-y-1"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('changeledger')"
>
<span class="shrink-0 rounded-lg p-2.5 bg-[var(--theme-bg)]">
<Icon icon="akar-icons:file" class="size-24px text-[--theme-color]" />
</span>
<div class="flex flex-col">
<CountTo
:start-value="0"
:end-value="homeData?.summary?.total || 0"
class="text-26px font-600"
/>
<p class="text-[11px] font-semibold text-slate-400">变更总数 · {{ dateLabel }}</p>
</div>
</div>
</NGi>
<NGi>
<div
class="h-100% flex-1 flex items-center gap-3 bg-white px-16px py-12px cursor-pointer transition hover:-translate-y-1"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('changeledger')"
>
<span class="shrink-0 rounded-lg p-2.5 bg-[var(--warning-bg)]">
<Icon icon="lucide:shield-check" class="size-24px text-[--warning-color]" />
</span>
<div class="flex flex-col">
<CountTo
:start-value="0"
:end-value="homeData?.summary?.important_total || 0"
class="text-26px font-600"
/>
<p class="text-[11px] font-semibold text-slate-400">重要变更数量 · {{ dateLabel }} · 占比 {{homeData?.summary?.important_pct || 0}}%</p>
</div>
</div>
</NGi>
<NGi>
<div
class="h-100% flex-1 flex items-center gap-3 bg-white px-16px py-12px cursor-pointer transition hover:-translate-y-1"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('my_highrisk')"
>
<span class="shrink-0 rounded-lg p-2.5 bg-[var(--warning-bg)]">
<Icon icon="lucide:shield-check" class="size-24px text-[--warning-color]" />
</span>
<div class="flex flex-col">
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.risk?.inherent_high || 0"
class="text-26px font-600 text-[var(--error-color)]"
/>
<p class="text-26px font-600 text-[var(--error-color)]"> 高 /</p>
<CountTo
:start-value="0"
:end-value="homeData?.risk?.inherent_mid || 0"
class="text-26px font-600 text-[var(--error-color)]"
/><p class="text-26px font-600 text-[var(--error-color)]"> 中</p>
</div>
<p class="text-[11px] font-semibold text-slate-400">变更新增风险 · 剩余高风险 {{homeData?.risk?.residual_high || 0}} · 剩余中风险 {{homeData?.risk?.residual_mid || 0}}</p>
</div>
</div>
</NGi>
<NGi>
<div
class="h-100% flex-1 flex items-center gap-3 bg-white px-16px py-12px cursor-pointer transition hover:-translate-y-1"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('my_effect')"
>
<span class="shrink-0 rounded-lg p-2.5 bg-[var(--success-bg)]">
<Icon icon="material-symbols:check-circle-outline" class="size-24px text-[--success-color]" />
</span>
<div class="flex flex-col">
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.invest?.total_fee || 0"
:decimals="1"
class="text-26px font-600"
/><p class="text-26px font-600 ml-1">万元</p>
</div>
<p class="text-[11px] font-semibold text-slate-400">验收投入合计 · 已验收 {{homeData?.invest?.total_count || 0}} 起 · 完全达标率 {{homeData?.invest?.full_rate || 0}}%</p>
</div>
</div>
</NGi>
</NGrid>
<NGrid
:x-gap="16" :y-gap="16" responsive="screen" item-responsive
:style="{
'--theme-color':themeStore.themeColor
}"
>
<NGi span="24 s:24 m:24 l:14">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<div class="flex items-center mb-3 gap-2">
<Icon icon="akar-icons:file" class="size-16px text-[--theme-color]" />
<p class="text-13px font-700 text-#64749a">各车间变更数量</p>
<p class="text-11px text-#94a3b8 mt-2px">{{dateLabel}} · 按申请部门</p>
</div>
<ColumnChart v-if="homeData" :data="homeData?.workshop_counts?.items" style="height: 100%;" />
</NCard>
</NGi>
<NGi span="24 s:24 m:24 l:10">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<div class="flex items-center mb-3 gap-2">
<Icon icon="akar-icons:file" class="size-16px text-[--theme-color]" />
<p class="text-13px font-700 text-#64749a">变更类型分布</p>
<p class="text-11px text-#94a3b8 mt-2px">{{dateLabel}} · 工艺 / 设备 / 管理</p>
</div>
<RingChart v-if="homeData" :data="homeData?.type_dist?.items" :range="dateLabel" />
</NCard>
</NGi>
</NGrid>
<NGrid
:x-gap="16" :y-gap="16" responsive="screen" item-responsive
:style="{
'--error-color':themeStore.otherColor.error,
'--warning-color':themeStore.otherColor.warning,
'--success-color':themeStore.otherColor.success,
'--theme-color':themeStore.themeColor
}"
>
<NGi span="24 s:24 m:24 l:10">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<div class="flex items-center mb-3 gap-2">
<Icon icon="lucide:shield-alert" class="size-16px text-[--theme-color]" />
<p class="text-13px font-700 text-#64749a">风险增量与管控</p>
<p class="text-11px text-#94a3b8 mt-2px">{{dateLabel}} · 变更新增风险与管控落实</p>
</div>
<div class="space-y-2.5">
<div class="grid grid-cols-2 gap-2">
<div class="rounded-lg bg-red-50 px-3 py-2">
<div class="text-[11px] text-[var(--error-color)]">新增固有高风险项</div>
<div class="text-xl font-bold text-[var(--error-color)]">{{homeData?.risk?.inherent_high || 0}}</div>
</div>
<div class="rounded-lg bg-amber-50 px-3 py-2">
<div class="text-[11px] text-[var(--warning-color)]">新增中风险项</div>
<div class="text-xl font-bold text-[var(--warning-color)]">{{homeData?.risk?.inherent_mid || 0}}</div>
</div>
<div class="rounded-lg bg-slate-50 px-3 py-2">
<div class="text-[11px] text-slate-500">剩余高风险项</div>
<div class="text-xl font-bold text-slate-800">{{homeData?.risk?.residual_high || 0}}</div>
</div>
<div class="rounded-lg bg-slate-50 px-3 py-2">
<div class="text-[11px] text-slate-500">剩余中风险项</div>
<div class="text-xl font-bold text-slate-800">{{homeData?.risk?.residual_mid || 0}}</div>
</div>
</div>
<!-- <div>
<div class="flex justify-between text-[11px] text-slate-500">
<span>管控措施落实率</span>
<span class="font-semibold text-[var(--success-color)]">96%</span>
</div>
<div class="mt-1 h-2 w-full rounded-full bg-slate-100">
<div class="h-2 rounded-full bg-[var(--success-color)]" style="width: 96%" />
</div>
</div> -->
<div v-if="homeData?.risk?.high_item" @click="jumpTo('details_index', homeData?.risk?.high_item?.change_id)"
class="cursor-pointer w-full rounded-lg border border-red-100 bg-[#FFF2F0] px-3 py-2 text-left transition hover:border-red-300">
<span class="block text-[11px] font-medium text-[var(--error-color)]">剩余高风险 · 措施落实中</span>
<span class="mt-0.5 block truncate text-xs font-medium text-slate-800">{{ homeData?.risk?.high_item?.title }}</span>
<span class="mt-0.5 block text-[11px] text-slate-400">{{ homeData?.risk?.high_item?.change_no }}</span>
</div>
</div>
</NCard>
<!-- 卡片数据 -->
<NGrid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16"
:style="{
'--theme-color': themeStore.themeColor,
'--theme-bg': getColorWithOpacity(themeStore.themeColor, 0.2),
'--warning-bg': getColorWithOpacity(themeStore.otherColor.warning, 0.2),
'--warning-color': themeStore.otherColor.warning,
'--success-color': themeStore.otherColor.success,
'--success-bg': getColorWithOpacity(themeStore.otherColor.success, 0.2),
'--error-color': themeStore.otherColor.error,
'--error-bg': getColorWithOpacity(themeStore.otherColor.error, 0.2),
}"
>
<NGi>
<div
class="h-100% flex-1 flex items-center gap-3 bg-white px-16px py-12px cursor-pointer transition hover:-translate-y-1"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('changeledger')"
>
<span class="shrink-0 rounded-lg p-2.5 bg-[var(--theme-bg)]">
<Icon icon="akar-icons:file" class="size-24px text-[--theme-color]" />
</span>
<div class="flex flex-col">
<CountTo
:start-value="0"
:end-value="homeData?.summary?.total || 0"
class="text-26px font-600"
/>
<p class="text-[11px] font-semibold text-slate-400">变更总数 · {{ dateLabel }}</p>
</div>
</div>
</NGi>
<NGi>
<div
class="h-100% flex-1 flex items-center gap-3 bg-white px-16px py-12px cursor-pointer transition hover:-translate-y-1"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('changeledger')"
>
<span class="shrink-0 rounded-lg p-2.5 bg-[var(--warning-bg)]">
<Icon icon="lucide:shield-check" class="size-24px text-[--warning-color]" />
</span>
<div class="flex flex-col">
<CountTo
:start-value="0"
:end-value="homeData?.summary?.important_total || 0"
class="text-26px font-600"
/>
<p class="text-[11px] font-semibold text-slate-400">重要变更数量 · {{ dateLabel }} · 占比 {{homeData?.summary?.important_pct || 0}}%</p>
</div>
</div>
</NGi>
<NGi>
<div
class="h-100% flex-1 flex items-center gap-3 bg-white px-16px py-12px cursor-pointer transition hover:-translate-y-1"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('my_highrisk')"
>
<span class="shrink-0 rounded-lg p-2.5 bg-[var(--warning-bg)]">
<Icon icon="lucide:shield-check" class="size-24px text-[--warning-color]" />
</span>
<div class="flex flex-col">
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.risk?.inherent_high || 0"
class="text-26px font-600 text-[var(--error-color)]"
/>
<p class="text-26px font-600 text-[var(--error-color)]"> 高 /</p>
<CountTo
:start-value="0"
:end-value="homeData?.risk?.inherent_mid || 0"
class="text-26px font-600 text-[var(--error-color)]"
/><p class="text-26px font-600 text-[var(--error-color)]"> 中</p>
</div>
<p class="text-[11px] font-semibold text-slate-400">变更新增风险 · 剩余高风险 {{homeData?.risk?.residual_high || 0}} · 剩余中风险 {{homeData?.risk?.residual_mid || 0}}</p>
</div>
</div>
</NGi>
<NGi>
<div
class="h-100% flex-1 flex items-center gap-3 bg-white px-16px py-12px cursor-pointer transition hover:-translate-y-1"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('my_effect')"
>
<span class="shrink-0 rounded-lg p-2.5 bg-[var(--success-bg)]">
<Icon icon="material-symbols:check-circle-outline" class="size-24px text-[--success-color]" />
</span>
<div class="flex flex-col">
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.invest?.total_fee || 0"
:decimals="1"
class="text-26px font-600"
/><p class="text-26px font-600 ml-1">万元</p>
</div>
<p class="text-[11px] font-semibold text-slate-400">验收投入合计 · 已验收 {{homeData?.invest?.total_count || 0}} 起 · 完全达标率 {{homeData?.invest?.full_rate || 0}}%</p>
</div>
</div>
</NGi>
</NGrid>
<NGrid
:x-gap="16" :y-gap="16" responsive="screen" item-responsive
:style="{
'--theme-color':themeStore.themeColor
}"
>
<NGi span="24 s:24 m:24 l:14">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<div class="flex items-center mb-3 gap-2">
<Icon icon="akar-icons:file" class="size-16px text-[--theme-color]" />
<p class="text-13px font-700 text-#64749a">各车间变更数量</p>
<p class="text-11px text-#94a3b8 mt-2px">{{dateLabel}} · 按申请部门</p>
</div>
<ColumnChart v-if="homeData" :data="homeData?.workshop_counts?.items" style="height: 100%;" />
</NCard>
</NGi>
<NGi span="24 s:24 m:24 l:10">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<div class="flex items-center mb-3 gap-2">
<Icon icon="akar-icons:file" class="size-16px text-[--theme-color]" />
<p class="text-13px font-700 text-#64749a">变更类型分布</p>
<p class="text-11px text-#94a3b8 mt-2px">{{dateLabel}} · 工艺 / 设备 / 管理</p>
</div>
<RingChart v-if="homeData" :data="homeData?.type_dist?.items" :range="dateLabel" />
</NCard>
</NGi>
</NGrid>
<NGrid
:x-gap="16" :y-gap="16" responsive="screen" item-responsive
:style="{
'--error-color':themeStore.otherColor.error,
'--warning-color':themeStore.otherColor.warning,
'--success-color':themeStore.otherColor.success,
'--theme-color':themeStore.themeColor
}"
>
<NGi span="24 s:24 m:24 l:10">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<div class="flex items-center mb-3 gap-2">
<Icon icon="lucide:shield-alert" class="size-16px text-[--theme-color]" />
<p class="text-13px font-700 text-#64749a">风险增量与管控</p>
<p class="text-11px text-#94a3b8 mt-2px">{{dateLabel}} · 变更新增风险与管控落实</p>
</div>
<div class="space-y-2.5">
<div class="grid grid-cols-2 gap-2">
<div class="rounded-lg bg-red-50 px-3 py-2">
<div class="text-[11px] text-[var(--error-color)]">新增固有高风险项</div>
<div class="text-xl font-bold text-[var(--error-color)]">{{homeData?.risk?.inherent_high || 0}}</div>
</div>
<div class="rounded-lg bg-amber-50 px-3 py-2">
<div class="text-[11px] text-[var(--warning-color)]">新增中风险项</div>
<div class="text-xl font-bold text-[var(--warning-color)]">{{homeData?.risk?.inherent_mid || 0}}</div>
</div>
<div class="rounded-lg bg-slate-50 px-3 py-2">
<div class="text-[11px] text-slate-500">剩余高风险项</div>
<div class="text-xl font-bold text-slate-800">{{homeData?.risk?.residual_high || 0}}</div>
</div>
<div class="rounded-lg bg-slate-50 px-3 py-2">
<div class="text-[11px] text-slate-500">剩余中风险项</div>
<div class="text-xl font-bold text-slate-800">{{homeData?.risk?.residual_mid || 0}}</div>
</div>
</div>
<!-- <div>
<div class="flex justify-between text-[11px] text-slate-500">
<span>管控措施落实率</span>
<span class="font-semibold text-[var(--success-color)]">96%</span>
</div>
<div class="mt-1 h-2 w-full rounded-full bg-slate-100">
<div class="h-2 rounded-full bg-[var(--success-color)]" style="width: 96%" />
</div>
</div> -->
<div v-if="homeData?.risk?.high_item" @click="jumpTo('details_index', homeData?.risk?.high_item?.change_id)"
class="cursor-pointer w-full rounded-lg border border-red-100 bg-[#FFF2F0] px-3 py-2 text-left transition hover:border-red-300">
<span class="block text-[11px] font-medium text-[var(--error-color)]">剩余高风险 · 措施落实中</span>
<span class="mt-0.5 block truncate text-xs font-medium text-slate-800">{{ homeData?.risk?.high_item?.title }}</span>
<span class="mt-0.5 block text-[11px] text-slate-400">{{ homeData?.risk?.high_item?.change_no }}</span>
</div>
</div>
</NCard>
</NGi>
<NGi span="24 s:24 m:24 l:14">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<div class="flex items-center mb-3 gap-2">
<Icon icon="material-symbols:check-circle-outline" class="size-16px text-[--theme-color]" />
<p class="text-13px font-700 text-#64749a">变更投入和效果</p>
<p class="text-11px text-#94a3b8 mt-2px">{{dateLabel}} · 支持查询验收明细</p>
</div>
<RingChart2 v-if="homeData" :data="homeData?.invest?.items" :range="dateLabel" />
<div class="flex items-center justify-between border-slate-200 pt-1.5">
<span class="text-[11px] text-slate-400">投入合计(变更材料和实施费) · 完全达到率 {{homeData?.invest?.full_rate || 0}}%</span>
<span class="text-xs font-bold text-[var(--theme-color)]">{{homeData?.invest?.total_fee || 0}} 万元</span>
</div>
<div @click="jumpTo('my_effect')"
class="cursor-pointer mt-3 flex w-full items-center justify-center gap-1 rounded-lg border border-dashed py-2 text-[11px] text-slate-400 transition hover:border-[#6E93C6] hover:text-[#1D4E9C]">
验收明细(名称 / 日期链 / 费用 / 结论)见左边栏「变更效果跟踪」<ChevronRight class="h-3 w-3" />
</div>
</NCard>
</NGi>
</NGrid>
</NSpin>
</NGi>
<NGi span="24 s:24 m:24 l:14">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<div class="flex items-center mb-3 gap-2">
<Icon icon="material-symbols:check-circle-outline" class="size-16px text-[--theme-color]" />
<p class="text-13px font-700 text-#64749a">变更投入和效果</p>
<p class="text-11px text-#94a3b8 mt-2px">{{dateLabel}} · 支持查询验收明细</p>
</div>
<RingChart2 v-if="homeData" :data="homeData?.invest?.items" :range="dateLabel" />
<div class="flex items-center justify-between border-slate-200 pt-1.5">
<span class="text-[11px] text-slate-400">投入合计(变更材料和实施费) · 完全达到率 {{homeData?.invest?.full_rate || 0}}%</span>
<span class="text-xs font-bold text-[var(--theme-color)]">{{homeData?.invest?.total_fee || 0}} 万元</span>
</div>
<div @click="jumpTo('my_effect')"
class="cursor-pointer mt-3 flex w-full items-center justify-center gap-1 rounded-lg border border-dashed py-2 text-[11px] text-slate-400 transition hover:border-[#6E93C6] hover:text-[#1D4E9C]">
验收明细(名称 / 日期链 / 费用 / 结论)见左边栏「变更效果跟踪」<ChevronRight class="h-3 w-3" />
</div>
</NCard>
</NGi>
</NGrid>
</NSpace>
</NSpin>
</template>
<style scoped></style>