关闭确认静态页面完成
This commit is contained in:
@@ -249,7 +249,8 @@ const local: App.I18n.Schema = {
|
|||||||
riskstatistics: '安全与风险统计',
|
riskstatistics: '安全与风险统计',
|
||||||
home_pendingchange: '待投用变更',
|
home_pendingchange: '待投用变更',
|
||||||
home_effect: '变更投入和效果',
|
home_effect: '变更投入和效果',
|
||||||
home_datachangecenter: '变更数据中心'
|
home_datachangecenter: '变更数据中心',
|
||||||
|
home_closeconfirm: '关闭确认'
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
required: '不能为空',
|
required: '不能为空',
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
changestatistics: () => import("@/views/changeStatistics/index.vue"),
|
changestatistics: () => import("@/views/changeStatistics/index.vue"),
|
||||||
docs_archives: () => import("@/views/docs/archives/index.vue"),
|
docs_archives: () => import("@/views/docs/archives/index.vue"),
|
||||||
docs_query: () => import("@/views/docs/query/index.vue"),
|
docs_query: () => import("@/views/docs/query/index.vue"),
|
||||||
|
home_closeconfirm: () => import("@/views/home/closeConfirm/index.vue"),
|
||||||
home_datachangecenter: () => import("@/views/home/dataChangeCenter/index.vue"),
|
home_datachangecenter: () => import("@/views/home/dataChangeCenter/index.vue"),
|
||||||
home_effect: () => import("@/views/home/effect/index.vue"),
|
home_effect: () => import("@/views/home/effect/index.vue"),
|
||||||
home_highrisk: () => import("@/views/home/highRisk/index.vue"),
|
home_highrisk: () => import("@/views/home/highRisk/index.vue"),
|
||||||
|
|||||||
@@ -127,6 +127,17 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
order: 1
|
order: 1
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
name: 'home_closeconfirm',
|
||||||
|
path: '/home/closeconfirm',
|
||||||
|
component: 'view.home_closeconfirm',
|
||||||
|
meta: {
|
||||||
|
title: 'home_closeconfirm',
|
||||||
|
i18nKey: 'route.home_closeconfirm',
|
||||||
|
icon: '',
|
||||||
|
order: 6
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'home_datachangecenter',
|
name: 'home_datachangecenter',
|
||||||
path: '/home/datachangecenter',
|
path: '/home/datachangecenter',
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ const routeMap: RouteMap = {
|
|||||||
"docs_archives": "/docs/archives",
|
"docs_archives": "/docs/archives",
|
||||||
"docs_query": "/docs/query",
|
"docs_query": "/docs/query",
|
||||||
"home": "/home",
|
"home": "/home",
|
||||||
|
"home_closeconfirm": "/home/closeconfirm",
|
||||||
"home_datachangecenter": "/home/datachangecenter",
|
"home_datachangecenter": "/home/datachangecenter",
|
||||||
"home_effect": "/home/effect",
|
"home_effect": "/home/effect",
|
||||||
"home_highrisk": "/home/highrisk",
|
"home_highrisk": "/home/highrisk",
|
||||||
|
|||||||
Vendored
+2
@@ -28,6 +28,7 @@ declare module "@elegant-router/types" {
|
|||||||
"docs_archives": "/docs/archives";
|
"docs_archives": "/docs/archives";
|
||||||
"docs_query": "/docs/query";
|
"docs_query": "/docs/query";
|
||||||
"home": "/home";
|
"home": "/home";
|
||||||
|
"home_closeconfirm": "/home/closeconfirm";
|
||||||
"home_datachangecenter": "/home/datachangecenter";
|
"home_datachangecenter": "/home/datachangecenter";
|
||||||
"home_effect": "/home/effect";
|
"home_effect": "/home/effect";
|
||||||
"home_highrisk": "/home/highrisk";
|
"home_highrisk": "/home/highrisk";
|
||||||
@@ -112,6 +113,7 @@ declare module "@elegant-router/types" {
|
|||||||
| "changestatistics"
|
| "changestatistics"
|
||||||
| "docs_archives"
|
| "docs_archives"
|
||||||
| "docs_query"
|
| "docs_query"
|
||||||
|
| "home_closeconfirm"
|
||||||
| "home_datachangecenter"
|
| "home_datachangecenter"
|
||||||
| "home_effect"
|
| "home_effect"
|
||||||
| "home_highrisk"
|
| "home_highrisk"
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
<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 { getColorWithOpacity } from '@/utils/common';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
||||||
|
|
||||||
|
const emit = defineEmits<{ go: [v: string, preset?: string];}>();
|
||||||
|
|
||||||
|
const restoresDone = ref<boolean>(false);
|
||||||
|
const restores = ref<{ item: string, done: boolean }[]>([
|
||||||
|
{ item: "安全阀起跳压力已恢复至原设定 0.8MPa(恢复操作票 CAO-2026-0812)", done: true },
|
||||||
|
{ item: "恢复后运行参数核对:连续 24h 无起跳、阀体无泄漏", done: false },
|
||||||
|
{ item: "恢复性检查记录与现场照片已上传", done: false },
|
||||||
|
]);
|
||||||
|
const canRestore = ref<boolean>(false);
|
||||||
|
const readyToClose = ref<boolean>(false);
|
||||||
|
|
||||||
|
const checks = ref<{ item: string, done: boolean, note?: string }[]>([
|
||||||
|
{ item: "变更验收完成(结论:基本达到 · 遗留项 2 项已登记跟踪)", done: true },
|
||||||
|
{ item: "资料上传齐全(验收报告、运行记录、恢复确认记录)", done: false, note: "缺:更新后的安全阀台账" },
|
||||||
|
{ item: "行动项全部关闭(4/4,含「关闭前完成」标记项)", done: true },
|
||||||
|
{ item: "受控文件已更新至生效版(P&ID Rev.4、操作规程)", done: false, note: "安全阀台账待更新发布" },
|
||||||
|
{ item: "培训考核完成(12/12 人),签到与成绩已归档", done: true },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const archiving = ref<boolean>(false);
|
||||||
|
const step = ref<number>(0);
|
||||||
|
const archivedHash = ref<string | null>(null);
|
||||||
|
const allDone = computed(() => checks.value.every((c: { done: boolean }) => c.done));
|
||||||
|
const isCloser = ref<boolean>(true);
|
||||||
|
const startArchive = () => { step.value = 0; archiving.value = true; };
|
||||||
|
|
||||||
|
// 关闭即归档:自动归档八步流程
|
||||||
|
const ARCHIVE_STEPS = [
|
||||||
|
"关闭触发:接收「同意关闭」归档指令(不可逆)",
|
||||||
|
"文件收集:申请表 / 审批记录 / 风险分析 / 培训 / PSSR / 验收单生成 PDF",
|
||||||
|
"附件归集:原始附件、培训签到照片、PSSR 现场照片、验收签字文件",
|
||||||
|
"受控文件复制:生效版与被替代旧版物理复制,与工作区脱离",
|
||||||
|
"清单生成:自动生成归档索引与文件更新清单(新旧版本对照)",
|
||||||
|
"打包锁定:写入归档区(装置 / 年份 / 变更单号),设为只读",
|
||||||
|
"哈希存证:计算归档包 SHA-256 哈希值入库,防篡改校验",
|
||||||
|
"状态更新:变更为「已关闭-已归档」,首页数据同步",
|
||||||
|
];
|
||||||
|
const closedAt = ref<string | null>(null);
|
||||||
|
|
||||||
|
const OVERDUE_LIST = [
|
||||||
|
{ id: "MOC-2026-R01-0039", title: "V-102 安全阀起跳压力临时调整", stage: "待关闭", days: 4 },
|
||||||
|
{ id: "MOC-2026-R02-0031", title: "冷冻盐水泵 P-103 叶轮材质变更", stage: "待验收", days: 7 },
|
||||||
|
{ id: "MOC-2026-R01-0029", title: "操作规程修订(夏季工况)", stage: "审批中", days: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical :size="16">
|
||||||
|
<!-- 关闭确认 -->
|
||||||
|
<NCard
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
:style="{
|
||||||
|
borderRadius: themeStore.themeRadius + 'px',
|
||||||
|
'--error-color': themeStore.otherColor.error,
|
||||||
|
'--success-color': themeStore.otherColor.success,
|
||||||
|
'--success-bg': getColorWithOpacity(themeStore.otherColor.success, 0.1),
|
||||||
|
'--warning-color': themeStore.otherColor.warning,
|
||||||
|
'--warning-bg': getColorWithOpacity(themeStore.otherColor.warning, 0.1),
|
||||||
|
'--theme-color': themeStore.themeColor,
|
||||||
|
'--theme-bg': getColorWithOpacity(themeStore.themeColor, 0.1),
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<NCard :bordered="true" :content-style="{ padding: '0' }" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||||
|
<div class="py-3 px-4 border-b flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-2 text-base font-semibold">
|
||||||
|
<Icon icon="system-uicons:reset" class="size-16px text-[var(--error-color)]" />临时变更恢复确认 · 期限 30 天
|
||||||
|
</div>
|
||||||
|
<NTag v-if="restoresDone" size="small" type="success">恢复确认完成</NTag>
|
||||||
|
<NTag v-else size="small" type="error">已超期 4 天 · 已升级上报刘副总</NTag>
|
||||||
|
</div>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex flex-wrap gap-x-6 gap-y-1 text-xs text-slate-500">
|
||||||
|
<span>计划恢复时间:2026-08-14(临时调整起跳压力 0.88MPa → 恢复原设定 0.8MPa)</span>
|
||||||
|
<span class="text-[var(--error-color)]">超期未恢复:每日 08:00 提醒申请人与属地负责人,已自动升级上报分管领导</span>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2 pt-3">
|
||||||
|
<div v-for="(r, i) in restores" :key="r.item" class="flex items-center gap-3 rounded-lg border p-3"
|
||||||
|
:class="r.done ? 'border-emerald-200 bg-emerald-50/40' : 'border-amber-200 bg-amber-50/40'">
|
||||||
|
<Icon v-if="r.done" icon="material-symbols:check-circle-outline" class="size-20px text-[var(--success-color)]" />
|
||||||
|
<Icon v-else icon="system-uicons:reset" class="size-18px text-[var(--warning-color)]" />
|
||||||
|
<div class="flex-1 text-sm">{{ r.item }}</div>
|
||||||
|
<NButton v-if="!r.done && canRestore" size="small" ghost class="text-xs"
|
||||||
|
@click="restores = restores.map((x, xi) => (xi === i ? { ...x, done: true } : x)); toast.success('演示:恢复确认完成(移动端可拍照上传凭证)');">
|
||||||
|
<Icon icon="ant-design:camera-outlined" class="size-16px mr-1" />
|
||||||
|
确认恢复
|
||||||
|
</NButton>
|
||||||
|
<span v-if="!r.done && !canRestore" class="text-xs text-slate-400">待申请人 / 属地负责人确认</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="restoresDone" class="mt-3 rounded-lg bg-emerald-50 px-3 py-2 text-xs text-emerald-700">
|
||||||
|
临时变更恢复确认完成,恢复操作票、参数核对记录与照片将自动纳入归档包「5-验收记录」
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NCard>
|
||||||
|
<NCard :bordered="true" :content-style="{ padding: '0' }" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||||
|
<div class="py-3 px-4 border-b flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-2 text-base font-semibold">
|
||||||
|
<Icon icon="material-symbols:check-circle-outline" class="size-18px text-slate-500" />关闭校验 · MOC-2026-R01-0039(紧急变更补办)
|
||||||
|
</div>
|
||||||
|
<NTag v-if="readyToClose" size="small" type="success">校验全绿 · 已推送专责</NTag>
|
||||||
|
<NTag v-else size="small" type="warning">超期 4 天</NTag>
|
||||||
|
</div>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex items-center gap-2 rounded-lg bg-[var(--theme-bg)] px-3 py-2 text-xs text-[var(--theme-color)]">
|
||||||
|
<Icon icon="lucide:bell" class="size-14px" />
|
||||||
|
前置条件(验收完成 / 资料齐全 / 行动项关闭 / 恢复确认 / 受控文件更新)全部满足后,系统自动推送变更主管部门专责(王海燕 · 安全环保部),并抄送申请人与属地负责人
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2 pt-3">
|
||||||
|
<div v-for="(c, i) in checks" :key="c.item" class="flex items-center gap-3 rounded-lg border p-3"
|
||||||
|
:class="c.done ? 'border-emerald-200 bg-emerald-50/40' : 'border-red-200 bg-[#FFF2F0]'">
|
||||||
|
<Icon v-if="c.done" icon="material-symbols:check-circle-outline" class="size-20px text-[var(--success-color)]" />
|
||||||
|
<Icon v-else icon="ant-design:close-circle-outlined" class="size-18px text-[var(--error-color)]" />
|
||||||
|
<div class="flex-1 text-sm">{{ c.item }}<span v-if="c.note" class="ml-2 text-xs text-[var(--error-color)]">{{ c.note }}</span></div>
|
||||||
|
<NButton v-if="!c.done" text type="primary"
|
||||||
|
@click="checks = checks.map((x, xi) => (xi === i ? { ...x, done: true } : x)); toast.success('演示:资料已补充上传,关闭校验清单已同步更新并重新推送专责');">去补充</NButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 关闭按钮区 -->
|
||||||
|
<div v-if="!archivedHash && !archiving" class="mt-4 space-y-2">
|
||||||
|
<div v-if="readyToClose" class="flex items-center gap-2 rounded-lg bg-[var(--theme-bg)] px-3 py-2 text-xs text-[var(--theme-color)]">
|
||||||
|
<Icon icon="lucide:bell" class="size-14px" />
|
||||||
|
系统已于今日 09:12 自动推送关闭待办至变更主管部门专责(王海燕)· 系统消息 + 企业微信
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap items-center justify-between gap-2 rounded-lg bg-slate-50 p-3">
|
||||||
|
<span class="text-sm text-slate-500">
|
||||||
|
{{ !restoresDone
|
||||||
|
? '临时变更尚未完成恢复确认,禁止关闭(恢复确认是临时变更关闭的前置条件)'
|
||||||
|
: allDone
|
||||||
|
? isCloser
|
||||||
|
? '全部条件满足。确认关闭后系统记录关闭时间与关闭人,并自动生成归档包(副本归档 · 只读锁定 · 哈希存证)'
|
||||||
|
: '全部条件满足,待变更主管部门专责(王海燕 · 安全环保部)确认关闭'
|
||||||
|
: '存在未满足条件,禁止关闭;确需例外须填写说明并经分管领导确认' }}
|
||||||
|
</span>
|
||||||
|
<NButton v-if="isCloser" size="small" type="primary" :disabled="!readyToClose" class="text-xs" @click="startArchive">
|
||||||
|
<Icon icon="uil:lock" class="size-14px mr-1" /> 确认关闭并归档
|
||||||
|
</NButton>
|
||||||
|
<span v-else class="text-xs text-[var(--warning-color)]">仅变更主管部门专责可确认关闭 · 切换至「王海燕 · 安全工程师」体验</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 自动归档流程 -->
|
||||||
|
<div v-if="archiving" class="mt-4 space-y-1.5 rounded-lg border border-[var(--theme-color)] bg-[var(--theme-bg)] p-4">
|
||||||
|
<div class="mb-2 flex items-center gap-2 text-sm font-semibold text-[var(--theme-color)]">
|
||||||
|
<Icon icon="ri:loader-4-fill" class="size-18px animate-spin" />
|
||||||
|
系统自动归档中,无需人工整理…
|
||||||
|
</div>
|
||||||
|
<div v-for="(s, i) in ARCHIVE_STEPS" :key="s" class="flex items-center gap-2 text-xs"
|
||||||
|
:class="i < step ? 'text-[var(--success-color)]' : i === step ? 'font-medium text-[var(--theme-color)]' : 'text-slate-400'">
|
||||||
|
<Icon v-if="i < step" icon="material-symbols:check-circle-outline" class="size-16px" />
|
||||||
|
<Icon v-else-if="i === step" icon="ri:loader-4-fill" class="size-16px animate-spin" />
|
||||||
|
<span v-else class="h-3.5 w-3.5 shrink-0 rounded-full border border-slate-300"></span>
|
||||||
|
{{ i + 1 }}. {{ s }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 归档完成 -->
|
||||||
|
<div v-if="archivedHash" class="mt-4 space-y-3 rounded-lg border border-emerald-300 bg-emerald-50/60 p-4">
|
||||||
|
<div class="flex flex-wrap items-center gap-2 text-sm font-semibold text-emerald-800">
|
||||||
|
<Icon icon="uil:lock" class="size-18px" /> 已关闭-已归档 · 归档包只读锁定,任何角色不可修改或删除
|
||||||
|
</div>
|
||||||
|
<div class="grid gap-1.5 text-xs text-emerald-800/90 sm:grid-cols-2">
|
||||||
|
<span>关闭时间:{{ closedAt }} · 关闭人:王海燕(变更管理专责 · 安全环保部)</span>
|
||||||
|
<span>归档路径:归档区 / 01-常减压装置 / 2026 / MOC-2026-R01-0039/</span>
|
||||||
|
<span class="flex items-center gap-1"><Hash class="h-3 w-3" /> SHA-256:{{ archivedHash }}</span>
|
||||||
|
<span>归档内容:6 类文件夹 + 归档索引.txt(共 24 个文件,18.4MB)</span>
|
||||||
|
<span>防篡改:系统定期校验哈希值,异常自动告警并从备份恢复</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<NButton size="small" type="success" class="text-xs" @click="emit('go', 'knowledge')">
|
||||||
|
<Icon icon="lucide:folder-archive" class="size-14px mr-1" /> 查看归档包
|
||||||
|
</NButton>
|
||||||
|
<NButton size="small" type="primary" class="text-xs" @click="toast.success('演示:下载完整归档包(ZIP,用于外部审计)')">
|
||||||
|
<Icon icon="material-symbols:download" class="size-18px" /> 下载 ZIP
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NCard>
|
||||||
|
<NCard :bordered="true" :content-style="{ padding: '0' }" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||||
|
<div class="py-3 px-4 border-b flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-2 text-base font-semibold">
|
||||||
|
<Icon icon="proicons:alert-triangle" class="size-18px text-slate-500" />关闭提醒
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2 p-4">
|
||||||
|
<div v-for="o in OVERDUE_LIST" :key="o.id" class="flex items-center justify-between rounded-lg border border-amber-200 bg-amber-50 px-3 py-2">
|
||||||
|
<div class="min-w-0">
|
||||||
|
<div class="truncate text-sm">{{ o.title }}</div>
|
||||||
|
<div class="text-xs text-slate-500">{{ o.id }} · {{ o.stage }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<NTag size="small" type="error">超期 {{ o.days }} 天</NTag>
|
||||||
|
<NButton size="small" class="text-xs" @click="toast.info('演示:已催办(系统消息 + 企业微信)')">催办</NButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NCard>
|
||||||
|
</div>
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
@@ -45,7 +45,6 @@ const { domRef, updateOptions } = useEcharts(() => ({
|
|||||||
},
|
},
|
||||||
formatter: function (name: string) {
|
formatter: function (name: string) {
|
||||||
var info = props.data?.find((item: { name: string }) => item.name === name);
|
var info = props.data?.find((item: { name: string }) => item.name === name);
|
||||||
console.group(info)
|
|
||||||
if(info){
|
if(info){
|
||||||
return '{name|'+name+'} {count|'+(info.value)+' 起}{rate|'+(info.rate)+' %} {money|'+(info.money)+' 万元}';
|
return '{name|'+name+'} {count|'+(info.value)+' 起}{rate|'+(info.rate)+' %} {money|'+(info.money)+' 万元}';
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
Reference in New Issue
Block a user