待投用变更页面完成

This commit is contained in:
2026-09-24 16:03:36 +08:00
parent c6ebecda4b
commit 252b960f04
2 changed files with 159 additions and 120 deletions
+149 -120
View File
@@ -1,101 +1,110 @@
<!-- 待投用变更 -->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
import { getColorWithOpacity } from '@/utils/common';
import { localStg } from '@/utils/storage';
import previewComponent from '@/components/previewFile/index.vue'
import { tagTextColor, tagBgColor, tagBorderColor, downloadFileFun } from '@/utils/common';
import { systemConfigApi } from '@/service/api/system';
import { pendingChangeListApi } from '@/service/api/pendingChange';
const themeStore = useThemeStore();
const { routerPushByKey } = useRouterPush();
const loading = ref<boolean>(false);
const btnLoading = ref<boolean>(false);
const useTime = ref<any>(null);
const useTimeModal = ref<boolean>(false);
const currentRow = ref<any>(null);
// 组织
const org = ref<string>(localStg.get('userInfo')?.department?.label || '');
// 列表
const list = ref<any[]>([]);
const lists = [
{
id: "MOC-2026-R02-0038",statusDetail: "培训 / PSSR 资料已传 · 待确认投用",
approveTime: "2026-07-31", applicant: "吴强", level: "一般", type: "工艺",
title: 'DCS 罐区液位报警值 LIA-301 调整', status: '待PSSR', duration: "永久",
useTime: "", planUse: "2026-08-01", pssr: "PSSR 检查确认表-签字版.pdf",
hasChange: true,
},
{
id: "MOC-2026-R02-0038",statusDetail: "培训 / PSSR 资料已传 · 待确认投用",
approveTime: "2026-07-31", applicant: "吴强", level: "重要", type: "工艺",
title: 'DCS 罐区液位报警值 LIA-301 调整', status: '待PSSR', duration: "临时",
useTime: "2026-07-16 06:30", planUse: "2026-08-01", pssr: "",
hasChange: true,
},
];
const timePickerProps = {
format: 'HH:mm', // 时间选择器面板上显示的格式
seconds: false // 隐藏秒选择器[reference:3][reference:4]
}
// 禁用今天之前的日期(今天可选)
const disabledDate = (ts: number) => {
const today = new Date()
today.setHours(0, 0, 0, 0) // 今天的 00:00:00
return ts < today.getTime() // 时间戳小于今天 0 点则禁用
}
// 获取列表数据
const getList = () => {
loading.value = true;
setTimeout(() => {
list.value = lists;
loading.value = false;
}, 1000);
}
// 分页
const pagination = ref<{page: number, pageSize: number, itemCount: number}>({
page: 1,
pageSize: 20,
itemCount: 0,
})
// 变更类型列表
const typeList = ref<any[]>([]);
// 变更等级列表
const levelList = ref<any[]>([]);
// 附件在线预览弹窗
const previewModal = ref<boolean>(false);
// 附件在线预览文件
const previewFile = ref<any>(null);
// 查看
const viewFile = (record: any) => {
window.$message?.info(`预览「${record.pssr}」(上传件不做内容识别,原件已随单归档)`)
const viewFile = (file: any) => {
previewFile.value = file;
previewModal.value = true;
};
// 下载
const downloadFile = (record: any) => {
window.$message?.success(`下载「${record.pssr}」`)
const downloadFile = (file: any) => {
downloadFileFun(file.file_url, file.file_name);
};
// 打开变更单
const openChange = (record: any) => {
routerPushByKey('details_index', { query: { params: JSON.stringify(record) } })
routerPushByKey("details_index",{ query: { id: record?.id?.toString(),status:record?.status?.toString() || '',key:'my_pendingchange' } });
};
// 填写投用日期
const fillUseTime = (record: any) => {
useTime.value = null;
currentRow.value = record;
useTimeModal.value = true;
};
// 保存投用日期
const saveUseTime = () => {
if (!useTime.value) {
return window.$message?.warning("请选择投用时间");
}
btnLoading.value = true;
setTimeout(() => {
btnLoading.value = false;
useTimeModal.value = false;
}, 1000);
// 分页改变
const pageChange = (page: number) => {
pagination.value.page = page;
getList();
}
// 匹配对象
const matchObj= (list:any[],val:any) => {
const item = list.find((item:any) => item.value == val);
return item || null;
}
// 获取列表数据
const getList = async () => {
loading.value = true;
const {data,error} = await pendingChangeListApi({
page_num:pagination.value.page,
page_size:pagination.value.pageSize,
});
if(!error){
list.value = data?.list || [];
pagination.value.itemCount = data?.total || 0;
}
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 = typeList.value.concat(changeTypeGroup.items);
}
// 解析 变更等级设置
const changeLevelGroup = data.find((group:{group_key:string}) => group.group_key === 'change_level');
if (changeLevelGroup) {
levelList.value = levelList.value.concat(changeLevelGroup.items);
}
getList();
}
loading.value = false;
}
onMounted(() => {
getList();
getConfig();
})
</script>
<template>
<NSpace vertical :size="16">
<!-- PSSR监督 -->
<NSpin :show="loading" size="small">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
<div class="flex flex-wrap items-center gap-2 mb-3">
<span class="text-sm font-semibold text-slate-700">待投用变更(本车间 · 一车间)</span>
<NTag size="small" type="primary">{{ list.length }} 起</NTag>
<span class="text-sm font-semibold text-slate-700">待投用变更(本车间 · {{ org }})</span>
<NTag size="small" type="primary">{{ pagination.itemCount }} 起</NTag>
<span class="ml-auto text-[11px] text-slate-400">按审批通过时间排序 · 投用时间由发起人在资料关闭页填报 · 支持属地负责人在此代录投用日期</span>
</div>
<div class="scroll">
@@ -107,28 +116,51 @@ onMounted(() => {
}"
>
<div v-for="(s, i) in list" :key="`${s.id ?? 'x'}-${i}`" class="flex flex-wrap items-center gap-x-3 gap-y-2 border p-4" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
<NTag v-if="s.status==='待PSSR'" type="warning" size="small">{{ s.status }}</NTag>
<NTag v-if="s.status==='待关闭'" type="error" size="small">{{ s.status }}</NTag>
<NTag v-if="s.status==='实施中'" type="primary" size="small">{{ s.status }}</NTag>
<NTag v-if="s.status==='待验收'" type="success" size="small">{{ s.status }}</NTag>
<!-- 状态标签 -->
<NTag v-if="s.status==='APPROVING'" size="small" type="warning">审批中</NTag>
<NTag v-if="s.status==='APPROVED'" size="small" type="success">已通过</NTag>
<NTag v-if="s.status==='IMPLEMENTED'" size="small" type="success">待验收</NTag>
<NTag v-if="s.status==='ACCEPTED'" size="small" type="error">待关闭</NTag>
<NTag v-if="s.status==='CLOSED'" size="small" type="success">已关闭</NTag>
<NTag v-if="s.status==='WITHDRAWN'" size="small" type="error">已撤回</NTag>
<NTag v-if="s.status==='REJECTED'" size="small" type="error">已驳回</NTag>
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-1.5">
<p class="truncate text-sm font-medium text-slate-800">{{ s.title }}</p>
<NTag v-if="s.level==='一般'" size="small">{{s.level}}</NTag>
<NTag v-if="s.level==='重要'" size="small" type="error">{{s.level}}</NTag>
<NTag size="small" type="primary">{{s.type}}</NTag>
<NTag v-if="s.duration==='永久'" size="small">{{s.duration}}</NTag>
<NTag v-if="s.duration==='临时'" size="small" type="warning">{{s.duration}}</NTag>
<!-- 等级 -->
<NTag
size="small"
:color="{
color: tagBgColor(matchObj(levelList,s.change_level)?.color),
textColor: tagTextColor(matchObj(levelList,s.change_level)?.color),
borderColor: tagBorderColor(matchObj(levelList,s.change_level)?.color),
}"
>
{{ matchObj(levelList,s.change_level)?.label }}
</NTag>
<!-- 类型 -->
<NTag
size="small"
:color="{
color: tagBgColor(matchObj(typeList,s.change_type)?.color),
textColor: tagTextColor(matchObj(typeList,s.change_type)?.color),
borderColor: tagBorderColor(matchObj(typeList,s.change_type)?.color),
}"
>
{{ matchObj(typeList,s.change_type)?.label }}
</NTag>
<NTag v-if="s.duration_type===2" size="small" type="warning">临时</NTag>
<NTag v-if="s.duration_type===1" size="small" type="info">永久</NTag>
</div>
<p class="mt-0.5 text-[11px] text-slate-400">{{ s.id }} · {{ s.applicant }}</p>
<p class="text-slate-400 text-[11px]">审批通过时间:<span class="font-medium text-slate-600">{{ s.approveTime ?? '—' }}</span></p>
<p class="text-slate-400 text-[11px]">变更投用时间:<span class="font-medium text-[var(--warning-color)]">{{ s.useTime ? s.useTime : `待投用(计划 ${s.planUse})` }}</span></p>
<p class="mt-0.5 text-[11px] text-slate-400">{{ s.statusDetail }}</p>
<p class="mt-0.5 text-[11px] text-slate-400">{{ s.change_no }} · {{ s.applicant_name }}</p>
<p class="text-slate-400 text-[11px]">审批通过时间:<span class="font-medium text-slate-600">{{ s.approve_time?.slice(0,16) }}</span></p>
<p class="text-slate-400 text-[11px]">变更投用时间:<span class="font-medium text-[var(--warning-color)]">{{ s.use_time ? s.use_time?.slice(0,10) : `待投用(计划 ${s.plan_use_date?.slice(0,10)})` }}</span></p>
</div>
<div class="flex items-center gap-1.5 rounded-md border bg-slate-50/60 px-2 py-1">
<Icon icon="akar-icons:file" class="size-14px text-[var(--theme-color)]" />
<span class="text-[11px] font-medium text-slate-600">PSSR 确认表</span>
<template v-if="s.pssr">
<template v-if="s.pssr_attachment">
<NButton ghost size="tiny" class="text-11px" @click="viewFile(s)">查看</NButton>
<NButton ghost size="tiny" class="text-11px" @click="downloadFile(s)">下载</NButton>
</template>
@@ -136,60 +168,57 @@ onMounted(() => {
<span class="text-[11px] text-[var(--warning-color)]">待传</span>
</template>
</div>
<NButton v-if="!s.useTime" size="small" type="primary" class="text-xs" @click="fillUseTime(s)">填写投用日期</NButton>
<NButton v-if="s.hasChange" size="small" ghost class="text-xs" @click="openChange(s)">打开变更单</NButton>
<NButton size="small" ghost class="text-xs" @click="openChange(s)">打开变更单</NButton>
</div>
<div v-if="list.length === 0" class="h-100% flex items-center justify-center">
<NEmpty description="暂无数据"></NEmpty>
</div>
</div>
</div>
</NCard>
<!-- 填写投用日期弹窗 -->
<NModal
v-model:show="useTimeModal"
preset="card"
title="填写投用日期"
:auto-focus="false"
:style="{ width: '450px', height: 'auto' }"
:segmented="{ content: true, footer: true }"
>
<template #default>
<div
class="space-y-3 text-sm"
:style="{
'--warning-bg': getColorWithOpacity(themeStore.otherColor.warning, 0.1),
'--warning-color': themeStore.otherColor.warning,
}"
<div class="flex justify-end mt-3">
<NPagination
v-model:page="pagination.page"
:page-size="pagination.pageSize"
:item-count="pagination.itemCount"
:on-update:page="pageChange"
>
<p class="text-xs text-slate-500">{{ currentRow?.id }} · {{ currentRow?.title }}</p>
<div class="rounded-lg border border-[var(--warning-color)] bg-[var(--warning-bg)] px-3 py-2 text-xs leading-relaxed text-[var(--warning-color)]">
提示:投用前应完成变更培训与 PSSR 确认(资料已上传)。投用后变更进入待验收阶段,临时变更自投用日起计算时限。
</div>
<NDatePicker
v-model:value="useTime"
type="datetime"
:is-date-disabled="disabledDate"
format="yyyy-MM-dd HH:mm"
placeholder="请选择投用时间"
:time-picker-props="timePickerProps"
/>
<template #prefix="{ itemCount }">
共 {{ itemCount }} 条
</template>
</NPagination>
</div>
</NCard>
<!-- 附件在线预览弹窗 -->
<NModal
v-model:show="previewModal"
preset="card"
:auto-focus="false"
:style="{ width: '80%', height: 'auto' }"
:segmented="{ content: true, footer: true }"
>
<template #header>
<div class="flex items-center gap-2 text-base font-semibold text-slate-800">
<Icon icon="akar-icons:file" class="size-18px" /> {{ previewFile?.file_name }}
</div>
</template>
<template #footer>
<div class="flex justify-end gap-[10px]">
<NButton size="small" @click="useTimeModal = false">取消</NButton>
<NButton size="small" type="primary" :loading="btnLoading" @click="saveUseTime">确认投用</NButton>
</div>
</template>
</NModal>
</template>
<template #default>
<div class="space-y-3">
<div class="flex flex-wrap gap-x-4 gap-y-1 text-xs text-slate-400">
<span>上传人:{{ previewFile?.uploader_name }}</span>
<span>上传时间:{{ previewFile?.created_at?.slice(0, 16) }}</span>
<span v-if="previewFile?.version">{{ `版本:${previewFile?.version}(受控)` }}</span>
</div>
<previewComponent v-if="previewFile" :previewFile="previewFile" />
</div>
</template>
</NModal>
</NSpin>
</NSpace>
</template>
<style scoped lang="scss">
.scroll {
height: calc(100vh - 180px);
height: calc(100vh - 220px);
overflow-y: auto;
}
</style>