删除多余路由
This commit is contained in:
+474
-539
File diff suppressed because it is too large
Load Diff
@@ -1,565 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed, onMounted } from 'vue';
|
|
||||||
import { Icon } from '@iconify/vue'
|
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
|
||||||
import { tagTextColor, tagBgColor, tagBorderColor } from '@/utils/common';
|
|
||||||
import { systemConfigApi } from '@/service/api/system';
|
|
||||||
import { myTasksListApi, myTasksWithdrawApi, myTasksCloseApi } from '@/service/api/myTasks';
|
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
|
||||||
const { routerPushByKey } = useRouterPush();
|
|
||||||
|
|
||||||
const loading = ref<boolean>(false);
|
|
||||||
// 当前行
|
|
||||||
const currentRow = ref<any>(null);
|
|
||||||
// 进度抽屉
|
|
||||||
const progressDrawer = ref<boolean>(false);
|
|
||||||
// 撤回弹框
|
|
||||||
const revokeModal = ref<boolean>(false);
|
|
||||||
const revokeSaving = ref<boolean>(false);
|
|
||||||
// 撤回原因
|
|
||||||
const revokeReason = ref<string>("");
|
|
||||||
// 处置申请弹框
|
|
||||||
const disposalModal = ref<boolean>(false);
|
|
||||||
// 变更类型
|
|
||||||
const typeList = ref<any[]>([]);
|
|
||||||
// 变更等级
|
|
||||||
const levelList = ref<any[]>([]);
|
|
||||||
// 提醒天数
|
|
||||||
const remindDays = ref<number>(7);
|
|
||||||
// 源数据
|
|
||||||
const dataSource = ref<any>(null);
|
|
||||||
// 列表
|
|
||||||
const list = ref<any[]>([]);
|
|
||||||
// 数量统计
|
|
||||||
const tabCount = ref<Record<string, number>>({
|
|
||||||
"all": 0,
|
|
||||||
"approval": 0,
|
|
||||||
"apply": 0,
|
|
||||||
"reject": 0,
|
|
||||||
});
|
|
||||||
// 选项卡
|
|
||||||
const currentTab = ref<string>("all");
|
|
||||||
// 选项卡切换
|
|
||||||
const tabChange = (k: string) => {
|
|
||||||
currentTab.value = k;
|
|
||||||
// 全部
|
|
||||||
if(k === "all"){
|
|
||||||
list.value = resetGrouped([...dataSource.value.my_applications,...dataSource.value.todo]);
|
|
||||||
}
|
|
||||||
// 待我审批
|
|
||||||
if(k === "approval"){
|
|
||||||
list.value = resetGrouped([...dataSource.value.todo]);
|
|
||||||
}
|
|
||||||
// 我的申请
|
|
||||||
if(k === "apply"){
|
|
||||||
list.value = resetGrouped([...dataSource.value.my_applications]);
|
|
||||||
}
|
|
||||||
// 驳回处理
|
|
||||||
if(k === "reject"){
|
|
||||||
const arr = [...dataSource.value.my_applications,...dataSource.value.todo];
|
|
||||||
const rejectedList = arr.filter((item: any) => item.status === "REJECTED");
|
|
||||||
list.value = resetGrouped(rejectedList);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const SUB_ORDERS: any[] = [];
|
|
||||||
const subsOf = (id: string) => SUB_ORDERS.filter((s:any) => s.parentId === id);
|
|
||||||
const pendingSubs = computed(() => subsOf(currentRow.value?.id).filter((s:any) => s.status === "审批中"));
|
|
||||||
interface Opt { act: any; name: string; desc: string; disabled?: boolean; note?: string }
|
|
||||||
const convPassed = computed(() => subsOf(currentRow.value?.id).some((s) => s.kind === "permanent" && s.status === "已通过"));
|
|
||||||
const convActive = computed(() => subsOf(currentRow.value?.id).some((s) => s.kind === "permanent" && s.status !== "已驳回"));
|
|
||||||
const extActive = computed(() => subsOf(currentRow.value?.id).some((s) => s.kind === "extend" && s.status === "审批中"));
|
|
||||||
const opts = computed<Opt[]>(() => {
|
|
||||||
const list: Opt[] = [
|
|
||||||
{ act: "restore", name: "申请恢复", desc: "投用后申请恢复原状:生成恢复操作票(执行类单据,随单归档,无需另行审批),现场恢复并拍照确认后纳入归档" },
|
|
||||||
];
|
|
||||||
if (currentRow.value?.dur === "临时" && !convPassed.value) {
|
|
||||||
list.push(
|
|
||||||
{ act: "extend", name: currentRow.value?.extended ? "申请延期(已延期 1 次)" : "申请延期", desc: "临时变更到期前申请延期:子单走属地 + 主管部门简化审批,通过后回写原单到期时间 · 至多延期 1 次", disabled: !!currentRow.value?.extended || extActive.value, note: extActive.value ? "延期子单审批中" : currentRow.value?.extended ? "本变更已延期 1 次,不可再次延期" : "" },
|
|
||||||
{ act: "permanent", name: "申请转为永久", desc: "临时运行一个验证周期效果稳定后转永久:按永久变更补齐风险评估与完整审批链,通过后原临时变更自动关闭", disabled: convActive.value, note: convActive.value ? "转永久子单审批中 / 已通过" : "" },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
list.push(
|
|
||||||
{ act: "unimpl", name: "未实施情况说明", desc: "审批通过后变更未实施:填写情况说明(未实施原因、现场维持原状确认),知会原审批人后直接转入关闭流程" },
|
|
||||||
{ act: "unuse", name: "未投用情况说明", desc: "变更已实施完成但未正式投用:填写情况说明(含现场状态与后续安排),知会原审批人后直接转入关闭流程" },
|
|
||||||
);
|
|
||||||
return list;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 打开详情、修改重报
|
|
||||||
const openDetail = (c: any,type:string = '') => {
|
|
||||||
if(type==='detail'){
|
|
||||||
routerPushByKey("my_highriskdetail",{ query: { id: c.change_id.toString() } });
|
|
||||||
}
|
|
||||||
if(type==='edit'){
|
|
||||||
routerPushByKey("my_initiatechange",{ query: { id: c.change_id.toString() } });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// 打开资料关闭
|
|
||||||
const openDocClose = (c: any) => {
|
|
||||||
currentRow.value = c;
|
|
||||||
// routerPushByKey("my_docclose",{ query: { id: c.change_id.toString() } });
|
|
||||||
};
|
|
||||||
// 打开处置申请
|
|
||||||
const openDisposal = (c: any,) => {
|
|
||||||
currentRow.value = c;
|
|
||||||
disposalModal.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭变更
|
|
||||||
const closeChange = async (c: any) => {
|
|
||||||
const {error} = await myTasksCloseApi(c?.change_id);
|
|
||||||
if(!error){
|
|
||||||
window.$message?.success(`关闭变更成功`);
|
|
||||||
getList();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// 打开进度
|
|
||||||
const openProgress = (c: any) => {
|
|
||||||
currentRow.value = c;
|
|
||||||
progressDrawer.value = true;
|
|
||||||
};
|
|
||||||
// 打开撤回
|
|
||||||
const openWithdraw = (c: any) => {
|
|
||||||
revokeReason.value = "";
|
|
||||||
currentRow.value = c;
|
|
||||||
revokeModal.value = true;
|
|
||||||
};
|
|
||||||
// 撤回确认
|
|
||||||
const revokeSave = async () => {
|
|
||||||
if (!revokeReason.value) {
|
|
||||||
return window.$message?.warning("请填写撤回理由");
|
|
||||||
}
|
|
||||||
revokeSaving.value = true;
|
|
||||||
const {error} = await myTasksWithdrawApi(currentRow.value?.change_id,{reason:revokeReason.value});
|
|
||||||
if(!error){
|
|
||||||
revokeModal.value = false;
|
|
||||||
window.$message?.success(`撤回成功,申请退回草稿箱,审批记录保留可查,修改后可重新提交`);
|
|
||||||
getList();
|
|
||||||
}
|
|
||||||
revokeSaving.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 匹配变更等级
|
|
||||||
const getChangeLevel = (level:number) => {
|
|
||||||
const changeLevel = levelList.value.find((item:any) => item.value == level);
|
|
||||||
return changeLevel || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按 status 归纳
|
|
||||||
const resetGrouped = (data:any) => {
|
|
||||||
const grouped = Object.values(
|
|
||||||
data.reduce((acc:any, item:any) => {
|
|
||||||
const key = item.status;
|
|
||||||
if (!acc[key]) {
|
|
||||||
acc[key] = { status: key, items: [] };
|
|
||||||
}
|
|
||||||
acc[key].items.push(item);
|
|
||||||
return acc;
|
|
||||||
}, {})
|
|
||||||
);
|
|
||||||
return grouped;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取列表数据
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true;
|
|
||||||
const {data,error} = await myTasksListApi();
|
|
||||||
if(!error){
|
|
||||||
dataSource.value = data;
|
|
||||||
tabChange(currentTab.value);
|
|
||||||
// 统计数量
|
|
||||||
// 全部
|
|
||||||
tabCount.value["all"] = [...dataSource.value.my_applications,...dataSource.value.todo].length;
|
|
||||||
// 待我审批
|
|
||||||
tabCount.value["approval"] = [...dataSource.value.todo].length;
|
|
||||||
// 我的申请
|
|
||||||
tabCount.value["apply"] = [...dataSource.value.my_applications].length;
|
|
||||||
// 驳回处理
|
|
||||||
const arr = [...dataSource.value.my_applications,...dataSource.value.todo];
|
|
||||||
const rejectedList = arr.filter((item: any) => item.status === "REJECTED");
|
|
||||||
tabCount.value["reject"] = rejectedList.length;
|
|
||||||
}
|
|
||||||
loading.value = false;
|
|
||||||
};
|
|
||||||
// 获取配置
|
|
||||||
const getConfig = async () => {
|
|
||||||
loading.value = true;
|
|
||||||
const {data,error} = await systemConfigApi();
|
|
||||||
if(!error){
|
|
||||||
// 解析 变更类型设置
|
|
||||||
const changeTypeGroup = data.find((group:{group_key:string}) => group.group_key === 'change_type');
|
|
||||||
if (changeTypeGroup) {
|
|
||||||
typeList.value = changeTypeGroup.items;
|
|
||||||
}
|
|
||||||
// 解析 变更等级设置
|
|
||||||
const changeLevelGroup = data.find((group:{group_key:string}) => group.group_key === 'change_level');
|
|
||||||
if (changeLevelGroup) {
|
|
||||||
levelList.value = changeLevelGroup.items;
|
|
||||||
}
|
|
||||||
getList();
|
|
||||||
}
|
|
||||||
loading.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getConfig();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NSpace vertical :size="16">
|
|
||||||
<NSpin :show="loading" size="small">
|
|
||||||
<!-- 我的任务 -->
|
|
||||||
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
|
||||||
<div
|
|
||||||
class="h-100% flex flex-wrap items-center gap-2 border border-slate-200 px-3 py-2 mb-3"
|
|
||||||
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
|
|
||||||
>
|
|
||||||
<div @click="tabChange('all')"
|
|
||||||
class="flex items-center gap-1.5 px-3 py-1.5 text-sm cursor-pointer select-none"
|
|
||||||
:style="{
|
|
||||||
borderRadius: themeStore.themeRadius + 'px',
|
|
||||||
'--theme-color': themeStore.themeColor,
|
|
||||||
}"
|
|
||||||
:class="currentTab === 'all' ? 'bg-[var(--theme-color)] font-medium text-white' : 'text-slate-600 hover:bg-slate-100'">
|
|
||||||
全部
|
|
||||||
<NTag size="tiny" round :type="currentTab === 'all' ?'default':'primary'">{{tabCount["all"]}}</NTag>
|
|
||||||
</div>
|
|
||||||
<div @click="tabChange('approval')"
|
|
||||||
class="flex items-center gap-1.5 px-3 py-1.5 text-sm cursor-pointer select-none"
|
|
||||||
:style="{
|
|
||||||
borderRadius: themeStore.themeRadius + 'px',
|
|
||||||
'--theme-color': themeStore.themeColor,
|
|
||||||
}"
|
|
||||||
:class="currentTab === 'approval' ? 'bg-[var(--theme-color)] font-medium text-white' : 'text-slate-600 hover:bg-slate-100'">
|
|
||||||
待我审批
|
|
||||||
<NTag size="tiny" round :type="currentTab === 'approval' ?'default':'primary'">{{tabCount["approval"]}}</NTag>
|
|
||||||
</div>
|
|
||||||
<div @click="tabChange('apply')"
|
|
||||||
class="flex items-center gap-1.5 px-3 py-1.5 text-sm cursor-pointer select-none"
|
|
||||||
:style="{
|
|
||||||
borderRadius: themeStore.themeRadius + 'px',
|
|
||||||
'--theme-color': themeStore.themeColor,
|
|
||||||
}"
|
|
||||||
:class="currentTab === 'apply' ? 'bg-[var(--theme-color)] font-medium text-white' : 'text-slate-600 hover:bg-slate-100'">
|
|
||||||
我的申请
|
|
||||||
<NTag size="tiny" round :type="currentTab === 'apply' ?'default':'primary'">{{tabCount["apply"]}}</NTag>
|
|
||||||
</div>
|
|
||||||
<div @click="tabChange('reject')"
|
|
||||||
class="flex items-center gap-1.5 px-3 py-1.5 text-sm cursor-pointer select-none"
|
|
||||||
:style="{
|
|
||||||
borderRadius: themeStore.themeRadius + 'px',
|
|
||||||
'--theme-color': themeStore.themeColor,
|
|
||||||
}"
|
|
||||||
:class="currentTab === 'reject' ? 'bg-[var(--theme-color)] font-medium text-white' : 'text-slate-600 hover:bg-slate-100'">
|
|
||||||
驳回处理
|
|
||||||
<NTag size="tiny" round :type="currentTab === 'reject' ?'default':'error'">{{tabCount["reject"]}}</NTag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 临时变更临期提醒 -->
|
|
||||||
<div
|
|
||||||
class="flex flex-wrap items-center gap-2 border border-slate-200 px-3 py-2 text-xs text-slate-400 mb-3"
|
|
||||||
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
|
|
||||||
>
|
|
||||||
<Icon icon="lets-icons:clock" class="size-16px text-amber-600" />
|
|
||||||
临时变更临期提醒:到期前
|
|
||||||
<NInputNumber v-model:value="remindDays" :min="1" :max="30" size="tiny" placeholder="" :show-button="false" class="text-11px w-14 text-center" />
|
|
||||||
天提醒(可设置)· 超期红色、临期琥珀色标识
|
|
||||||
<span class="ml-auto">
|
|
||||||
共 {{tabCount["all"]}} 项 · 点击行内按钮办理各阶段业务
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="space-y-2 scroll">
|
|
||||||
<div class="flex flex-col gap-3">
|
|
||||||
<div v-if="list.length===0" class="text-center text-slate-400">
|
|
||||||
暂无数据
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="border border-slate-200 p-4"
|
|
||||||
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
|
|
||||||
v-for="(item,index) in list"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<!-- 审批中 -->
|
|
||||||
<div v-if="item.status==='APPROVING'" class="flex items-center gap-2">
|
|
||||||
<Icon icon="ant-design:clock-circle-outlined" class="size-16px" :style="{ color: themeStore.otherColor.warning }" />
|
|
||||||
<p class="text-13px font-700" :style="{ color: themeStore.otherColor.warning }">审批中</p>
|
|
||||||
<p class="text-11px text-#94a3b8">共 {{item.items.length}} 项 · 审批流程推进中 · 可查看进度或撤回</p>
|
|
||||||
</div>
|
|
||||||
<!-- 已通过 -->
|
|
||||||
<div v-if="item.status==='APPROVED'" class="flex items-center gap-2">
|
|
||||||
<Icon icon="ix:success" class="size-16px" :style="{ color: themeStore.otherColor.success }" />
|
|
||||||
<p class="text-13px font-700" :style="{ color: themeStore.otherColor.success }">审批通过</p>
|
|
||||||
<p class="text-11px text-#94a3b8">共 {{item.items.length}} 项 · 待实施 / 待验收 / 待关闭 · 处置申请与资料关闭在行内办理</p>
|
|
||||||
</div>
|
|
||||||
<!-- 已驳回 -->
|
|
||||||
<div v-if="item.status==='REJECTED'" class="flex items-center gap-2">
|
|
||||||
<Icon icon="system-uicons:reset" class="size-16px" :style="{ color: themeStore.otherColor.error }" />
|
|
||||||
<p class="text-13px font-700" :style="{ color: themeStore.otherColor.error }">驳回处理</p>
|
|
||||||
<p class="text-11px text-#94a3b8">{{item.items.length}} 项待处理</p>
|
|
||||||
</div>
|
|
||||||
<div v-if="item.items.length===0" class="text-center text-slate-400 mt-3 border-t pt-4 text-xs">
|
|
||||||
暂无数据
|
|
||||||
</div>
|
|
||||||
<div v-if="item.items && item.items.length" class="flex flex-col gap-3 mt-3">
|
|
||||||
<div v-for="c in item.items" :key="c.change_no"
|
|
||||||
class="flex flex-wrap items-center gap-3 border px-4 py-3"
|
|
||||||
:class="c.overdue_status===2 ? 'border-red-200 bg-[#FFF2F0]' : c.overdue_status===1 ? 'border-amber-200 bg-amber-50/40' : ''"
|
|
||||||
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
|
|
||||||
>
|
|
||||||
<!-- 左区 -->
|
|
||||||
<!-- 审批中/已通过 -->
|
|
||||||
<div v-if="item.status==='APPROVING' || item.status==='APPROVED'" class="min-w-0 flex-1">
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
|
||||||
<!-- 变更编号 -->
|
|
||||||
<span class="font-mono text-[11px] text-slate-400">{{ c.change_no }}</span>
|
|
||||||
<!-- 变更等级 -->
|
|
||||||
<NTag
|
|
||||||
size="small"
|
|
||||||
:color="{
|
|
||||||
color: tagBgColor(getChangeLevel(c.change_level)?.color),
|
|
||||||
textColor: tagTextColor(getChangeLevel(c.change_level)?.color),
|
|
||||||
borderColor: tagBorderColor(getChangeLevel(c.change_level)?.color),
|
|
||||||
}"
|
|
||||||
>{{getChangeLevel(c.change_level)?.label}}</NTag>
|
|
||||||
<!-- 变更时限 -->
|
|
||||||
<NTag v-if="c.duration_type===2" size="small" type="warning">临时</NTag>
|
|
||||||
<NTag v-if="c.duration_type===1" size="small" type="primary">永久</NTag>
|
|
||||||
<NTag v-if="c.urgent===1" size="small" type="error">紧急</NTag>
|
|
||||||
<!-- 超期/临期/延期 -->
|
|
||||||
<NTag v-if="c.overdue_status===2" size="small" type="error">已超期 {{ c.overdue_days }} 天</NTag>
|
|
||||||
<NTag v-if="c.overdue_status===1" size="small" type="warning">临期 · 剩 {{ c.overdue_days }} 天</NTag>
|
|
||||||
<NTag v-if="c.extend_count" size="small">已延期 {{ c.extend_count }} 次</NTag>
|
|
||||||
<!-- 变更状态 -->
|
|
||||||
<NTag v-if="c.status==='APPROVING'" size="small" type="warning">审批中</NTag>
|
|
||||||
<NTag v-if="c.status==='APPROVED'" size="small" type="success">已通过</NTag>
|
|
||||||
</div>
|
|
||||||
<div class="mt-1 flex flex-wrap items-baseline gap-x-2">
|
|
||||||
<span class="text-sm font-medium text-slate-800">{{ c.title }}</span>
|
|
||||||
<span v-if="c.apply_time" class="text-[11px] text-slate-400">申请 {{ c.apply_time.slice(0, 16) }}</span>
|
|
||||||
</div>
|
|
||||||
<!-- 当前节点与会签成员进度;状态更新时间跟在审核人员名字后面 -->
|
|
||||||
<div class="mt-1 flex flex-wrap items-center gap-1.5 text-[11px]">
|
|
||||||
<span class="text-slate-400">当前节点</span>
|
|
||||||
<span class="font-medium text-slate-600">{{ c.step_name }}</span>
|
|
||||||
<div v-if="c.current_step_members && c.current_step_members.length" class="flex items-center gap-1">
|
|
||||||
<div v-for="(m, mi) in c.current_step_members" :key="mi">
|
|
||||||
<NTag v-if="m.status==='PENDING'" size="small" type="warning">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<Icon icon="tabler:clock" />{{ m.user_name }}
|
|
||||||
</div>
|
|
||||||
</NTag>
|
|
||||||
<NTag v-else size="small" type="success">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<Icon icon="ix:success" />{{ m.user_name }}
|
|
||||||
</div>
|
|
||||||
</NTag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span v-if="c.status_changed_at" class="text-slate-400">状态更新 {{ c.status_changed_at.slice(0, 16) }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 驳回处理 -->
|
|
||||||
<NTag v-if="item.status==='REJECTED'" size="small" type="error">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<Icon icon="system-uicons:reset" />驳回处理
|
|
||||||
</div>
|
|
||||||
</NTag>
|
|
||||||
<div v-if="item.status==='REJECTED'" class="min-w-0 flex-1">
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
|
||||||
<span class="text-sm font-medium text-slate-800">{{ c.title }}</span>
|
|
||||||
<span class="font-mono text-[11px] text-slate-400">{{ c.change_no }}</span>
|
|
||||||
</div>
|
|
||||||
<p class="px-2 py-1 text-xs border border-red-100 bg-red-50/60 px-2 py-1 text-red-600 rounded-md mt-1">
|
|
||||||
驳回原因:{{ c.reject_comment }}
|
|
||||||
</p>
|
|
||||||
<span v-if="c.rejected_at" class="text-11px text-slate-400">驳回于 {{ c.rejected_at.slice(0, 16) }}</span>
|
|
||||||
</div>
|
|
||||||
<!-- 右区 -->
|
|
||||||
<!-- 审批中 -->
|
|
||||||
<div v-if="c.status === 'APPROVING'" class="flex gap-1.5">
|
|
||||||
<NButton class="text-xs" text type="primary" @click="openDetail(c,'detail')">详情</NButton>
|
|
||||||
<NButton class="text-xs" text type="warning" @click="openProgress(c)">进度</NButton>
|
|
||||||
<NButton class="text-xs" text type="error" @click="openWithdraw(c)">撤回</NButton>
|
|
||||||
</div>
|
|
||||||
<!-- 审批通过 -->
|
|
||||||
<div v-if="c.status === 'APPROVED'" class="flex gap-1.5">
|
|
||||||
<NButton class="text-xs" text type="primary" @click="openDetail(c,'detail')">详情</NButton>
|
|
||||||
<NButton class="text-xs" text type="warning" @click="openProgress(c)">进度</NButton>
|
|
||||||
<NButton class="text-xs" text type="primary" @click="openDisposal(c)">处置申请</NButton>
|
|
||||||
<NButton class="text-xs" text type="error" @click="openDocClose(c)">资料关闭</NButton>
|
|
||||||
</div>
|
|
||||||
<!-- 驳回处理 -->
|
|
||||||
<div v-if="c.status === 'REJECTED'" class="flex gap-1.5">
|
|
||||||
<NButton class="text-xs" text type="primary" @click="openDetail(c,'detail')">详情</NButton>
|
|
||||||
<NButton class="text-xs" text type="warning" @click="openProgress(c)">进度</NButton>
|
|
||||||
<NButton class="text-xs" text type="primary" @click="openDetail(c,'edit')">修改重报</NButton>
|
|
||||||
<NPopconfirm
|
|
||||||
positive-text="确定"
|
|
||||||
:positiveButtonProps="{ size: 'tiny' }"
|
|
||||||
:negativeButtonProps="{ size: 'tiny' }"
|
|
||||||
@positive-click="closeChange(c)"
|
|
||||||
>
|
|
||||||
<template #trigger>
|
|
||||||
<NButton class="text-xs" text type="error">关闭变更</NButton>
|
|
||||||
</template>
|
|
||||||
确定关闭该变更吗?
|
|
||||||
</NPopconfirm>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 进度弹框 -->
|
|
||||||
<NDrawer v-model:show="progressDrawer" :width="502" placement="right">
|
|
||||||
<NDrawerContent title="变更进度">
|
|
||||||
<div class="rounded-lg border bg-slate-50/60 px-3 py-2.5">
|
|
||||||
<div class="text-sm font-semibold text-slate-800">{{ currentRow?.title }}</div>
|
|
||||||
<div class="mt-1.5 flex flex-wrap items-center gap-1.5 text-[11px] text-slate-400">
|
|
||||||
<span class="font-mono">{{ currentRow?.change_no }}</span>
|
|
||||||
<NTag v-if="currentRow?.status==='APPROVING'" size="small" type="warning">审批中</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='APPROVED'" size="small" type="success">已通过</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='IMPLEMENTED'" size="small" type="warning">待验收</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='ACCEPTED'" size="small" type="error">待关闭</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='REJECTED'" size="small" type="error">已驳回</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='CLOSED'" size="small" type="error">已关闭</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='WITHDRAWN'" size="small" type="error">已撤回</NTag>
|
|
||||||
<span v-if="currentRow?.overdue_status===2" class="font-medium text-red-500">超期 {{ currentRow?.overdue_days }} 天</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="currentRow?.dept || currentRow?.applicant" class="mt-1 text-[11px] text-slate-400">{{ [currentRow?.dept, currentRow?.applicant].filter(Boolean).join(' · ') }}</div>
|
|
||||||
<div v-if="currentRow?.meta" class="mt-1 text-[11px] text-slate-400">{{ currentRow?.meta }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="mt-4">
|
|
||||||
<div v-for="(s, i) in currentRow?.approval_records" :key="s.id" class="flex gap-3">
|
|
||||||
<div class="flex flex-col items-center">
|
|
||||||
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full border-2 border-[#7CC242] bg-[#7CC242] text-white">
|
|
||||||
<Icon icon="ix:success" />
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="Number(i) < Number(currentRow?.approval_records?.length) - 1"
|
|
||||||
class="w-0.5 flex-1"
|
|
||||||
:class="Number(i) < Number(currentRow?.approval_records?.length) ? 'bg-[#7CC242]' : 'bg-slate-200'"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
<div class="pb-5">
|
|
||||||
<p class="text-sm">{{ s.action }}</p>
|
|
||||||
<span class="mt-0.5 text-[11px] text-slate-400">{{s.finished_at?.slice(0, 16)}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="currentRow?.duration_type === 2" class="rounded-lg border border-amber-200 bg-amber-50/50 px-3 py-2 text-[11px] leading-5 text-amber-700">
|
|
||||||
临时变更:计划恢复 {{ currentRow?.restore_deadline ?? '—' }} · 恢复 / 延期 / 转永久请在详情页中操作
|
|
||||||
</div>
|
|
||||||
</NDrawerContent>
|
|
||||||
</NDrawer>
|
|
||||||
<!-- 撤回弹框 -->
|
|
||||||
<NModal
|
|
||||||
v-model:show="revokeModal"
|
|
||||||
preset="card"
|
|
||||||
:auto-focus="false"
|
|
||||||
:style="{ width: '450px', height: 'auto' }"
|
|
||||||
:segmented="{ content: true, footer: true }"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<h3 class="text-sm font-bold text-slate-800">撤回变更申请</h3>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<div class="rounded-lg bg-slate-50 px-3 py-2 text-xs leading-5 text-slate-500">
|
|
||||||
变更编号 {{ currentRow?.id }}:撤回后申请退回草稿状态,已产生的审批记录保留可查;修改内容后可重新提交,重新提交按新申请计。
|
|
||||||
</div>
|
|
||||||
<NInput
|
|
||||||
class="mt-3"
|
|
||||||
v-model:value="revokeReason"
|
|
||||||
type="textarea"
|
|
||||||
:rows="3"
|
|
||||||
placeholder="撤回理由(必填):如补充材料、变更范围调整、需重新组织风险分析…"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<div class="flex justify-end gap-[10px]">
|
|
||||||
<NButton size="small" @click="revokeModal = false">取消</NButton>
|
|
||||||
<NButton size="small" type="primary" :loading="revokeSaving" @click="revokeSave">保存</NButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
<!-- 处置申请弹框 -->
|
|
||||||
<NModal
|
|
||||||
v-model:show="disposalModal"
|
|
||||||
preset="card"
|
|
||||||
:auto-focus="false"
|
|
||||||
:style="{ width: '670px', height: 'auto' }"
|
|
||||||
:segmented="{ content: true, footer: false }"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center gap-2 flex-wrap">
|
|
||||||
<h3 class="text-base font-semibold text-slate-800">处置申请 · {{ currentRow?.title }}</h3>
|
|
||||||
<span class="shrink-0 border-slate-200 bg-slate-100 font-mono text-xs text-slate-600 px-1.5 py-0.5 border rounded-md">编号:{{ currentRow?.id }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<div class="rounded-lg border bg-white px-4 py-3">
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
|
||||||
<NTag v-if="currentRow?.level=== '重要'" type="error" size="small">{{ currentRow?.level }}</NTag>
|
|
||||||
<NTag v-else type="primary" size="small">{{ currentRow?.level }}</NTag>
|
|
||||||
<NTag v-if="currentRow?.dur=== '临时'" type="warning" size="small">{{ currentRow?.dur }}</NTag>
|
|
||||||
<NTag v-else type="primary" size="small">{{ currentRow?.dur }}</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='审批中'" size="small" type="warning">{{currentRow?.status}}</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='已批准'" size="small" type="primary">{{currentRow?.status}}</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='实施中'" size="small" type="warning">{{currentRow?.status}}</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='待PSSR'" size="small" type="warning">{{currentRow?.status}}</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='待验收'" size="small" type="success">{{currentRow?.status}}</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='待关闭'" size="small" type="error">{{currentRow?.status}}</NTag>
|
|
||||||
<NTag v-if="currentRow?.status==='已关闭'" size="small" type="primary">{{currentRow?.status}}</NTag>
|
|
||||||
<span v-if="currentRow?.applyTime" class="text-[11px] text-slate-400">申请 {{ currentRow?.applyTime }}</span>
|
|
||||||
<span v-if="currentRow?.dur === '临时' && currentRow?.deadline" class="text-[11px] text-amber-600">
|
|
||||||
到期 {{ currentRow?.deadline }}{{ (currentRow?.day ?? 0) < 0 ? ` · 已超期 ${-(currentRow?.day ?? 0)} 天` : currentRow?.day !== undefined ? ` · 剩 ${currentRow?.day} 天` : '' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="mt-1 text-[11px] text-slate-400">{{ currentRow?.detail }}</div>
|
|
||||||
</div>
|
|
||||||
<!-- 进行中的子单提示 -->
|
|
||||||
<div v-if="pendingSubs.length" class="rounded-lg border border-amber-200 bg-amber-50/60 px-4 py-2.5 text-xs text-amber-700">
|
|
||||||
已有子单在审批中:{{ pendingSubs.map((s:any) => s.id).join('、') }} · 审批进度见「待我处理」列表行内展示
|
|
||||||
</div>
|
|
||||||
<!-- 处置选项 -->
|
|
||||||
<div class="rounded-lg border bg-white mt-3">
|
|
||||||
<div class="border-b px-4 py-2.5 text-sm font-bold text-slate-800">
|
|
||||||
选择处置方式
|
|
||||||
<span class="ml-2 text-[11px] font-normal text-slate-400">申请恢复所有变更可发起;申请延期 / 申请转为永久仅临时变更可用</span>
|
|
||||||
</div>
|
|
||||||
<div class="divide-y">
|
|
||||||
<div v-for="o in opts" :key="o.act" class="flex flex-wrap items-center gap-3 px-4 py-3.5">
|
|
||||||
<div class="min-w-0 flex-1">
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
|
||||||
<span class="text-sm font-semibold text-slate-800">{{ o.name }}</span>
|
|
||||||
<NTag v-if="o.note" type="warning" size="small">{{ o.note }}</NTag>
|
|
||||||
</div>
|
|
||||||
<div class="mt-0.5 text-[11px] leading-4 text-slate-400">{{ o.desc }}</div>
|
|
||||||
</div>
|
|
||||||
<NButton size="small" type="primary" :disabled="o.disabled">
|
|
||||||
发起<Icon icon="formkit:right" class="size-12px text-white ml-1" />
|
|
||||||
</NButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
</NCard>
|
|
||||||
</NSpin>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.scroll {
|
|
||||||
height: calc(100vh - 260px);
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Reference in New Issue
Block a user