资料关闭完成
This commit is contained in:
@@ -4,9 +4,15 @@ import { Icon } from '@iconify/vue'
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { getColorWithOpacity, tagTextColor, tagBgColor, tagBorderColor } from '@/utils/common';
|
||||
import { getColorWithOpacity, tagTextColor, tagBgColor, tagBorderColor, formatTimestamp, anyToTimestamp } from '@/utils/common';
|
||||
import { uploadFileApi, deleteFileApi, fileListApi } from "@/service/api/file";
|
||||
import { systemConfigApi } from '@/service/api/system';
|
||||
import { getChangeDetailApi } from '@/service/api/change';
|
||||
import { myTasksCloseMaterialApi } from '@/service/api/myTasks';
|
||||
|
||||
import PDF from 'pdf-vue3'
|
||||
import { VueOfficeDocx, VueOfficeExcel } from 'vue3-office-preview'
|
||||
import 'vue3-office-preview/lib/style.css'
|
||||
|
||||
const themeStore = useThemeStore();
|
||||
const { routerPushByKey } = useRouterPush();
|
||||
@@ -19,43 +25,220 @@ const currentInfo = ref<any>(null);
|
||||
const currentId = ref<number>(0);
|
||||
// 是否开启 AI 辅助功能
|
||||
const isAiOn = ref<boolean>(false);
|
||||
// 变更等级选项
|
||||
const levelList = ref<any>([]);
|
||||
// 变更类型选项
|
||||
const typeList = ref<any>([]);
|
||||
// 培训签到上传文件列表
|
||||
const trainFiles = ref<any>([]);
|
||||
// pssr上传文件列表
|
||||
const pssrFiles = ref<any>([]);
|
||||
// 验收上传文件列表
|
||||
const acceptFiles = ref<any>([]);
|
||||
// 实际投用日期
|
||||
const useDate = ref<number | null>(null);
|
||||
// 变更材料和实施费(万元)
|
||||
const price = ref<number | null>(null);
|
||||
// 验收状态
|
||||
const resultStatus = ref<number>(0);
|
||||
// 验收结论
|
||||
const acceptDesc = ref<string>('');
|
||||
// 验收时间
|
||||
const acceptDate = ref<number | null>(null);
|
||||
// 附件选择列表弹框
|
||||
const filesModal = ref<boolean>(false);
|
||||
// 选中的文件列表
|
||||
const checkedFiles = ref<any>([]);
|
||||
// 附件列表
|
||||
const fileList = ref<any>([]);
|
||||
// 当前从变更附件选择的row下标
|
||||
const currentRowIndex = ref<number>(-1);
|
||||
// 当前预览文件
|
||||
const previewFile = ref<any>(null);
|
||||
// 预览文件弹框
|
||||
const previewModal = ref<boolean>(false);
|
||||
|
||||
const list = ref<any>([
|
||||
{isArchiving:0,name:'变更申请表及审批记录',files:[{file_name:'审批记录(含会签意见).pdf',file_type:'pdf',file_url:''}]},
|
||||
{isArchiving:1,name:'风险分析记录(风险预分析 / HAZOP / FMEA / 风险检查表)',files:[]},
|
||||
{isArchiving:0,name:'培训签到与考核记录',files:[]},
|
||||
{isArchiving:0,name:'PSSR 检查记录(含现场照片)',files:[]},
|
||||
{isArchiving:2,name:'验收报告与运行数据',files:[]},
|
||||
{isArchiving:0,name:'更新后的 P&ID / 操作规程(受控最新版)',files:[]},
|
||||
{isArchiving:0,name:'设备 / 联锁台账更新记录',files:[]},
|
||||
{isArchiving:0,name:'应急预案修订',files:[]},
|
||||
{id:1,status:null,name:'变更申请表及审批记录',files:[]},
|
||||
{id:2,status:null,name:'风险分析记录(风险预分析 / HAZOP / FMEA / 风险检查表)',files:[]},
|
||||
{id:3,status:null,name:'培训签到与考核记录',files:[]},
|
||||
{id:4,status:null,name:'PSSR 检查记录(含现场照片)',files:[]},
|
||||
{id:5,status:null,name:'验收报告与运行数据',files:[]},
|
||||
{id:6,status:null,name:'更新后的 P&ID / 操作规程(受控最新版)',files:[]},
|
||||
{id:7,status:null,name:'设备 / 联锁台账更新记录',files:[]},
|
||||
{id:8,status:null,name:'应急预案修订',files:[]},
|
||||
]);
|
||||
|
||||
// 删除文件
|
||||
const removeFile = (item:any, fileIndex:number) => {
|
||||
item.files.splice(fileIndex, 1);
|
||||
}
|
||||
|
||||
// 返回
|
||||
const back = () => {
|
||||
routerPushByKey('my_mytask');
|
||||
};
|
||||
|
||||
// 上传文件
|
||||
const onUploadFile = async (type:string = '',files:any,index:number = -1) => {
|
||||
const file = files?.fileList[0]?.file;
|
||||
if (!file) return;
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
// 3:培训签到;4:PSSR;5:验收
|
||||
if(type==='3' || type==='4' || type==='5'){
|
||||
formData.append('step', type);
|
||||
}else{
|
||||
formData.append('close_doc_id', currentId.value.toString());
|
||||
}
|
||||
loading.value = true;
|
||||
const {data,error} = await uploadFileApi(currentId.value, formData);
|
||||
if(!error){
|
||||
// 是否是本地上传
|
||||
data.isLocalUpload = true;
|
||||
console.log(data)
|
||||
window.$message?.success('上传成功');
|
||||
if(type==='3'){
|
||||
trainFiles.value=[data];
|
||||
}else if(type==='4'){
|
||||
pssrFiles.value=[data];
|
||||
}else if(type==='5'){
|
||||
acceptFiles.value=[data];
|
||||
}else{
|
||||
list.value[index].files.push(data);
|
||||
list.value[index].status = 1;
|
||||
}
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
const downloadFile = (type:string) => {
|
||||
window.$message?.info('还差接口')
|
||||
}
|
||||
// 删除文件(真删)
|
||||
const removeFileReal = async (type:string, fileIndex:number,index:number = -1) => {
|
||||
let id = 0;
|
||||
if(type==='3'){
|
||||
id = trainFiles.value[fileIndex].id;
|
||||
}else if(type==='4'){
|
||||
id = pssrFiles.value[fileIndex].id;
|
||||
}else if(type==='5'){
|
||||
id = acceptFiles.value[fileIndex].id;
|
||||
}else{
|
||||
id = list.value[index]?.files[fileIndex]?.id;
|
||||
}
|
||||
loading.value = true;
|
||||
const {error} = await deleteFileApi(currentId.value,id);
|
||||
if(!error){
|
||||
if(type==='3'){
|
||||
trainFiles.value.splice(fileIndex, 1);
|
||||
}else if(type==='4'){
|
||||
pssrFiles.value.splice(fileIndex, 1);
|
||||
}else if(type==='5'){
|
||||
acceptFiles.value.splice(fileIndex, 1);
|
||||
}else{
|
||||
list.value[index]?.files.splice(fileIndex, 1);
|
||||
if(list.value[index]?.files.length === 0){
|
||||
list.value[index].status = 2;
|
||||
}
|
||||
}
|
||||
window.$message?.success('删除成功');
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
// 删除文件(假删)
|
||||
const removeFile = (index:number, fileIndex:number) => {
|
||||
// 判断是否是本地上传
|
||||
if(list.value[index]?.files[fileIndex]?.isLocalUpload){
|
||||
removeFileReal(list.value[index]?.name, fileIndex, index);
|
||||
}else{
|
||||
list.value[index]?.files.splice(fileIndex, 1);
|
||||
if(list.value[index]?.files.length === 0){
|
||||
list.value[index].status = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断文件类型
|
||||
const handleFileType = (type:string) => {
|
||||
if(type){
|
||||
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg']
|
||||
const wordExtensions = ['docx']
|
||||
const excelExtensions = ['xlsx']
|
||||
if(wordExtensions.includes(type)){
|
||||
return 'word'
|
||||
}
|
||||
if(excelExtensions.includes(type)){
|
||||
return 'excel'
|
||||
}
|
||||
if(imageExtensions.includes(type)){
|
||||
return 'image'
|
||||
}
|
||||
if(type==='pdf'){
|
||||
return 'pdf'
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
// 打开预览
|
||||
const preview = (file:any) => {
|
||||
console.log(file)
|
||||
previewFile.value = file;
|
||||
previewModal.value = true;
|
||||
}
|
||||
|
||||
// 打开附件选择
|
||||
const openFileList = (index:number) => {
|
||||
currentRowIndex.value = index;
|
||||
filesModal.value = true;
|
||||
}
|
||||
// 选择文件
|
||||
const chooseFiles = () => {
|
||||
// 过滤出选中的文件
|
||||
const ids = new Set(checkedFiles.value);
|
||||
const result = fileList.value.filter((item:any) => ids.has(item.id));
|
||||
// 合并文件列表
|
||||
let files = list.value[currentRowIndex.value]?.files.concat(result);
|
||||
// 按id去重
|
||||
const uniqueList = [
|
||||
...new Map(files.map((item:any) => [item.id, item])).values(),
|
||||
];
|
||||
list.value[currentRowIndex.value].files = uniqueList;
|
||||
if(uniqueList.length > 0){
|
||||
list.value[currentRowIndex.value].status = 1;
|
||||
}
|
||||
filesModal.value = false;
|
||||
}
|
||||
|
||||
// 保存/申请关闭
|
||||
const submitCloseDoc = async (type:string) => {
|
||||
if(!useDate.value){
|
||||
return window.$message?.error('请选择实际投用日期');
|
||||
}
|
||||
if(!resultStatus.value){
|
||||
return window.$message?.error('请选择验收结论');
|
||||
}
|
||||
const result = list.value.map(({ id, status }:{id:number,status:number} ) => ({ id, status }));
|
||||
loading.value = true;
|
||||
const {error} = await myTasksCloseMaterialApi(currentId.value,{
|
||||
type:type,
|
||||
result_status:resultStatus.value,
|
||||
note:acceptDesc.value,
|
||||
use_date:formatTimestamp(useDate.value,'yyyy-MM-dd'),
|
||||
accept_date:formatTimestamp(acceptDate.value,'yyyy-MM-dd'),
|
||||
cost:price.value,
|
||||
close_docs:result,
|
||||
});
|
||||
if(!error){
|
||||
if(type==='save'){
|
||||
window.$message?.success('保存成功');
|
||||
}
|
||||
if(type==='close'){
|
||||
back();
|
||||
window.$message?.success('申请关闭成功');
|
||||
}
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 获取变更详情
|
||||
const getDetail = async () => {
|
||||
loading.value = true;
|
||||
const {data,error} = await getChangeDetailApi(currentId.value)
|
||||
if(!error){
|
||||
currentInfo.value = data;
|
||||
console.log(data)
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
@@ -72,31 +255,25 @@ const getConfig = async () => {
|
||||
isAiOn.value = target.value==1?true:false;
|
||||
}
|
||||
}
|
||||
// 解析 变更类型设置
|
||||
const changeTypeGroup = data.find((group:{group_key:string}) => group.group_key === 'change_type');
|
||||
if (changeTypeGroup) {
|
||||
typeList.value = changeTypeGroup.items.map((item:any) => ({
|
||||
...item,
|
||||
value: Number(item.value)
|
||||
}));
|
||||
}
|
||||
// 解析 变更等级设置
|
||||
const changeLevelGroup = data.find((group:{group_key:string}) => group.group_key === 'change_level');
|
||||
if (changeLevelGroup) {
|
||||
levelList.value = changeLevelGroup.items.map((item:any) => ({
|
||||
...item,
|
||||
value: Number(item.value)
|
||||
}));
|
||||
}
|
||||
getDetail();
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
// 获取文件列表
|
||||
const getFileList = async () => {
|
||||
const {data,error} = await fileListApi(currentId.value);
|
||||
if(!error){
|
||||
console.log(data)
|
||||
fileList.value = data;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
useDate.value = new Date().getTime();
|
||||
if(route.query && route.query.id){
|
||||
if(route.query.id){
|
||||
currentId.value = Number(route.query.id);
|
||||
getFileList();
|
||||
getConfig();
|
||||
}
|
||||
}
|
||||
@@ -105,7 +282,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<NSpace vertical :size="16">
|
||||
<!-- 本专业高风险 -->
|
||||
<!-- 资料关闭详情-->
|
||||
<NSpin :show="loading" size="small">
|
||||
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||
<div
|
||||
@@ -123,20 +300,104 @@ onMounted(async () => {
|
||||
<div class="border rounded-md">
|
||||
<div class="flex items-center gap-x-3 px-4 py-2.5 border-b">
|
||||
<p class="font-600 text-slate-800 text-sm">投用前确认(含变更培训、投用前检查)</p>
|
||||
<span class="text-slate-400 text-xs">上传线下纸质表照片 / 扫描件即视为完成确认,上传后可查阅、下载;也可在移动端 / PC 端逐条确认;空白模板可下载</span>
|
||||
<span class="text-slate-400 text-xs">上传线下纸质表照片 / 扫描件即视为完成确认,上传后可查阅、下载;空白模板可下载</span>
|
||||
</div>
|
||||
<div class="p-3">
|
||||
<div class="p-3 space-y-3">
|
||||
<div class="border rounded-lg py-3 px-4 flex items-center gap-3">
|
||||
<Icon icon="bi:upload" class="size-18px" :style="{ color: themeStore.themeColor }" />
|
||||
<Icon icon="bi:upload" class="size-18px" :style="{ color: trainFiles.length?themeStore.otherColor.success:themeStore.themeColor }" />
|
||||
<div class="flex-1">
|
||||
<p class="text-slate-800 text-sm font-600">上传 PSSR 检查</p>
|
||||
<p class="text-slate-400 text-xs mt-0.5 leading-4">PSSR 纸质确认表照片或扫描件;上传后视为 PSSR 全项确认完成,具备投用条件</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-slate-800 text-sm font-600">上传培训签到</span>
|
||||
<p v-if="trainFiles.length" class="py-0.5 px-1.5 text-11px bg-emerald-100 rounded-md text-emerald-700">已上传 · 视为完成确认</p>
|
||||
</div>
|
||||
<p class="text-slate-400 text-xs mt-1 leading-4">线下培训签到表照片或扫描件;上传后视为培训完成确认,签到记录随单归档</p>
|
||||
<div v-if="trainFiles.length" class="flex flex-wrap gap-2 mt-1">
|
||||
<div
|
||||
v-for="(file, fileIndex) in trainFiles"
|
||||
:key="fileIndex"
|
||||
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
||||
>
|
||||
<Icon icon="akar-icons:file" class="size-12px" />
|
||||
{{file.file_name}}
|
||||
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
||||
<NButton text type="primary" class="text-11px" @click="downloadFile('pssr')">下载</NButton>
|
||||
<NPopconfirm
|
||||
positive-text="确定"
|
||||
:positiveButtonProps="{ size: 'tiny' }"
|
||||
:negativeButtonProps="{ size: 'tiny' }"
|
||||
@positive-click="removeFileReal('3', Number(fileIndex))"
|
||||
>
|
||||
<template #trigger>
|
||||
<NButton text type="error" class="text-11px">删除</NButton>
|
||||
</template>
|
||||
确定删除吗?
|
||||
</NPopconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NButton ghost type="primary" size="small" class="text-xs">
|
||||
<NButton ghost type="primary" size="small" class="text-xs" @click="downloadFile('pssr')">
|
||||
<Icon icon="akar-icons:download" class="size-14px mr-1" />
|
||||
下载培训签到表
|
||||
</NButton>
|
||||
<NUpload
|
||||
class="w-auto"
|
||||
accept="image/*,.pdf,.docx,.xlsx"
|
||||
:default-upload="false"
|
||||
:show-file-list="false"
|
||||
@change="(files) => onUploadFile('3', files)"
|
||||
>
|
||||
<NButton type="primary" size="small" class="text-xs" :disabled="loading">
|
||||
{{trainFiles.length?'重新上传':'上传'}}
|
||||
</NButton>
|
||||
</NUpload>
|
||||
</div>
|
||||
<div class="border rounded-lg py-3 px-4 flex items-center gap-3">
|
||||
<Icon icon="bi:upload" class="size-18px" :style="{ color: pssrFiles.length?themeStore.otherColor.success:themeStore.themeColor }" />
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-slate-800 text-sm font-600">上传 PSSR 检查</span>
|
||||
<p v-if="pssrFiles.length" class="py-0.5 px-1.5 text-11px bg-emerald-100 rounded-md text-emerald-700">已上传 · 视为完成确认</p>
|
||||
</div>
|
||||
<p class="text-slate-400 text-xs mt-1 leading-4">PSSR 纸质确认表照片或扫描件;上传后视为 PSSR 全项确认完成,具备投用条件</p>
|
||||
<div v-if="pssrFiles.length" class="flex flex-wrap gap-2 mt-1">
|
||||
<div
|
||||
v-for="(file, fileIndex) in pssrFiles"
|
||||
:key="fileIndex"
|
||||
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
||||
>
|
||||
<Icon icon="akar-icons:file" class="size-12px" />
|
||||
{{file.file_name}}
|
||||
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
||||
<NButton text type="primary" class="text-11px" @click="downloadFile('pssr')">下载</NButton>
|
||||
<NPopconfirm
|
||||
positive-text="确定"
|
||||
:positiveButtonProps="{ size: 'tiny' }"
|
||||
:negativeButtonProps="{ size: 'tiny' }"
|
||||
@positive-click="pssrFiles.splice(fileIndex, 1)"
|
||||
>
|
||||
<template #trigger>
|
||||
<NButton text type="error" class="text-11px">删除</NButton>
|
||||
</template>
|
||||
确定删除吗?
|
||||
</NPopconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NButton ghost type="primary" size="small" class="text-xs" @click="downloadFile('pssr')">
|
||||
<Icon icon="akar-icons:download" class="size-14px mr-1" />
|
||||
下载PSSR检查表
|
||||
</NButton>
|
||||
<NButton type="primary" size="small" class="text-xs">上传</NButton>
|
||||
<NUpload
|
||||
class="w-auto"
|
||||
accept="image/*,.pdf,.docx,.xlsx"
|
||||
:default-upload="false"
|
||||
:show-file-list="false"
|
||||
@change="(files) => onUploadFile('4', files)"
|
||||
>
|
||||
<NButton type="primary" size="small" class="text-xs" :disabled="loading">
|
||||
{{pssrFiles.length?'重新上传':'上传'}}
|
||||
</NButton>
|
||||
</NUpload>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,7 +412,7 @@ onMounted(async () => {
|
||||
<NDatePicker v-model:value="useDate" type="date" size="small" />
|
||||
<p class="text-xs text-slate-400 mt-1px">默认为当天日期,可按实际投用日期调整</p>
|
||||
</div>
|
||||
<div class="mt-2.5 rounded-lg border px-3 py-2 text-[11px] leading-5 border-amber-200 bg-amber-50/60 text-amber-700"> 投用提示:投用前应完成 PSSR 检查和培训(上方入口上传,或移动端 / PC 端逐条确认)。 当前培训签到、PSSR 检查尚未确认,未完成的变更不得投用。</div>
|
||||
<div class="mt-2.5 rounded-lg border px-3 py-2 text-[11px] leading-5 border-amber-200 bg-amber-50/60 text-amber-700"> 投用提示:投用前应完成 PSSR 检查。 未完成的变更不得投用。</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 变更验收 -->
|
||||
@@ -162,22 +423,71 @@ onMounted(async () => {
|
||||
</div>
|
||||
<div class="p-3 space-y-2.5">
|
||||
<div class="border rounded-lg py-3 px-4 flex items-center gap-3">
|
||||
<Icon icon="bi:upload" class="size-18px" :style="{ color: themeStore.themeColor }" />
|
||||
<Icon icon="bi:upload" class="size-18px" :style="{ color: acceptFiles.length?themeStore.otherColor.success:themeStore.themeColor }" />
|
||||
<div class="flex-1">
|
||||
<p class="text-slate-800 text-sm font-600">上传验收资料</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-slate-800 text-sm font-600">上传验收资料</span>
|
||||
<p v-if="pssrFiles.length" class="py-0.5 px-1.5 text-11px bg-emerald-100 rounded-md text-emerald-700">已上传 · 视为完成确认</p>
|
||||
</div>
|
||||
<p class="text-slate-400 text-xs mt-0.5 leading-4">验收报告、运行数据记录等验收证明材料;上传后纳入归档清单</p>
|
||||
<div v-if="acceptFiles.length" class="flex flex-wrap gap-2 mt-1">
|
||||
<div
|
||||
v-for="(file, fileIndex) in acceptFiles"
|
||||
:key="fileIndex"
|
||||
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
||||
>
|
||||
<Icon icon="akar-icons:file" class="size-12px" />
|
||||
{{file.file_name}}
|
||||
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
||||
<NButton text type="primary" class="text-11px" @click="downloadFile('pssr')">下载</NButton>
|
||||
<NPopconfirm
|
||||
positive-text="确定"
|
||||
:positiveButtonProps="{ size: 'tiny' }"
|
||||
:negativeButtonProps="{ size: 'tiny' }"
|
||||
@positive-click="acceptFiles.splice(fileIndex, 1)"
|
||||
>
|
||||
<template #trigger>
|
||||
<NButton text type="error" class="text-11px">删除</NButton>
|
||||
</template>
|
||||
确定删除吗?
|
||||
</NPopconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NButton ghost type="primary" size="small" class="text-xs">
|
||||
<NButton ghost type="primary" size="small" class="text-xs" @click="downloadFile('accept')">
|
||||
<Icon icon="akar-icons:download" class="size-14px mr-1" />
|
||||
下载验收表
|
||||
</NButton>
|
||||
<NButton type="primary" size="small" class="text-xs">上传</NButton>
|
||||
<NUpload
|
||||
class="w-auto"
|
||||
accept="image/*,.pdf,.docx,.xlsx"
|
||||
:default-upload="false"
|
||||
:show-file-list="false"
|
||||
@change="(files) => onUploadFile('5', files)"
|
||||
>
|
||||
<NButton type="primary" size="small" class="text-xs" :disabled="loading">
|
||||
{{acceptFiles.length?'重新上传':'上传'}}
|
||||
</NButton>
|
||||
</NUpload>
|
||||
</div>
|
||||
<div class="border rounded-lg py-3 px-4 flex items-center gap-3">
|
||||
<div class="border rounded-lg py-3 px-4 flex flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<p class="text-xs font-600" :style="{ color: themeStore.themeColor }">验收结论(互斥选择)</p>
|
||||
<NButton size="small" :type="resultStatus === 3?'primary':'default'" class="text-xs" @click="resultStatus = 3">完全达到</NButton>
|
||||
<NButton size="small" :type="resultStatus === 2?'primary':'default'" class="text-xs" @click="resultStatus = 2">基本达到</NButton>
|
||||
<NButton size="small" :type="resultStatus === 1?'primary':'default'" class="text-xs" @click="resultStatus = 1">未达到</NButton>
|
||||
</div>
|
||||
<NInput v-model:value="acceptDesc" type="textarea" size="small" :autosize="{ minRows: 2 }" placeholder="请输入验收结论" />
|
||||
</div>
|
||||
<div class="border rounded-lg py-3 px-4 flex items-center gap-2">
|
||||
<p class="text-xs font-600" :style="{ color: themeStore.themeColor }">变更材料和实施费(万元)</p>
|
||||
<NInputNumber v-model:value="price" size="small" :min="0" :show-button="false" placeholder="如 12.6" />
|
||||
<p class="text-xs text-slate-400 mt-1px">非必填 · 填报本次变更材料与实施费用合计,用于变更投入与效果统计</p>
|
||||
</div>
|
||||
<div class="border rounded-lg py-3 px-4 flex items-center gap-2">
|
||||
<p class="text-xs font-600" :style="{ color: themeStore.themeColor }">验收时间</p>
|
||||
<NDatePicker v-model:value="acceptDate" type="date" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 变更关闭资料归档确认 -->
|
||||
@@ -200,7 +510,7 @@ onMounted(async () => {
|
||||
<tr v-for="(item, index) in list" :key="index">
|
||||
<td>
|
||||
<p>{{item.name}}</p>
|
||||
<div v-if="item.files.length" class="flex flex-wrap">
|
||||
<div v-if="item.files.length" class="flex flex-wrap gap-2">
|
||||
<div
|
||||
v-for="(file, fileIndex) in item.files"
|
||||
:key="fileIndex"
|
||||
@@ -208,13 +518,13 @@ onMounted(async () => {
|
||||
>
|
||||
<Icon icon="akar-icons:file" class="size-12px" />
|
||||
{{file.file_name}}
|
||||
<NButton text type="primary" class="text-11px">预览</NButton>
|
||||
<NButton text type="primary" class="text-11px">下载</NButton>
|
||||
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
||||
<NButton text type="primary" class="text-11px" @click="downloadFile('accept')">下载</NButton>
|
||||
<NPopconfirm
|
||||
positive-text="确定"
|
||||
:positiveButtonProps="{ size: 'tiny' }"
|
||||
:negativeButtonProps="{ size: 'tiny' }"
|
||||
@positive-click="removeFile(item, fileIndex)"
|
||||
@positive-click="removeFile(Number(index), Number(fileIndex))"
|
||||
>
|
||||
<template #trigger>
|
||||
<NButton text type="error" class="text-11px">删除</NButton>
|
||||
@@ -226,25 +536,36 @@ onMounted(async () => {
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex justify-center gap-2">
|
||||
<NButton size="small" :type="item.isArchiving === 1 ? 'primary' : 'default'" class="text-xs">是</NButton>
|
||||
<NButton size="small" :type="item.isArchiving === 0 ? 'primary' : 'default'" class="text-xs">否</NButton>
|
||||
<NButton size="small" :type="item.isArchiving === 2 ? 'primary' : 'default'" class="text-xs">不涉及</NButton>
|
||||
<NButton size="small" :type="item.status === 1 ? 'primary' : 'default'" class="text-xs" @click="item.status = 1">是</NButton>
|
||||
<NButton size="small" :type="item.status === 2 ? 'primary' : 'default'" class="text-xs" @click="item.status = 2">否</NButton>
|
||||
<NButton size="small" :type="item.status === 0 ? 'primary' : 'default'" class="text-xs" @click="item.status = 0">不涉及</NButton>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="item.isArchiving === 0" class="flex justify-center gap-2">
|
||||
<NButton size="small" class="text-xs">从变更附件选择</NButton>
|
||||
<NButton size="small" type="primary" ghost class="text-xs">本地上传</NButton>
|
||||
<div v-if="item.status === 2" class="flex justify-center gap-2">
|
||||
<NButton size="small" class="text-xs" @click="openFileList(Number(index))">从变更附件选择</NButton>
|
||||
<NUpload
|
||||
class="w-auto"
|
||||
accept="image/*,.pdf,.docx,.xlsx"
|
||||
:default-upload="false"
|
||||
:show-file-list="false"
|
||||
@change="(files) => onUploadFile(item.name, files, Number(index))"
|
||||
>
|
||||
<NButton type="primary" size="small" ghost class="text-xs" :disabled="loading">
|
||||
本地上传
|
||||
</NButton>
|
||||
</NUpload>
|
||||
</div>
|
||||
<div v-if="item.isArchiving === 1" class="flex justify-end gap-2">
|
||||
<div v-if="item.status === 1" class="flex justify-end gap-2">
|
||||
<NTag size="small" type="success" class="text-xs">
|
||||
<template #icon>
|
||||
<Icon icon="akar-icons:circle-check" class="size-12px" />
|
||||
</template>
|
||||
已归档1件
|
||||
<span v-if="item.files.length">已归档{{item.files.length}}件</span>
|
||||
<span v-else>已归档</span>
|
||||
</NTag>
|
||||
</div>
|
||||
<div v-if="item.isArchiving === 2" class="flex justify-end gap-2">
|
||||
<div v-if="item.status === 0" class="flex justify-end gap-2">
|
||||
<span class="text-xs text-slate-400">无需处理</span>
|
||||
</div>
|
||||
</td>
|
||||
@@ -258,10 +579,80 @@ onMounted(async () => {
|
||||
<p class="text-xs text-slate-400">待归档 5 项:培训签到与考核记录、PSSR 检查记录(含现场照片)、验收报告与运行数据、更新后的 P&ID / 操作规程(受控最新版)、设备 / 联锁台账更新记录</p>
|
||||
<div class="flex gap-2">
|
||||
<NButton ghost size="small" @click="back">取消</NButton>
|
||||
<NButton ghost type="primary" size="small">保存</NButton>
|
||||
<NButton type="primary" size="small">申请关闭</NButton>
|
||||
<NButton ghost type="primary" size="small" @click="submitCloseDoc('save')">保存</NButton>
|
||||
<NButton type="primary" size="small" @click="submitCloseDoc('close')">申请关闭</NButton>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 附件选择弹框 -->
|
||||
<NModal
|
||||
v-model:show="filesModal"
|
||||
preset="card"
|
||||
title="从变更附件选择"
|
||||
:auto-focus="false"
|
||||
:style="{ width: '450px', height: 'auto' }"
|
||||
:segmented="{ content: true, footer: true }"
|
||||
>
|
||||
<div class="max-h-60vh overflow-y-auto">
|
||||
<NCheckboxGroup v-model:value="checkedFiles">
|
||||
<div class="space-y-2">
|
||||
<div v-for="item in fileList" :key="item.id">
|
||||
<NCheckbox :value="item.id" :label="item.file_name" />
|
||||
</div>
|
||||
</div>
|
||||
</NCheckboxGroup>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-end gap-[10px]">
|
||||
<NButton size="small" @click="filesModal = false">取消</NButton>
|
||||
<NButton size="small" type="primary" @click="chooseFiles">确定选择</NButton>
|
||||
</div>
|
||||
</template>
|
||||
</NModal>
|
||||
<!-- 附件在线预览弹窗 -->
|
||||
<NModal
|
||||
v-model:show="previewModal"
|
||||
preset="card"
|
||||
:auto-focus="false"
|
||||
:style="{ width: '1100px', 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 #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 }}</span>
|
||||
<span v-if="previewFile?.version">{{ `版本:${previewFile?.version}(受控)` }}</span>
|
||||
</div>
|
||||
<div v-if="previewFile" class="max-h-[70vh] overflow-x-auto rounded-lg border">
|
||||
<!-- 图片预览 -->
|
||||
<template v-if="handleFileType(previewFile?.file_type) === 'image'">
|
||||
<div class="flex items-center justify-center py-2">
|
||||
<NImage :src="previewFile?.file_url" />
|
||||
</div>
|
||||
</template>
|
||||
<!-- Word 预览 -->
|
||||
<template v-if="handleFileType(previewFile?.file_type) === 'word'">
|
||||
<VueOfficeDocx :src="previewFile?.file_url" class="w-full" />
|
||||
</template>
|
||||
<!-- Excel 预览 -->
|
||||
<template v-if="handleFileType(previewFile?.file_type) === 'excel'">
|
||||
<VueOfficeExcel :src="previewFile?.file_url" class="w-full" />
|
||||
</template>
|
||||
<!-- PDF 预览 -->
|
||||
<template v-if="handleFileType(previewFile?.file_type) === 'pdf'">
|
||||
<div class="h-[68vh]">
|
||||
<PDF :src="previewFile?.file_url" class="w-full h-full" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</NModal>
|
||||
</NCard>
|
||||
</NSpin>
|
||||
</NSpace>
|
||||
|
||||
Reference in New Issue
Block a user