style: 统一行尾符为LF(gitattributes renormalize,无代码变更)

This commit is contained in:
2026-10-12 10:14:11 +08:00
parent 7c5f0dea0b
commit 7b71038949
37 changed files with 12551 additions and 12551 deletions
+342 -342
View File
@@ -1,342 +1,342 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
import RingChart from './modules/ringChart.vue';
import { systemConfigApi } from "@/service/api/system";
import { deptStatisticsApi } from '@/service/api/statistics';
const themeStore = useThemeStore();
const { routerPushByKey } = useRouterPush();
const loading = ref<boolean>(false);
// 变更临期天数
const remindDays = ref<number>(0);
const homeData = ref<any>(null);
// 小时转换天
const hourToDay = (hour: number) => {
return Math.trunc((hour / 24) * 100) / 100;
}
// 跳转页面
const jumpTo = (key: string) => {
routerPushByKey(key as any);
}
// 获取数据
const getData = async () => {
loading.value = true;
const {data,error} = await deptStatisticsApi({
from: null,
to: null,
});
if(!error){
homeData.value = data;
}
loading.value = false;
}
// 获取系统配置
const getConfig = async () => {
const {data,error} = await systemConfigApi();
if(!error){
// 解析 临时变更临期提醒
const remindDaysGroup = data.find((group:{group_key:string}) => group.group_key === 'change_remind');
if (remindDaysGroup) {
remindDaysGroup?.items.forEach((item:any) => {
if(item.item_key === 'temp_due_remind_days'){
remindDays.value = Number(item.value);
}
})
}
}
}
onMounted(() => {
getConfig();
getData();
});
</script>
<template>
<NSpace vertical :size="16">
<!-- 卡片数据 -->
<NGrid cols="s:1 m:4 l:4" responsive="screen" :x-gap="16" :y-gap="16">
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">本车间变更总数</p>
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.kpis?.apply_total || 0"
class="text-24px font-600 text-slate-800"
/>
</div>
<p class="text-12px">近一年 · 汇报口径</p>
</div>
</NGi>
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">在办变更</p>
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.kpis?.unclosed_total || 0"
class="text-26px font-600"
:style="{color:themeStore.themeColor}"
/>
</div>
<p class="text-12px">实时 · 三大阶段分布见下</p>
</div>
</NGi>
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">超期未关闭</p>
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.kpis?.overdue_total || 0"
class="text-26px font-600"
:style="{color:themeStore.otherColor.error}"
/>
</div>
<p class="text-12px">管理考核一票指标</p>
</div>
</NGi>
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">我的审批按时率</p>
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.kpis?.my_timely_rate || 0"
:decimals="1"
class="text-26px font-600"
:style="{color:themeStore.otherColor.success}"
/><p class="text-26px font-600" :style="{color:themeStore.otherColor.success}">%</p>
</div>
<p class="text-12px">考核:目标 ≥90% · 达标</p>
</div>
</NGi>
</NGrid>
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive>
<!-- 我的审批效率 -->
<NGi span="24 s:24 m:24 l:8">
<NCard :bordered="true" size="small" :content-style="{padding: 0}" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<p class=" text-base font-semibold border-b border-slate-100 p-3">我的审批效率(考核口径)</p>
<div class="py-3 px-4">
<div class="space-y-3">
<div class="flex items-center gap-2 rounded-lg bg-slate-50 px-3 py-2.5">
<span class="min-w-0 flex-1 text-sm text-slate-700">平均审批时长</span>
<span class="text-base font-bold" :style="{color:themeStore.themeColor}">{{homeData?.my_eff?.avg_audit_days || 0}} 天</span>
<span class="text-[11px]" :style="{color:themeStore.otherColor.success}">目标 ≤{{hourToDay(homeData?.my_eff?.sign_timely_hours || 0)}} 天</span>
</div>
<div class="flex items-center gap-2 rounded-lg bg-slate-50 px-3 py-2.5">
<span class="min-w-0 flex-1 text-sm text-slate-700">按时审批率</span>
<span class="text-base font-bold" :style="{color:themeStore.themeColor}">{{homeData?.my_eff?.timely_rate || 0}}%</span>
<span class="text-[11px]" :style="{color:themeStore.otherColor.success}">目标 ≥90%</span>
</div>
<div class="flex items-center gap-2 rounded-lg bg-slate-50 px-3 py-2.5">
<span class="min-w-0 flex-1 text-sm text-slate-700">驳回补充说明率</span>
<span class="text-base font-bold" :style="{color:themeStore.themeColor}">{{homeData?.my_eff?.reject_comment_rate || 0}}%</span>
<span class="text-[11px]" :style="{color:themeStore.otherColor.success}">驳回必附整改指引</span>
</div>
<p class="text-[11px] text-slate-400">考核数据来源:本人节点签收 → 签出时长,系统自动统计,不可修改</p></div>
</div>
</NCard>
</NGi>
<!-- 本车间变更状态构成 -->
<NGi span="24 s:24 m:24 l:8">
<NCard :bordered="true" size="small" :content-style="{padding: 0}" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<p class=" text-base font-semibold border-b border-slate-100 p-3">本车间变更状态构成(实时)</p>
<div class="py-3 px-4">
<div class="space-y-2.5">
<div v-for="item in homeData?.status_dist" :key="item.status" class="flex items-center gap-2">
<!-- 审批中 -->
<span v-if="item.status==='APPROVING'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.otherColor.warning}"></span>
<!-- 已批准待投用 -->
<span v-if="item.status==='APPROVED'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.themeColor}"></span>
<!-- 待验收 -->
<span v-if="item.status==='IMPLEMENTED'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.otherColor.success}"></span>
<!-- 待关闭 -->
<span v-if="item.status==='ACCEPTED'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.otherColor.error}"></span>
<!-- 已驳回 -->
<span v-if="item.status==='REJECTED'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.otherColor.error}"></span>
<span class="min-w-0 flex-1 text-sm text-slate-600">{{item.label}}</span>
<span class="text-sm font-bold text-slate-800">{{item.count}}</span>
</div>
<p class="pt-1 text-[11px] text-slate-400">管理价值:一眼定位积压阶段,决定催办方向</p></div>
</div>
</NCard>
</NGi>
<!-- 临时变更监控 -->
<NGi span="24 s:24 m:24 l:8">
<NCard :bordered="true" size="small" :content-style="{padding: 0}" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<p class=" text-base font-semibold border-b border-slate-100 p-3">临时变更监控(管理抓手)</p>
<div class="py-3 px-4">
<div class="space-y-3">
<div class="rounded-lg border px-3 py-2">
<div class="flex items-center gap-2 text-xs">
<span class="h-2 w-2 rounded-full" :style="{ background: themeStore.otherColor.success }"></span>
<span class="font-medium text-slate-700">正常(到期 > {{remindDays}} 天)</span>
<span class="ml-auto font-semibold text-slate-600">{{homeData?.temp_watch?.normal || 0}}</span>
</div>
</div>
<div class="rounded-lg border px-3 py-2">
<div class="flex items-center gap-2 text-xs">
<span class="h-2 w-2 rounded-full" :style="{ background: themeStore.otherColor.warning }"></span>
<span class="font-medium text-slate-700">临期(到期 ≤ {{remindDays}} 天)</span>
<span class="ml-auto font-semibold text-slate-600">{{homeData?.temp_watch?.near || 0}}</span>
</div>
</div>
<div class="rounded-lg border px-3 py-2">
<div class="flex items-center gap-2 text-xs">
<span class="h-2 w-2 rounded-full" :style="{ background: themeStore.otherColor.error }"></span>
<span class="font-medium text-slate-700">已超期</span>
<span class="ml-auto font-semibold text-slate-600">{{homeData?.temp_watch?.overdue || 0}}</span>
</div>
</div>
<NButton size="small" ghost dashed block class="text-xs text-slate-400" @click="jumpTo('changeledger')">前往变更台账查看处置 →</NButton>
</div>
</div>
</NCard>
</NGi>
</NGrid>
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive>
<!-- 变更类型分布 -->
<NGi span="24 s:24 m:24 l:10">
<NCard :bordered="true" size="small" :content-style="{padding: 0}"
:style="{
borderRadius: themeStore.themeRadius + 'px',
height: '100%',
}"
>
<p class=" text-base font-semibold border-b border-slate-100 p-3">变更类型分布(近一年)</p>
<RingChart v-if="homeData" :data="homeData?.type_dist || []" />
</NCard>
</NGi>
<!-- 风险增量与管控 -->
<NGi span="24 s:24 m:24 l:14">
<NCard
:bordered="true"
size="small"
:content-style="{padding: 0}"
:style="{
borderRadius: themeStore.themeRadius + 'px',
height: '100%',
}"
>
<p class=" text-base font-semibold border-b border-slate-100 p-3">风险增量与管控(近一年 · 本车间变更新增风险与管控落实)</p>
<div class="py-3 px-4 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-slate-500">新增固有高风险项</div>
<div class="text-xl font-bold" :style="{color:themeStore.otherColor.error}">{{homeData?.risk?.new_high || 0}}</div>
</div>
<div class="rounded-lg bg-amber-50 px-3 py-2">
<div class="text-[11px] text-slate-500">新增中风险项</div>
<div class="text-xl font-bold" :style="{color:themeStore.otherColor.warning}">{{homeData?.risk?.new_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" :style="{color:themeStore.otherColor.error}">{{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" :style="{color:themeStore.otherColor.warning}">{{homeData?.risk?.residual_mid || 0}}</div>
</div>
</div>
<!-- <div class="flex items-center gap-2 text-slate-500">
<span class="text-xs">管控措施落实率</span>
<div class="h-2 flex-1 rounded-full bg-slate-100">
<div class="h-2 rounded-full" :style="{ background: themeStore.otherColor.success }" style="width: 96%"></div>
</div>
<span class="font-semibold" :style="{color:themeStore.otherColor.success}">96%</span>
</div> -->
<p class="mt-1.5 text-[11px] text-slate-400">高风险场景明细见「高风险」页(按部门筛选一车间)</p>
</div>
</NCard>
</NGi>
</NGrid>
<!-- 变更投入和效果 -->
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive>
<NGi span="24 s:24 m:24 l:24">
<NCard :bordered="true" size="small" :content-style="{padding: 0}"
:style="{
borderRadius: themeStore.themeRadius + 'px',
height: '100%',
'--error-color': themeStore.otherColor.error,
'--success-color': themeStore.otherColor.success,
}"
>
<div class="flex items-center p-3 border-b border-slate-100">
<p class=" text-base font-semibold">变更投入和效果(近一年 · 已验收变更)</p>
<p class="text-11px text-slate-400 mt-3px">费用取验收时填报的「变更材料和实施费」;完整明细见操作台「变更投入和效果」</p>
<p class="text-[11px] text-slate-400 ml-auto">
已验收 <b class="text-slate-700">{{homeData?.invest?.total || 0}}</b> 起 ·
投入合计 <b :style="{color:themeStore.themeColor}">{{homeData?.invest?.total_cost || 0}}</b> 万元 ·
完全达到率 <b :style="{color:themeStore.otherColor.success}">{{homeData?.invest?.full_rate || 0}}%</b>
</p>
</div>
<div class="py-3 px-4">
<NTable size="small">
<thead>
<tr>
<th>变更名称 / 编号</th>
<th>申请日期</th>
<th>投用日期</th>
<th>验收日期</th>
<th>费用(万元)</th>
<th>验收结论</th>
</tr>
</thead>
<tbody>
<tr v-if="homeData?.invest?.items?.length===0">
<td colspan="6">
<div class="h-100% flex items-center justify-center py-3">
<NEmpty description="暂无数据" class="!text-xs"></NEmpty>
</div>
</td>
</tr>
<tr v-for="row in homeData?.invest?.items || []" :key="row.change_id">
<td>
<p>{{row.title}}</p>
<span class="text-11px text-slate-400">{{row.change_no}}</span>
</td>
<td class="text-xs">{{row?.apply_time?.slice(0,10)}}</td>
<td class="text-xs">{{row?.use_time?.slice(0,10)}}</td>
<td class="text-xs">{{row?.accept_time?.slice(0,10)}}</td>
<td class="text-xs"><span class="font-600">{{row?.cost || 0}}</span></td>
<td>
<NTag v-if="row.result_status===3" size="small" type="success">{{row.result_label}}</NTag>
<NTag v-if="row.result_status===2" size="small" type="warning">{{row.result_label}}</NTag>
<NTag v-if="row.result_status===1" size="small" type="error">{{row.result_label}}</NTag>
</td>
</tr>
</tbody>
</NTable>
</div>
</NCard>
</NGi>
</NGrid>
</NSpace>
</template>
<style scoped></style>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
import RingChart from './modules/ringChart.vue';
import { systemConfigApi } from "@/service/api/system";
import { deptStatisticsApi } from '@/service/api/statistics';
const themeStore = useThemeStore();
const { routerPushByKey } = useRouterPush();
const loading = ref<boolean>(false);
// 变更临期天数
const remindDays = ref<number>(0);
const homeData = ref<any>(null);
// 小时转换天
const hourToDay = (hour: number) => {
return Math.trunc((hour / 24) * 100) / 100;
}
// 跳转页面
const jumpTo = (key: string) => {
routerPushByKey(key as any);
}
// 获取数据
const getData = async () => {
loading.value = true;
const {data,error} = await deptStatisticsApi({
from: null,
to: null,
});
if(!error){
homeData.value = data;
}
loading.value = false;
}
// 获取系统配置
const getConfig = async () => {
const {data,error} = await systemConfigApi();
if(!error){
// 解析 临时变更临期提醒
const remindDaysGroup = data.find((group:{group_key:string}) => group.group_key === 'change_remind');
if (remindDaysGroup) {
remindDaysGroup?.items.forEach((item:any) => {
if(item.item_key === 'temp_due_remind_days'){
remindDays.value = Number(item.value);
}
})
}
}
}
onMounted(() => {
getConfig();
getData();
});
</script>
<template>
<NSpace vertical :size="16">
<!-- 卡片数据 -->
<NGrid cols="s:1 m:4 l:4" responsive="screen" :x-gap="16" :y-gap="16">
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">本车间变更总数</p>
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.kpis?.apply_total || 0"
class="text-24px font-600 text-slate-800"
/>
</div>
<p class="text-12px">近一年 · 汇报口径</p>
</div>
</NGi>
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">在办变更</p>
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.kpis?.unclosed_total || 0"
class="text-26px font-600"
:style="{color:themeStore.themeColor}"
/>
</div>
<p class="text-12px">实时 · 三大阶段分布见下</p>
</div>
</NGi>
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">超期未关闭</p>
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.kpis?.overdue_total || 0"
class="text-26px font-600"
:style="{color:themeStore.otherColor.error}"
/>
</div>
<p class="text-12px">管理考核一票指标</p>
</div>
</NGi>
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">我的审批按时率</p>
<div class="flex items-center">
<CountTo
:start-value="0"
:end-value="homeData?.kpis?.my_timely_rate || 0"
:decimals="1"
class="text-26px font-600"
:style="{color:themeStore.otherColor.success}"
/><p class="text-26px font-600" :style="{color:themeStore.otherColor.success}">%</p>
</div>
<p class="text-12px">考核:目标 ≥90% · 达标</p>
</div>
</NGi>
</NGrid>
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive>
<!-- 我的审批效率 -->
<NGi span="24 s:24 m:24 l:8">
<NCard :bordered="true" size="small" :content-style="{padding: 0}" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<p class=" text-base font-semibold border-b border-slate-100 p-3">我的审批效率(考核口径)</p>
<div class="py-3 px-4">
<div class="space-y-3">
<div class="flex items-center gap-2 rounded-lg bg-slate-50 px-3 py-2.5">
<span class="min-w-0 flex-1 text-sm text-slate-700">平均审批时长</span>
<span class="text-base font-bold" :style="{color:themeStore.themeColor}">{{homeData?.my_eff?.avg_audit_days || 0}} 天</span>
<span class="text-[11px]" :style="{color:themeStore.otherColor.success}">目标 ≤{{hourToDay(homeData?.my_eff?.sign_timely_hours || 0)}} 天</span>
</div>
<div class="flex items-center gap-2 rounded-lg bg-slate-50 px-3 py-2.5">
<span class="min-w-0 flex-1 text-sm text-slate-700">按时审批率</span>
<span class="text-base font-bold" :style="{color:themeStore.themeColor}">{{homeData?.my_eff?.timely_rate || 0}}%</span>
<span class="text-[11px]" :style="{color:themeStore.otherColor.success}">目标 ≥90%</span>
</div>
<div class="flex items-center gap-2 rounded-lg bg-slate-50 px-3 py-2.5">
<span class="min-w-0 flex-1 text-sm text-slate-700">驳回补充说明率</span>
<span class="text-base font-bold" :style="{color:themeStore.themeColor}">{{homeData?.my_eff?.reject_comment_rate || 0}}%</span>
<span class="text-[11px]" :style="{color:themeStore.otherColor.success}">驳回必附整改指引</span>
</div>
<p class="text-[11px] text-slate-400">考核数据来源:本人节点签收 → 签出时长,系统自动统计,不可修改</p></div>
</div>
</NCard>
</NGi>
<!-- 本车间变更状态构成 -->
<NGi span="24 s:24 m:24 l:8">
<NCard :bordered="true" size="small" :content-style="{padding: 0}" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<p class=" text-base font-semibold border-b border-slate-100 p-3">本车间变更状态构成(实时)</p>
<div class="py-3 px-4">
<div class="space-y-2.5">
<div v-for="item in homeData?.status_dist" :key="item.status" class="flex items-center gap-2">
<!-- 审批中 -->
<span v-if="item.status==='APPROVING'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.otherColor.warning}"></span>
<!-- 已批准待投用 -->
<span v-if="item.status==='APPROVED'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.themeColor}"></span>
<!-- 待验收 -->
<span v-if="item.status==='IMPLEMENTED'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.otherColor.success}"></span>
<!-- 待关闭 -->
<span v-if="item.status==='ACCEPTED'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.otherColor.error}"></span>
<!-- 已驳回 -->
<span v-if="item.status==='REJECTED'" class="h-2.5 w-2.5 shrink-0 rounded-sm" :style="{background:themeStore.otherColor.error}"></span>
<span class="min-w-0 flex-1 text-sm text-slate-600">{{item.label}}</span>
<span class="text-sm font-bold text-slate-800">{{item.count}}</span>
</div>
<p class="pt-1 text-[11px] text-slate-400">管理价值:一眼定位积压阶段,决定催办方向</p></div>
</div>
</NCard>
</NGi>
<!-- 临时变更监控 -->
<NGi span="24 s:24 m:24 l:8">
<NCard :bordered="true" size="small" :content-style="{padding: 0}" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
<p class=" text-base font-semibold border-b border-slate-100 p-3">临时变更监控(管理抓手)</p>
<div class="py-3 px-4">
<div class="space-y-3">
<div class="rounded-lg border px-3 py-2">
<div class="flex items-center gap-2 text-xs">
<span class="h-2 w-2 rounded-full" :style="{ background: themeStore.otherColor.success }"></span>
<span class="font-medium text-slate-700">正常(到期 > {{remindDays}} 天)</span>
<span class="ml-auto font-semibold text-slate-600">{{homeData?.temp_watch?.normal || 0}}</span>
</div>
</div>
<div class="rounded-lg border px-3 py-2">
<div class="flex items-center gap-2 text-xs">
<span class="h-2 w-2 rounded-full" :style="{ background: themeStore.otherColor.warning }"></span>
<span class="font-medium text-slate-700">临期(到期 ≤ {{remindDays}} 天)</span>
<span class="ml-auto font-semibold text-slate-600">{{homeData?.temp_watch?.near || 0}}</span>
</div>
</div>
<div class="rounded-lg border px-3 py-2">
<div class="flex items-center gap-2 text-xs">
<span class="h-2 w-2 rounded-full" :style="{ background: themeStore.otherColor.error }"></span>
<span class="font-medium text-slate-700">已超期</span>
<span class="ml-auto font-semibold text-slate-600">{{homeData?.temp_watch?.overdue || 0}}</span>
</div>
</div>
<NButton size="small" ghost dashed block class="text-xs text-slate-400" @click="jumpTo('changeledger')">前往变更台账查看处置 →</NButton>
</div>
</div>
</NCard>
</NGi>
</NGrid>
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive>
<!-- 变更类型分布 -->
<NGi span="24 s:24 m:24 l:10">
<NCard :bordered="true" size="small" :content-style="{padding: 0}"
:style="{
borderRadius: themeStore.themeRadius + 'px',
height: '100%',
}"
>
<p class=" text-base font-semibold border-b border-slate-100 p-3">变更类型分布(近一年)</p>
<RingChart v-if="homeData" :data="homeData?.type_dist || []" />
</NCard>
</NGi>
<!-- 风险增量与管控 -->
<NGi span="24 s:24 m:24 l:14">
<NCard
:bordered="true"
size="small"
:content-style="{padding: 0}"
:style="{
borderRadius: themeStore.themeRadius + 'px',
height: '100%',
}"
>
<p class=" text-base font-semibold border-b border-slate-100 p-3">风险增量与管控(近一年 · 本车间变更新增风险与管控落实)</p>
<div class="py-3 px-4 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-slate-500">新增固有高风险项</div>
<div class="text-xl font-bold" :style="{color:themeStore.otherColor.error}">{{homeData?.risk?.new_high || 0}}</div>
</div>
<div class="rounded-lg bg-amber-50 px-3 py-2">
<div class="text-[11px] text-slate-500">新增中风险项</div>
<div class="text-xl font-bold" :style="{color:themeStore.otherColor.warning}">{{homeData?.risk?.new_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" :style="{color:themeStore.otherColor.error}">{{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" :style="{color:themeStore.otherColor.warning}">{{homeData?.risk?.residual_mid || 0}}</div>
</div>
</div>
<!-- <div class="flex items-center gap-2 text-slate-500">
<span class="text-xs">管控措施落实率</span>
<div class="h-2 flex-1 rounded-full bg-slate-100">
<div class="h-2 rounded-full" :style="{ background: themeStore.otherColor.success }" style="width: 96%"></div>
</div>
<span class="font-semibold" :style="{color:themeStore.otherColor.success}">96%</span>
</div> -->
<p class="mt-1.5 text-[11px] text-slate-400">高风险场景明细见「高风险」页(按部门筛选一车间)</p>
</div>
</NCard>
</NGi>
</NGrid>
<!-- 变更投入和效果 -->
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive>
<NGi span="24 s:24 m:24 l:24">
<NCard :bordered="true" size="small" :content-style="{padding: 0}"
:style="{
borderRadius: themeStore.themeRadius + 'px',
height: '100%',
'--error-color': themeStore.otherColor.error,
'--success-color': themeStore.otherColor.success,
}"
>
<div class="flex items-center p-3 border-b border-slate-100">
<p class=" text-base font-semibold">变更投入和效果(近一年 · 已验收变更)</p>
<p class="text-11px text-slate-400 mt-3px">费用取验收时填报的「变更材料和实施费」;完整明细见操作台「变更投入和效果」</p>
<p class="text-[11px] text-slate-400 ml-auto">
已验收 <b class="text-slate-700">{{homeData?.invest?.total || 0}}</b> 起 ·
投入合计 <b :style="{color:themeStore.themeColor}">{{homeData?.invest?.total_cost || 0}}</b> 万元 ·
完全达到率 <b :style="{color:themeStore.otherColor.success}">{{homeData?.invest?.full_rate || 0}}%</b>
</p>
</div>
<div class="py-3 px-4">
<NTable size="small">
<thead>
<tr>
<th>变更名称 / 编号</th>
<th>申请日期</th>
<th>投用日期</th>
<th>验收日期</th>
<th>费用(万元)</th>
<th>验收结论</th>
</tr>
</thead>
<tbody>
<tr v-if="homeData?.invest?.items?.length===0">
<td colspan="6">
<div class="h-100% flex items-center justify-center py-3">
<NEmpty description="暂无数据" class="!text-xs"></NEmpty>
</div>
</td>
</tr>
<tr v-for="row in homeData?.invest?.items || []" :key="row.change_id">
<td>
<p>{{row.title}}</p>
<span class="text-11px text-slate-400">{{row.change_no}}</span>
</td>
<td class="text-xs">{{row?.apply_time?.slice(0,10)}}</td>
<td class="text-xs">{{row?.use_time?.slice(0,10)}}</td>
<td class="text-xs">{{row?.accept_time?.slice(0,10)}}</td>
<td class="text-xs"><span class="font-600">{{row?.cost || 0}}</span></td>
<td>
<NTag v-if="row.result_status===3" size="small" type="success">{{row.result_label}}</NTag>
<NTag v-if="row.result_status===2" size="small" type="warning">{{row.result_label}}</NTag>
<NTag v-if="row.result_status===1" size="small" type="error">{{row.result_label}}</NTag>
</td>
</tr>
</tbody>
</NTable>
</div>
</NCard>
</NGi>
</NGrid>
</NSpace>
</template>
<style scoped></style>