Files
moc/src/views/my/pssr/index.vue
T

251 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- PSSR监督 -->
<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 { formatTimestamp, getTimeRangeStamp, downloadFileFun } from '@/utils/common';
import previewComponent from '@/components/previewFile/index.vue'
import { pssrListApi } from '@/service/api/pssr';
const themeStore = useThemeStore();
const { routerPushByKey } = useRouterPush();
const loading = ref<boolean>(false);
// 附件在线预览弹窗
const previewModal = ref<boolean>(false);
// 附件在线预览文件
const previewFile = ref<any>(null);
// 未关闭数量
const openCount = ref<number>(0);
// 已关闭数量
const closedCount = ref<number>(0);
const formInfo = ref<{from: number | null,to: number | null,kw: string,is_close: number }>({
from: null,
to: null,
kw: "",
is_close: 0,
});
const pagination = ref<{page: number, pageSize: number, itemCount: number}>({
page: 1,
pageSize: 20,
itemCount: 0,
})
const range = ref<string>('');
const timeRange = ref<{label: string,value: string}[]>([
{label: '全部时间', value: ''},
{label: '最近7天', value: 'week'},
{label: '最近30天', value: 'month'},
{label: '最近90天', value: 'quarter'},
]);
const list = ref<any[]>([]);
// 搜索
const searchHandle = () => {
pagination.value.page = 1;
getList();
}
// 切换tab
const tabChange = (k: number) => {
list.value = []
formInfo.value.is_close = k;
searchHandle();
}
// 切换时间范围
const timeRangeChange = (timeRange: string) => {
range.value = timeRange;
if(timeRange === ''){
formInfo.value.from = null;
formInfo.value.to = null;
}else{
formInfo.value.from = getTimeRangeStamp(timeRange)?.from || null;
formInfo.value.to = getTimeRangeStamp(timeRange)?.to || null;
}
searchHandle();
}
// 查看
const viewFile = (file: any) => {
previewFile.value = file;
previewModal.value = true;
};
// 下载
const downloadFile = (file: any) => {
downloadFileFun(file.file_url, file.file_name);
};
// 下载模板
const downloadTemplate = (record: any) => {
window.$message?.info('还差接口')
};
// 打开变更单
const openChange = (r: any) => {
routerPushByKey("details_index",{ query: { id: r?.id?.toString(),key:'my_pssr' } });
};
// 分页改变
const pageChange = (page: number) => {
pagination.value.page = page;
getList();
}
// 获取列表
const getList = async () => {
if(loading.value) return;
loading.value = true;
const {data,error} = await pssrListApi({
page_num: pagination.value.page,
page_size: pagination.value.pageSize,
kw: formInfo.value.kw,
is_close: formInfo.value.is_close,
from: formatTimestamp(formInfo.value.from, 'yyyy-MM-dd'),
to: formatTimestamp(formInfo.value.to, 'yyyy-MM-dd'),
});
if(!error){
list.value = data?.list || [];
openCount.value = data?.unclosed_count || 0;
closedCount.value = data?.closed_count || 0;
pagination.value.itemCount = data?.total || 0;
}
loading.value = false;
}
onMounted(() => {
getList();
})
</script>
<template>
<NSpace vertical :size="16">
<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"
:style="{ '--theme-color': themeStore.themeColor}"
>
<div class="flex overflow-hidden rounded-full border">
<div @click="tabChange(0)" class="px-3.5 py-1.5 text-xs cursor-pointer select-none"
:class="formInfo.is_close === 0 ? 'bg-[var(--theme-color)] font-medium text-white' : 'bg-white text-slate-600 hover:bg-slate-50'">
未关闭的变更 <b>{{ openCount }}</b>
</div>
<div @click="tabChange(1)" class="border-l px-3.5 py-1.5 text-xs cursor-pointer select-none"
:class="formInfo.is_close === 1 ? 'bg-[var(--theme-color)] font-medium text-white' : 'bg-white text-slate-600 hover:bg-slate-50'">
已关闭的变更 <b>{{ closedCount }}</b>
</div>
</div>
<NInput v-model:value="formInfo.kw" size="small" class="text-xs !w-160px" clearable placeholder="搜索变更名称 / 编号" @clear="formInfo.kw = ''; searchHandle()">
<template #suffix>
<Icon icon="akar-icons:search" class="size-14px text-slate-400 cursor-pointer" @click="searchHandle" />
</template>
</NInput>
<div class="flex items-center gap-1.5">
<NButton
size="tiny"
round
v-for="t in timeRange"
:key="t.value"
class="text-[11px]"
:type="range === t.value ? 'primary' : 'default'"
@click="timeRangeChange(t.value)"
>
{{ t.label }}
</NButton>
</div>
<span class="ml-auto text-[11px] text-slate-400">培训 / PSSR 为线下确认 + 资料上传制 · PSSR 检查表同行查看 / 下载</span>
</div>
<div class="text-[11px] leading-4 text-slate-400 pb-2">
培训 / PSSR 为线下组织并签字,拍照或扫描上传即视为完成确认并随单归档;系统不做内容识别,本页为 PSSR 检查表的查看 / 下载入口。
</div>
<!-- 列表 -->
<div class="scroll">
<div
v-if="list.length "
class="space-y-3 h-100%"
:style="{
'--theme-color': themeStore.themeColor,
'--warning-color': themeStore.otherColor.warning,
}"
>
<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==='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">
<p class="block truncate text-sm font-medium text-slate-800">{{ s.title }}</p>
<span class="mt-0.5 block text-[11px] text-slate-400">{{ s.change_no }} · {{ s.org_name }} · {{ s.applicant_name }} · 计划投用 {{ s.plan_use_date }}</span>
</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_attachment">
<span class="max-w-[150px] truncate text-[11px] text-slate-400">{{ s.pssr_attachment?.file_name }}</span>
<NButton ghost size="tiny" class="text-11px" @click="viewFile(s.pssr_attachment)">查看</NButton>
<NButton ghost size="tiny" class="text-11px" @click="downloadFile(s.pssr_attachment)">下载</NButton>
</template>
<template v-else>
<span class="text-[11px] text-[var(--warning-color)]">待传</span>
<NButton ghost size="tiny" class="text-11px" @click="downloadTemplate(s)">下载模板</NButton>
</template>
</div>
<NButton v-if="s.status==='APPROVING' || s.status==='APPROVED' || s.status==='ACCEPTED'" size="small" type="primary" 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 v-else class="h-100% flex items-center justify-center">
<NEmpty description="暂无数据"></NEmpty>
</div>
</div>
<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"
>
<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 #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 - 252px);
overflow-y: auto;
}
</style>