chore: 统一换行符为LF,与.gitattributes规范一致(内容无变化)
This commit is contained in:
@@ -1,246 +1,246 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { CHANGES, TEMP_WATCH, countOpenStatus } from "@/typings/model";
|
||||
import { formatTimestamp } from "@/utils/common";
|
||||
import RingChart from './modules/ringChart.vue';
|
||||
import PieChart from './modules/pieChart.vue';
|
||||
import PieChart2 from './modules/pieChart2.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
||||
|
||||
const P_PERIODS = ["近一年", "近半年", "近三个月", "近一个月", "自定义"] as const;
|
||||
const period = ref<(typeof P_PERIODS)[number]>("近一年");
|
||||
const from = ref<any>(null);
|
||||
const to = ref<any>(null);
|
||||
const rangeText = computed(() => period.value === "自定义"
|
||||
? (from.value || to.value ? `${formatTimestamp(from.value,'yyyy-MM-dd') || "开始"} ~ ${formatTimestamp(to.value,'yyyy-MM-dd') || "至今"}` : "自定义(未选日期)")
|
||||
: period.value);
|
||||
|
||||
const deptOpenStatus = countOpenStatus(CHANGES);
|
||||
const DEPT_ACCEPT = [
|
||||
{ name: "完全达到", value: 13 },
|
||||
{ name: "基本达到", value: 4 },
|
||||
{ name: "未达到", value: 2 },
|
||||
{ name: "处置关闭(未实施 / 未投用)", value: 3 },
|
||||
];
|
||||
const acceptTotal = DEPT_ACCEPT.reduce((a, b) => a + b.value, 0);
|
||||
const ACCEPT_COLORS = ["#7CC242", "#F5A623", "#E15555", "#94A3B8", "#64748B"];
|
||||
const DEPT_TYPE = [
|
||||
{ name: "工艺", value: 11 }, { name: "设备", value: 12 }, { name: "管理", value: 5 },
|
||||
];
|
||||
const DEPT_DUR = [
|
||||
{ name: "永久", value: 21 }, { name: "临时", value: 7 },
|
||||
];
|
||||
const DUR_COLORS = ["#1D4E9C", "#FAAD14"];
|
||||
const PIE_COLORS = ["#1D4E9C", "#7CC242", "#F5A623"];
|
||||
const APPROVER_TIME = [
|
||||
{ name: "李主任(部门审核)", days: 1.2 },
|
||||
{ name: "王仪表(仪表会签)", days: 0.8 },
|
||||
{ name: "周设备(设备会签)", days: 1.5 },
|
||||
{ name: "王海燕(安全审查)", days: 2.1 },
|
||||
{ name: "刘副总(公司终批)", days: 1.9 },
|
||||
];
|
||||
const openTotal = deptOpenStatus.reduce((a, b) => a + b.n, 0);
|
||||
const tempTotal = TEMP_WATCH.reduce((a, b) => a + b.n, 0);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NSpace vertical :size="16">
|
||||
<!-- 本部门变更统计 -->
|
||||
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||
<div class="flex flex-wrap items-center justify-between">
|
||||
<div class="flex-y-center">
|
||||
<span class="flex items-center gap-1.5 text-sm font-semibold text-slate-700">
|
||||
<Icon icon="akar-icons:calendar" class="size-14px" :style="{color: themeStore.themeColor}" />
|
||||
<span>本部门变更统计</span>
|
||||
<span class="text-xs font-normal text-slate-400">一车间 · 本部门口径</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 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>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-[11px] text-slate-400">统计口径:{{ rangeText }}</span>
|
||||
</div>
|
||||
</NCard>
|
||||
<!-- 卡片数据 -->
|
||||
<NGrid cols="s:1 m:5 l:5" responsive="screen" :x-gap="gap" :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="28"
|
||||
class="text-26px font-600 text-[#333]"
|
||||
/><p class="mt-10px ml-5px">项</p>
|
||||
</div>
|
||||
<p class="text-12px">永久 21 · 临时 7</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="7"
|
||||
class="text-26px font-600 text-[#333]"
|
||||
/><p class="mt-10px ml-5px">项</p>
|
||||
</div>
|
||||
<p class="text-12px">占比 25% · 均含公司终批</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="6"
|
||||
class="text-26px font-600 text-[#E15555]"
|
||||
/><p class="mt-10px ml-5px">项</p>
|
||||
</div>
|
||||
<p class="text-12px">其中超期 1 项 · 临期 1 项</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="7.4"
|
||||
:decimals="1"
|
||||
class="text-26px font-600 text-[#333]"
|
||||
/>
|
||||
<p class="mt-10px ml-5px">天</p>
|
||||
</div>
|
||||
<p class="text-12px">优于全厂均值 8.2 天</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="6.1"
|
||||
:decimals="1"
|
||||
class="text-26px font-600 text-[#333]"
|
||||
/><p class="mt-10px ml-5px">%</p>
|
||||
</div>
|
||||
<p class="text-12px">2 项被驳回 · 均已重新提交</p>
|
||||
</div>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
|
||||
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
||||
<NGi span="24 s:24 m:24 l:6">
|
||||
<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>
|
||||
<div class="space-y-3">
|
||||
<div v-for="s in deptOpenStatus" :key="s.name">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="min-w-0 flex-1 text-slate-700">{{ s.name }}</span>
|
||||
<span class="shrink-0 font-semibold text-slate-600">{{ s.n }} 项</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: `${openTotal ? (s.n / openTotal) * 100 : 0}%`, background: s.c }" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[11px] text-slate-400">阶段口径:审批中(含会签)→ 已批准待投用 → 待验收 → 待关闭 · 培训 / PSSR 线下签字后拍照上传,并入资料归档 · 明细见变更台账</div>
|
||||
</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 :data="DEPT_ACCEPT" :colors="ACCEPT_COLORS" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1 text-center text-xs font-semibold text-slate-600">变更类型分布</div>
|
||||
<PieChart :data="DEPT_TYPE" :colors="PIE_COLORS" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1 text-center text-xs font-semibold text-slate-600">变更时限分布</div>
|
||||
<PieChart2 :data="DEPT_DUR" :colors="DUR_COLORS" />
|
||||
</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 class="text-[11px] text-slate-400">超过 2 天红色标识 · 用于会签催办与流程优化参考</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</NGi>
|
||||
<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">临时变更到期提醒情况(共 {{tempTotal}} 项)</p>
|
||||
<div class="space-y-3">
|
||||
<div v-for="t in TEMP_WATCH" :key="t.label" 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: t.c }" />
|
||||
<span class="font-medium text-slate-700">{{ t.label }}</span>
|
||||
<span class="ml-auto font-semibold text-slate-600">{{ t.n }} 项</span>
|
||||
</div>
|
||||
<div class="mt-1 pl-4 text-[11px] text-slate-400">{{ t.note }}</div>
|
||||
</div>
|
||||
<div class="text-[11px] text-slate-400">提醒规则:到期前 7 天触发(可在「待我处理 · 我的申请」中调整)· 临期 / 超期项请按期办理延期、恢复或转永久</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { CHANGES, TEMP_WATCH, countOpenStatus } from "@/typings/model";
|
||||
import { formatTimestamp } from "@/utils/common";
|
||||
import RingChart from './modules/ringChart.vue';
|
||||
import PieChart from './modules/pieChart.vue';
|
||||
import PieChart2 from './modules/pieChart2.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
||||
|
||||
const P_PERIODS = ["近一年", "近半年", "近三个月", "近一个月", "自定义"] as const;
|
||||
const period = ref<(typeof P_PERIODS)[number]>("近一年");
|
||||
const from = ref<any>(null);
|
||||
const to = ref<any>(null);
|
||||
const rangeText = computed(() => period.value === "自定义"
|
||||
? (from.value || to.value ? `${formatTimestamp(from.value,'yyyy-MM-dd') || "开始"} ~ ${formatTimestamp(to.value,'yyyy-MM-dd') || "至今"}` : "自定义(未选日期)")
|
||||
: period.value);
|
||||
|
||||
const deptOpenStatus = countOpenStatus(CHANGES);
|
||||
const DEPT_ACCEPT = [
|
||||
{ name: "完全达到", value: 13 },
|
||||
{ name: "基本达到", value: 4 },
|
||||
{ name: "未达到", value: 2 },
|
||||
{ name: "处置关闭(未实施 / 未投用)", value: 3 },
|
||||
];
|
||||
const acceptTotal = DEPT_ACCEPT.reduce((a, b) => a + b.value, 0);
|
||||
const ACCEPT_COLORS = ["#7CC242", "#F5A623", "#E15555", "#94A3B8", "#64748B"];
|
||||
const DEPT_TYPE = [
|
||||
{ name: "工艺", value: 11 }, { name: "设备", value: 12 }, { name: "管理", value: 5 },
|
||||
];
|
||||
const DEPT_DUR = [
|
||||
{ name: "永久", value: 21 }, { name: "临时", value: 7 },
|
||||
];
|
||||
const DUR_COLORS = ["#1D4E9C", "#FAAD14"];
|
||||
const PIE_COLORS = ["#1D4E9C", "#7CC242", "#F5A623"];
|
||||
const APPROVER_TIME = [
|
||||
{ name: "李主任(部门审核)", days: 1.2 },
|
||||
{ name: "王仪表(仪表会签)", days: 0.8 },
|
||||
{ name: "周设备(设备会签)", days: 1.5 },
|
||||
{ name: "王海燕(安全审查)", days: 2.1 },
|
||||
{ name: "刘副总(公司终批)", days: 1.9 },
|
||||
];
|
||||
const openTotal = deptOpenStatus.reduce((a, b) => a + b.n, 0);
|
||||
const tempTotal = TEMP_WATCH.reduce((a, b) => a + b.n, 0);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NSpace vertical :size="16">
|
||||
<!-- 本部门变更统计 -->
|
||||
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||
<div class="flex flex-wrap items-center justify-between">
|
||||
<div class="flex-y-center">
|
||||
<span class="flex items-center gap-1.5 text-sm font-semibold text-slate-700">
|
||||
<Icon icon="akar-icons:calendar" class="size-14px" :style="{color: themeStore.themeColor}" />
|
||||
<span>本部门变更统计</span>
|
||||
<span class="text-xs font-normal text-slate-400">一车间 · 本部门口径</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 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>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-[11px] text-slate-400">统计口径:{{ rangeText }}</span>
|
||||
</div>
|
||||
</NCard>
|
||||
<!-- 卡片数据 -->
|
||||
<NGrid cols="s:1 m:5 l:5" responsive="screen" :x-gap="gap" :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="28"
|
||||
class="text-26px font-600 text-[#333]"
|
||||
/><p class="mt-10px ml-5px">项</p>
|
||||
</div>
|
||||
<p class="text-12px">永久 21 · 临时 7</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="7"
|
||||
class="text-26px font-600 text-[#333]"
|
||||
/><p class="mt-10px ml-5px">项</p>
|
||||
</div>
|
||||
<p class="text-12px">占比 25% · 均含公司终批</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="6"
|
||||
class="text-26px font-600 text-[#E15555]"
|
||||
/><p class="mt-10px ml-5px">项</p>
|
||||
</div>
|
||||
<p class="text-12px">其中超期 1 项 · 临期 1 项</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="7.4"
|
||||
:decimals="1"
|
||||
class="text-26px font-600 text-[#333]"
|
||||
/>
|
||||
<p class="mt-10px ml-5px">天</p>
|
||||
</div>
|
||||
<p class="text-12px">优于全厂均值 8.2 天</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="6.1"
|
||||
:decimals="1"
|
||||
class="text-26px font-600 text-[#333]"
|
||||
/><p class="mt-10px ml-5px">%</p>
|
||||
</div>
|
||||
<p class="text-12px">2 项被驳回 · 均已重新提交</p>
|
||||
</div>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
|
||||
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
||||
<NGi span="24 s:24 m:24 l:6">
|
||||
<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>
|
||||
<div class="space-y-3">
|
||||
<div v-for="s in deptOpenStatus" :key="s.name">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="min-w-0 flex-1 text-slate-700">{{ s.name }}</span>
|
||||
<span class="shrink-0 font-semibold text-slate-600">{{ s.n }} 项</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: `${openTotal ? (s.n / openTotal) * 100 : 0}%`, background: s.c }" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[11px] text-slate-400">阶段口径:审批中(含会签)→ 已批准待投用 → 待验收 → 待关闭 · 培训 / PSSR 线下签字后拍照上传,并入资料归档 · 明细见变更台账</div>
|
||||
</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 :data="DEPT_ACCEPT" :colors="ACCEPT_COLORS" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1 text-center text-xs font-semibold text-slate-600">变更类型分布</div>
|
||||
<PieChart :data="DEPT_TYPE" :colors="PIE_COLORS" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1 text-center text-xs font-semibold text-slate-600">变更时限分布</div>
|
||||
<PieChart2 :data="DEPT_DUR" :colors="DUR_COLORS" />
|
||||
</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 class="text-[11px] text-slate-400">超过 2 天红色标识 · 用于会签催办与流程优化参考</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</NGi>
|
||||
<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">临时变更到期提醒情况(共 {{tempTotal}} 项)</p>
|
||||
<div class="space-y-3">
|
||||
<div v-for="t in TEMP_WATCH" :key="t.label" 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: t.c }" />
|
||||
<span class="font-medium text-slate-700">{{ t.label }}</span>
|
||||
<span class="ml-auto font-semibold text-slate-600">{{ t.n }} 项</span>
|
||||
</div>
|
||||
<div class="mt-1 pl-4 text-[11px] text-slate-400">{{ t.note }}</div>
|
||||
</div>
|
||||
<div class="text-[11px] text-slate-400">提醒规则:到期前 7 天触发(可在「待我处理 · 我的申请」中调整)· 临期 / 超期项请按期办理延期、恢复或转永久</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useEcharts } from '@/hooks/common/echarts';
|
||||
|
||||
defineOptions({
|
||||
name: 'PieChart'
|
||||
});
|
||||
|
||||
const props = defineProps<{ data?: { name: string; value: number }[]; colors?: string[] }>();
|
||||
|
||||
const { domRef, updateOptions } = useEcharts(() => ({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
bottom: 3,
|
||||
left: 'center',
|
||||
icon: 'circle',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemStyle: {
|
||||
borderWidth: 0
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
color: ['#5da8ff', '#8e9dff', '#fedc69', '#26deca'],
|
||||
radius: '65%',
|
||||
center: ['50%', '43%'],
|
||||
label: {
|
||||
show: false,
|
||||
position: 'outside',
|
||||
formatter: '{b}\n{c} ({d}%)',
|
||||
fontSize: 12,
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
length: 10,
|
||||
length2: 10
|
||||
},
|
||||
data: [] as { name: string; value: number }[]
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
function init() {
|
||||
updateOptions(opts => {
|
||||
opts.series[0].data = props.data || [];
|
||||
opts.series[0].color = props.colors || [];
|
||||
return opts;
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="domRef" class="w-full h-220px overflow-hidden"></div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useEcharts } from '@/hooks/common/echarts';
|
||||
|
||||
defineOptions({
|
||||
name: 'PieChart'
|
||||
});
|
||||
|
||||
const props = defineProps<{ data?: { name: string; value: number }[]; colors?: string[] }>();
|
||||
|
||||
const { domRef, updateOptions } = useEcharts(() => ({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
bottom: 3,
|
||||
left: 'center',
|
||||
icon: 'circle',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemStyle: {
|
||||
borderWidth: 0
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
color: ['#5da8ff', '#8e9dff', '#fedc69', '#26deca'],
|
||||
radius: '65%',
|
||||
center: ['50%', '43%'],
|
||||
label: {
|
||||
show: false,
|
||||
position: 'outside',
|
||||
formatter: '{b}\n{c} ({d}%)',
|
||||
fontSize: 12,
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
length: 10,
|
||||
length2: 10
|
||||
},
|
||||
data: [] as { name: string; value: number }[]
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
function init() {
|
||||
updateOptions(opts => {
|
||||
opts.series[0].data = props.data || [];
|
||||
opts.series[0].color = props.colors || [];
|
||||
return opts;
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="domRef" class="w-full h-220px overflow-hidden"></div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useEcharts } from '@/hooks/common/echarts';
|
||||
|
||||
defineOptions({
|
||||
name: 'PieChart'
|
||||
});
|
||||
|
||||
const props = defineProps<{ data?: { name: string; value: number }[]; colors?: string[] }>();
|
||||
|
||||
const { domRef, updateOptions } = useEcharts(() => ({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
bottom: 3,
|
||||
left: 'center',
|
||||
icon: 'circle',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemStyle: {
|
||||
borderWidth: 0
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
color: ['#5da8ff', '#8e9dff', '#fedc69', '#26deca'],
|
||||
radius: '65%',
|
||||
center: ['50%', '43%'],
|
||||
label: {
|
||||
show: false,
|
||||
position: 'outside',
|
||||
formatter: '{b}\n{c} ({d}%)',
|
||||
fontSize: 12,
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
length: 10,
|
||||
length2: 10
|
||||
},
|
||||
data: [] as { name: string; value: number }[]
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
function init() {
|
||||
updateOptions(opts => {
|
||||
opts.series[0].data = props.data || [];
|
||||
opts.series[0].color = props.colors || [];
|
||||
return opts;
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="domRef" class="w-full h-220px overflow-hidden"></div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useEcharts } from '@/hooks/common/echarts';
|
||||
|
||||
defineOptions({
|
||||
name: 'PieChart'
|
||||
});
|
||||
|
||||
const props = defineProps<{ data?: { name: string; value: number }[]; colors?: string[] }>();
|
||||
|
||||
const { domRef, updateOptions } = useEcharts(() => ({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
bottom: 3,
|
||||
left: 'center',
|
||||
icon: 'circle',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemStyle: {
|
||||
borderWidth: 0
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
color: ['#5da8ff', '#8e9dff', '#fedc69', '#26deca'],
|
||||
radius: '65%',
|
||||
center: ['50%', '43%'],
|
||||
label: {
|
||||
show: false,
|
||||
position: 'outside',
|
||||
formatter: '{b}\n{c} ({d}%)',
|
||||
fontSize: 12,
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
length: 10,
|
||||
length2: 10
|
||||
},
|
||||
data: [] as { name: string; value: number }[]
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
function init() {
|
||||
updateOptions(opts => {
|
||||
opts.series[0].data = props.data || [];
|
||||
opts.series[0].color = props.colors || [];
|
||||
return opts;
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="domRef" class="w-full h-220px overflow-hidden"></div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user