@@ -1,246 +1,333 @@
<!-- 本部门变更统计 -- >
< script setup lang = "ts" >
< script setup lang = "ts" >
import { ref , computed } from 'vue' ;
import { ref , computed , onMounted } from 'vue' ;
import { useAppStore } from '@/store/modules/app' ;
import { Icon } from '@iconify/vue'
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme' ;
import { useThemeStore } from '@/store/modules/theme' ;
import { CHANGES , TEMP _WATCH , countOpenStatus } from "@/typings/model " ;
import { formatTimestamp , getTimeRangeStamp } from "@/utils/common " ;
import { formatTimestamp } from "@/utils/common" ;
import RingChart from './modules/ringChart.vue' ;
import RingChart from './modules/ringChart.vue' ;
import PieChart from './modules/pieChart.vue' ;
import PieChart from './modules/pieChart.vue' ;
import PieChart2 from './modules/pieChart2.vue' ;
import PieChart2 from './modules/pieChart2.vue' ;
import { systemConfigApi } from "@/service/api/system" ;
import { departmentStatisticsApi } from "@/service/api/statistics" ;
const appStore = useAppStore ( ) ;
const themeStore = useThemeStore ( ) ;
const themeStore = useThemeStore ( ) ;
const gap = computed ( ( ) => ( appStore . isMobile ? 0 : 16 ) ) ;
const P _PERIODS = [ "近一年" , "近半年" , "近三个月" , "近一个月" , "自定义" ] as const ;
const loading = ref < boolean > ( false ) ;
const period = ref < ( typeof P _PERIODS ) [ number ] > ( "近一年" ) ;
// 变更临期天数
const from = ref < any > ( null ) ;
const remindDays = ref < number > ( 0 ) ;
const to = ref < any > ( null ) ;
// 本部门变更统计
const rangeText = computed ( ( ) => period . value === "自定义"
const deptStatistics = ref < any > ( null ) ;
? ( from . value || to . value ? ` ${ formatTimestamp ( from . value , 'yyyy-MM-dd' ) || "开始" } ~ ${ formatTimestamp ( to . value , 'yyyy-MM-dd' ) || "至今" } ` : "自定义(未选日期)" )
// 变更类型列表
: period . value ) ;
const typeList = ref < any > ( [ ] ) ;
// 时间范围
const period = ref < string > ( "year" ) ;
// 开始时间
const from = ref < number | null > ( getTimeRangeStamp ( period . value ) ? . from ? ? null ) ;
// 结束时间
const to = ref < number | null > ( getTimeRangeStamp ( period . value ) ? . to ? ? null ) ;
// 时间范围选项
const TIME _RANGE = [
{ label : "近一年" , value : "year" } ,
{ label : "近半年" , value : "half-year" } ,
{ label : "近三个月" , value : "quarter" } ,
{ label : "近一个月" , value : "month" } ,
{ label : "自定义" , value : "custom" }
]
// 尚未关闭总数
const openTotal = computed ( ( ) => {
return deptStatistics . value ? . open _status _counts ? . reduce ( ( sum : number , item : any ) => sum + item . count , 0 ) || 0 ;
} ) ;
// 验收达标总数
const acceptTotal = computed ( ( ) => {
return deptStatistics . value ? . accept _dist ? . reduce ( ( sum : number , item : any ) => sum + item . count , 0 ) || 0 ;
} ) ;
// 临时变更到期提醒情况
const tempTotal = computed ( ( ) => {
return ( deptStatistics . value ? . kpis ? . unclosed _total || 0 ) + ( deptStatistics . value ? . kpis ? . near _overdue _total || 0 ) + ( deptStatistics . value ? . kpis ? . overdue _total || 0 ) ;
} ) ;
const deptOpenStatus = countOpenStatus ( CHANGES ) ;
// 时间范围切换
const DEPT _ACCEPT = [
const timeChange = ( val : string ) => {
{ name : "完全达到" , value : 13 } ,
period . value = val ;
{ name : "基本达到" , value : 4 } ,
if ( val === 'custom' ) {
{ name : "未达到" , value : 2 } ,
from . value = null ;
{ name : "处置关闭(未实施 / 未投用)" , value : 3 } ,
to . value = null ;
] ;
return
const acceptTotal = DEPT _ACCEPT . reduce ( ( a , b ) => a + b . value , 0 ) ;
} else {
const ACCEPT _COLORS = [ "#7CC242" , "#F5A623" , "#E15555" , "#94A3B8" , "#64748B" ] ;
from . value = getTimeRangeStamp ( val ) ? . from ? ? null ;
const DEPT _TYPE = [
to . value = getTimeRangeStamp ( val ) ? . to ? ? null ;
{ name : "工艺" , value : 11 } , { name : "设备" , value : 12 } , { name : "管理" , value : 5 } ,
}
] ;
getData ( ) ;
const DEPT _DUR = [
}
{ name : "永久" , value : 21 } , { name : "临时" , value : 7 } ,
// 自定义时间选择
] ;
const customTimeChange = ( type : string , val : any ) => {
const DUR _COLORS = [ "#1D4E9C" , "#FAAD14" ] ;
if ( type === 'start' ) {
const PIE _COLORS = [ "#1D4E9C" , "#7CC242" , "#F5A623" ] ;
from . value = val ;
const APPROVER _TIME = [
} else {
{ name : "李主任(部门审核)" , days : 1.2 } ,
to . value = val ;
{ name : "王仪表(仪表会签)" , days : 0.8 } ,
}
{ name : "周设备(设备会签)" , days : 1.5 } ,
if ( from . value && to . value ) {
{ name : "王海燕(安全审查)" , days : 2.1 } ,
if ( from . value > to . value ) {
{ name : "刘副总(公司终批)" , days : 1.9 } ,
return window . $message ? . warning ( '开始时间不能大于结束时间' ) ;
] ;
}
const openTotal = deptOpenStatus . reduce ( ( a , b ) => a + b . n , 0 ) ;
getData ( ) ;
const tempTotal = TEMP _WATCH . reduce ( ( a , b ) => a + b . n , 0 ) ;
}
}
// 获取数据
const getData = async ( ) => {
loading . value = true ;
const { data , error } = await departmentStatisticsApi ( {
from : formatTimestamp ( from . value , 'yyyy-MM-dd' ) || null ,
to : formatTimestamp ( to . value , 'yyyy-MM-dd' ) || null
} ) ;
if ( ! error ) {
console . log ( data ) ;
deptStatistics . 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 ) ;
}
} )
}
// 解析 变更类型设置
const changeTypeGroup = data . find ( ( group : { group _key : string } ) => group . group _key === 'change_type' ) ;
if ( changeTypeGroup ) {
typeList . value = changeTypeGroup . items ;
}
}
}
onMounted ( ( ) => {
getConfig ( ) ;
getData ( ) ;
} )
< / script >
< / script >
< template >
< template >
< NSpace vertical :size = "16" >
< NSpace vertical :size = "16" >
<!-- 本部门变更统计 -- >
< NSpin :show = "loading" size = "small" >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px' } ">
< div class = "space-y-3.5 ">
< div class = "flex flex-wrap items-center justify-between ">
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px' } ">
< div class = "flex-y-center " >
< div class = "flex flex-wrap items-center justify-between " >
< span class = "flex items -center gap-1.5 text-sm font-semibold text-slate-700 " >
< div class = "flex-y -center gap-3 " >
< Icon icon = "akar-icons:calendar" class = "size-14px" : style = "{color: themeStore.themeColor}" / >
< span class = "flex items-center gap-1.5 text-sm font-semibold text-slate-700" >
< span > 本部门变更统计 < / span >
< Icon icon = "akar-icons:calendar" class = "size-14px" : style = "{color: themeStore.themeColor}" / >
< span class = "text-xs font-normal text-slate-400" > 一车间 · 本部门口径 < / span >
< span > 本部门变更统计 < / span >
< / span >
< span class = "text-xs font-normal text-slate-400" > 一车间 · 本部门口径 < / span >
< div class = "flex flex-wrap items-center gap-1.5 min-h-28px" >
< span class = "text-xs text-slate-400" > 时间段 < / span >
< NButton size = "tiny" round v-for = "p in P_PERIODS" :key="p" @click="period = p"
: ghost = "period === p ? false : true"
: type = "period === p ? 'primary' : 'default'"
> { { p } } < / NButton >
< span v-if = "period === '自定义'" class="flex items-center gap-1" >
< NDatePicker v -model :value = "from" type = "date" size = "small" class = "w-32" / >
< span class = "text-xs text-slate-400" > 至 < / span >
< NDatePicker v -model :value = "to" type = "date" size = "small" class = "w-32" / >
< / span >
< / span >
< div class = "flex flex-wrap items-center gap-1.5 min-h-28px" >
< span class = "text-xs text-slate-400" > 时间段 < / span >
< NButton size = "tiny" round v-for = "p in TIME_RANGE" :key="p.value" @click="timeChange(p.value)"
: ghost = "period === p.value ? false : true"
: type = "period === p.value ? 'primary' : 'default'"
> { { p . label } } < / NButton >
< span v-if = "period === 'custom'" class="flex items-center gap-1" >
< NDatePicker :value = "from" type = "date" size = "small" class = "w-32" : on -update : value = "(v) => customTimeChange('start',v)" / >
< span class = "text-xs text-slate-400" > 至 < / span >
< NDatePicker :value = "to" type = "date" size = "small" class = "w-32" : on -update : value = "(v) => customTimeChange('end',v)" / >
< / span >
< / div >
< / div >
< / div >
< / div >
< / div >
< span class = "text-[11px] text-slate-400" > 统计口径 : { { rangeText } } < / span >
< / NCard >
< / div >
<!-- 卡片数据 -- >
< / NCard >
< NGrid cols = "s:1 m:5 l:5" responsive = "screen" :x-gap = "16" :y-gap = "16" >
<!-- 卡片数据 -- >
< NGi >
< NGrid cols = "s:1 m:5 l:5" responsive = "screen" :x-gap = "gap" :y-gap = "16" >
< div
< NGi >
class = "flex-1 bg-white px-16px py-12px text-[#94a3b8]"
< div
: style = "{ borderRadius: themeStore.themeRadius + 'px' }"
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" >
< p class = "text-12px font-600" > 申请变更数量 < / p >
< CountTo
< div class = "flex items-center" >
:start-value = "0"
< CountTo
: end -value = " deptStatistics ? .kpis ? .apply_total | | 0 "
:start-value = "0 "
class = "text-26px font-600 text-[#333] "
:end-value = "28"
/ > < p class = "mt-10px ml-5px" > 项 < / p >
class = "text-26px font-600 text-[#333]"
< / div >
/ > < p class = "mt-10px ml-5px" > 项 < / p >
< p class = "text-12px" > 永久 { { deptStatistics ? . kpis ? . permanent _total || 0 } } 项 · 临时 { { deptStatistics ? . kpis ? . temp _total || 0 } } 项 < / p >
< / div >
< / div >
< p class = "text-12px" > 永久 21 · 临时 7 < / p >
< / NGi >
< / div >
< NGi >
< / NGi >
< div
< NGi >
class = "flex-1 bg-white px-16px py-12px text-[#94a3b8]"
< div
: style = "{ borderRadius: themeStore.themeRadius + 'px' }"
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" >
< p class = "text-12px font-600" > 重要变更数量 < / p >
< CountTo
< div class = "flex items-center" >
:start-value = "0"
< CountTo
: end -value = " deptStatistics ? .kpis ? .important_total | | 0 "
:start-value = "0 "
class = "text-26px font-600 text-[#333] "
:end-value = "7"
/ > < p class = "mt-10px ml-5px" > 项 < / p >
class = "text-26px font-600 text-[#333]"
< / div >
/ > < p class = "mt-10px ml-5px" > 项 < / p >
< p class = "text-12px" > 占比 { { deptStatistics ? . kpis ? . important _pct || 0 } } % · 均含公司终批 < / p >
< / div >
< / div >
< p class = "text-12px" > 占比 25 % · 均含公司终批 < / p >
< / NGi >
< / div >
< NGi >
< / NGi >
< div
< NGi >
class = "flex-1 bg-white px-16px py-12px text-[#94a3b8]"
< div
: style = "{ borderRadius: themeStore.themeRadius + 'px' }"
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" >
< p class = "text-12px font-600" > 未关闭变更数量 < / p >
< CountTo
< div class = "flex items-center" >
:start-value = "0"
< CountTo
: end -value = " deptStatistics ? .kpis ? .unclosed_total | | 0 "
:start-value = "0 "
class = "text-26px font-600 text-[#E15555] "
:end-value = "6"
/ > < p class = "mt-10px ml-5px" > 项 < / p >
class = "text-26px font-600 text-[#E15555]"
< / div >
/ > < p class = "mt-10px ml-5px" > 项 < / p >
< p class = "text-12px" > 其中超期 { { deptStatistics ? . kpis ? . overdue _total || 0 } } 项 · 临期 { { deptStatistics ? . kpis ? . near _overdue _total || 0 } } 项 < / p >
< / div >
< / div >
< p class = "text-12px" > 其中超期 1 项 · 临期 1 项 < / p >
< / NGi >
< / div >
< NGi >
< / NGi >
< div
< NGi >
class = "flex-1 bg-white px-16px py-12px text-[#94a3b8]"
< div
: style = "{ borderRadius: themeStore.themeRadius + 'px' }"
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" >
< p class = "text-12px font-600" > 平均审批通过时长 < / p >
< CountTo
< div class = "flex items-center" >
:start-value = "0"
< CountTo
: end -value = " deptStatistics ? .kpis ? .avg_approval_days | | 0 "
:start-value = "0 "
:decimals = "1 "
:end-value = "7.4 "
class = "text-26px font-600 text-[#333] "
:decimals = "1"
/ >
class = "text-26px font-600 text-[#333]"
< p class = "mt-10px ml-5px" > 天 < / p >
/ >
< / div >
< p class = "mt-10px ml-5px" > 天 < / p >
< p class = "text-12px" > 优于全厂均值 8.2 天 < / p >
< / div >
< / div >
< p class = "text-12px" > 优于全厂均值 8.2 天 < / p >
< / NGi >
< / div >
< NGi >
< / NGi >
< div
< NGi >
class = "flex-1 bg-white px-16px py-12px text-[#94a3b8]"
< div
: style = "{ borderRadius: themeStore.themeRadius + 'px' }"
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" >
< p class = "text-12px font-600" > 申请驳回率 < / p >
< CountTo
< div class = "flex items-center" >
:start-value = "0"
< CountTo
: end -value = " deptStatistics ? .kpis ? .reject_rate | | 0 "
:start-value = "0 "
:decimals = "1 "
:end-value = "6.1 "
class = "text-26px font-600 text-[#333] "
:decimals = "1"
/ > < p class = "mt-10px ml-5px" > % < / p >
class = "text-26px font-600 text-[#333]"
< / div >
/ > < p class = "mt-10px ml-5px" > % < / p >
< p class = "text-12px" > { { deptStatistics ? . kpis ? . reject _count || 0 } } 项被驳回 · 均已重新提交 < / p >
< / div >
< / div >
< p class = "text-12px" > 2 项被驳回 · 均已重新提交 < / p >
< / NGi >
< / div >
< / NGrid >
< / NGi >
< / NGrid >
< NGrid :x-gap = "gap " :y-gap = "16" responsive = "screen" item -responsive >
< NGrid :x-gap = "16 " :y-gap = "16" responsive = "screen" item -responsive >
< NGi span = "24 s:24 m:24 l:6" >
< NGi span = "24 s:24 m:24 l:6" >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }" >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }" >
< p class = " text-base font-semibold border-b border-slate-100 pb-3 mb-4" > 尚未关闭的变更所处的状态数量 ( 共 { { openTotal } } 项 ) < / p >
< p class = " text-base font-semibold border-b border-slate-100 pb-3 mb-4" > 尚未关闭的变更所处的状态数量 ( 共 { { openTotal } } 项 ) < / p >
< div class = "space-y-3" >
< div class = "space-y-3" >
< div v-for = "s in deptO penS tatus" :key="s.name " >
< div v-for = "s in deptStatistics?.o pen_s tatus_counts " :key="s.status " >
< div class = "flex items-center gap-2 text-xs" >
< div class = "flex items-center gap-2 text-xs" >
< span class = "min-w-0 flex-1 text-slate-700" > { { s . name } } < / span >
< span class = "min-w-0 flex-1 text-slate-700" > { { s . label } } < / span >
< span class = "shrink-0 font-semibold text-slate-600" > { { s . n } } 项 < / span >
< span class = "shrink-0 font-semibold text-slate-600" > { { s . count } } 项 < / span >
< / div >
< div class = "mt-1 h-1.5 w-full overflow-hidden rounded-full bg-slate-100" >
<!-- 审批中 -- >
< div v-if = "s.status === 'APPROVING'" class="h-full rounded-full" :style="{ width: `${openTotal ? (s.count / openTotal) * 100 : 0}%`, background: themeStore.otherColor.warning }" > < / div >
< ! - - 已驳回 - - >
< div v-if = "s.status === 'REJECTED'" class="h-full rounded-full" :style="{ width: `${openTotal ? (s.count / openTotal) * 100 : 0}%`, background: themeStore.otherColor.error }" > < / div >
< ! - - 已批准待投用 - - >
< div v-if = "s.status === 'APPROVED'" class="h-full rounded-full" :style="{ width: `${openTotal ? (s.count / openTotal) * 100 : 0}%`, background: themeStore.themeColor }" > < / div >
< ! - - 待验收 - - >
< div v-if = "s.status === 'IMPLEMENTED'" class="h-full rounded-full" :style="{ width: `${openTotal ? (s.count / openTotal) * 100 : 0}%`, background: themeStore.otherColor.success }" > < / div >
< ! - - 待关闭 - - >
< div v-if = "s.status === 'ACCEPTED'" class="h-full rounded-full" :style="{ width: `${openTotal ? (s.count / openTotal) * 100 : 0}%`, background: themeStore.otherColor.error }" > < / div >
< / div >
< / div >
< / div >
< div class = "mt-1 h-1.5 w-full overflow-hidden rounded-full bg -slate-1 00" >
< div class = "text-[11px] text -slate-4 00" > 阶段口径 : 审批中 ( 含会签 ) → 已批准待投用 → 待验收 → 待关闭 · 培训 / PSSR 线下签字后拍照上传 , 并入资料归档 · 明细见变更台账 < / div >
< div class = "h-full rounded-full" : style = "{ width: `${openTotal ? (s.n / openTotal) * 100 : 0}%`, background: s.c }" / >
< / div >
< / NCard >
< / NGi >
< NGi span = "24 s:24 m:24 l:18" >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }" >
< p class = " text-base font-semibold border-b border-slate-100 pb-3 mb-4" > 验收达标 · 变更类型 · 变更时限分布 < / p >
< div class = "grid gap-4 grid-cols-3" >
< div >
< div class = "mb-1 text-center text-xs font-semibold text-slate-600" > 验收达标分布 ( 共 { { acceptTotal } } 项 · 处置关闭单列 ) < / div >
< RingChart v-if = "deptStatistics?.accept_dist?.length" :data="deptStatistics?.accept_dist || []" :total="acceptTotal" / >
< / div >
< div >
< div class = "mb-1 text-center text-xs font-semibold text-slate-600" > 变更类型分布 < / div >
< PieChart v-if = "deptStatistics?.type_dist?.length && typeList?.length" :data="deptStatistics?.type_dist || []" :typeList="typeList" / >
< / div >
< div >
< div class = "mb-1 text-center text-xs font-semibold text-slate-600" > 变更时限分布 < / div >
< PieChart2 v-if = "deptStatistics?.duration_dist?.length" :data="deptStatistics?.duration_dist || []" / >
< / div >
< / div >
< / div >
< / div >
< div class = "text-[11px] text-slate-400" > 阶段口径 : 审批中 ( 含会签 ) → 已批准待投用 → 待验收 → 待关闭 · 培训 / PSSR 线下签字后拍照上传 , 并入资料归档 · 明细见变更台账 < / div >
< / NCard >
< / div >
< / NGi >
< / NCard >
< / NGrid >
< / NGi >
< NGi span = "24 s:24 m:24 l:18" >
< NGrid :x-gap = "16" :y-gap = "16" responsive = "screen" item -responsive >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px', height: '100%' } ">
< NGi span = "24 s:24 m:24 l:12 ">
< p class = " text-base font-semibold border-b border-slate-100 pb-3 mb-4" > 验收达标 · 变更类型 · 变更时限分布 < / p >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }" >
< div class = "grid gap-4 grid-cols-3 " >
< div class = "border-b border-slate-100 pb-3 mb-4 flex justify-between items-center " >
< div >
< p class = " text-base font-semibold" > 各人员审批时间对比 ( 平均处理时长 ) < / p >
< div class = "mb-1 text-center text-xs font-semibold text-slate-6 00" > 验收达标分布 ( 共 { { acceptTotal } } 项 · 处置关闭单列 ) < / div >
< p class = "text-[11px] text-slate-4 00 mt-5px" > 超过 2 天红色标识 · 用于会签催办与流程优化参考 < / p >
< RingChart :data = "DEPT_ACCEPT" :colors = "ACCEPT_COLORS" / >
< / div >
< / div >
< div >
< div class = "space-y-3" >
< div class = "mb-1 text-center text-xs font-semibold text-slate-600" > 变更类型分布 < / div >
< div v-for = "a in deptStatistics?.approver_time" :key="a.name" >
< PieChart :data = "DEPT_TYPE" :colors = "PIE_COLORS" / >
< div class = "flex items-center gap-2 text-xs" >
< / div >
< span class = "min-w-0 flex-1 text-slate-700" > { { a . user _name } } { { a . step _name ? '( ' + a . step _name + ') ' : '' } } < / span >
< div >
< span class = "shrink-0 font-semibold text-slate-600" > { { a . avg _days } } 天 < / span >
< div class = "mb-1 text-center text-xs font-semibold text-slate-600" > 变更时限分布 < / div >
< / div >
< PieChart2 :data = "DEPT_DUR" :colors = "DUR_COLORS" / >
< div class = "mt-1 h-1.5 w-full overflow-hidden rounded-full bg-slate-100" >
< / div >
< div class = "h-full rounded-full" : style = "{ width: `${(a.avg_days / a.total) * 100}%`, background: a.avg_days > 2 ? themeStore.otherColor.error : themeStore.themeColor }" > < / div >
< / div >
< / div >
< div class = "mt-3 rounded-lg bg-slate-50 px-3 py-2 text-[11px] leading-5 text-slate-500" >
未达到 2 项 : P - 204 伴热改造温控未达预期 ( 已退回整改 ) 、 加药泵冲程调整流量偏差 ( 重新约定 30 天验证期 ) ; 处置关闭 3 项 : 未实施 2 项 ( 检修窗口调整 、 方案合并实施 ) 、 未投用 1 项 ( 投用条件不具备 ) , 均经情况说明后关闭 , 验收结论不再含未实施 / 未投用 。
< / div >
< / NCard >
< / NGi >
< / NGrid >
< NGrid :x-gap = "gap" :y-gap = "16" responsive = "screen" item -responsive >
< NGi span = "24 s:24 m:24 l:12" >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }" >
< p class = " text-base font-semibold border-b border-slate-100 pb-3 mb-4" > 各人员审批时间对比 ( 平均处理时长 ) < / p >
< div class = "space-y-3" >
< div v-for = "a in APPROVER_TIME" :key="a.name" >
< div class = "flex items-center gap-2 text-xs" >
< span class = "min-w-0 flex-1 text-slate-700" > { { a . name } } < / span >
< span class = "shrink-0 font-semibold text-slate-600" > { { a . days } } 天 < / span >
< / div >
< div class = "mt-1 h-1.5 w-full overflow-hidden rounded-full bg-slate-100" >
< div class = "h-full rounded-full" : style = "{ width: `${(a.days / 2.5) * 100}%`, background: a.days > 2 ? '#E15555' : '#1D4E9C' }" / >
< / div >
< / div >
< / div >
< / div >
< div class = "text-[11px] text-slate-400" > 超过 2 天红色标识 · 用于会签催办与流程优化参考 < / div >
< / NCard >
< / div >
< / NGi >
< / NCard >
< NGi span = "24 s:24 m:24 l:12" >
< / NGi >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }" >
< NGi span = "24 s:24 m:24 l:12" >
< p class = " text-base font-semibold border-b border-slate-100 pb-3 mb-4" > 临时变更到期提醒情况 ( 共 { { tempTotal } } 项 ) < / p >
< NCard :bordered = "false" size = "small" : style = "{ borderRadius: themeStore.themeRadius + 'px', height: '100%' } ">
< div class = "space-y-3 ">
< p class = " text-base font-semibold border-b border-slate-100 pb -3 mb-4" > 临时变更到期提醒情况 ( 共 { { tempTotal } } 项 ) < / p >
< div class = "rounded-lg border px -3 py-2" >
< div class = "space-y-3 " >
< div class = "flex items-center gap-2 text-xs " >
< div v-for = "t in TEMP_WATCH" :key="t.label" class="rounded-lg border px-3 py-2" >
< span class = "h-2 w-2 rounded-full" : style = "{ background: themeStore.otherColor.success }" > < / span >
< div class = "flex items-center gap-2 text-xs" >
< span class = "font-medium text-slate-700" > 正常 ( 到期 > { { remindDays } } 天 ) < / span >
< span class = "h-2 w-2 rounded-full" : style = "{ background: t.c }" / >
< span class = "ml-auto font-semibold text-slate-600" > { { deptStatistics ? . kpis ? . unclosed _total || 0 } } 项 < / span >
< span class = "font-medium text-slate-700" > { { t . label } } < / span >
< / div >
< span class = "ml-auto font-semibold text-slate-600" > { { t . n } } 项 < / span >
< / div >
< / div >
< div class = "mt-1 pl-4 text-[11px] text-slate-400" > { { t . note } } < / 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" > { { deptStatistics ? . kpis ? . near _overdue _total || 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" > { { deptStatistics ? . kpis ? . overdue _total || 0 } } 项 < / span >
< / div >
< / div >
< div class = "text-[11px] text-slate-400" > 提醒规则 : 到期前 { { remindDays } } 天触发 ( 可在 「 我的任务 · 我的申请 」 中调整 ) · 临期 / 超期项请按期办理延期 、 恢复或转永久 < / div >
< / div >
< / div >
< div class = "text-[11px] text-slate-400" > 提醒规则 : 到期前 7 天触发 ( 可在 「 待我处理 · 我的申请 」 中调整 ) · 临期 / 超期项请按期办理延期 、 恢复或转永久 < / div >
< / NCard >
< / div >
< / NGi >
< / NCard >
< / NGrid >
< / NGi >
< / div >
< / NGrid >
< / NSpin >
< / NSpace >
< / NSpace >
< / template >
< / template >
< style scoped > < / style >