Files
school-developer/src/views/purchase/purchasingrequisition/index.vue
吴红兵 15b3efe51e fix
2026-03-07 17:32:17 +08:00

1098 lines
35 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.

<template>
<div class="modern-page-container">
<div class="page-wrapper">
<!-- 搜索表单卡片 -->
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="采购编号" prop="purchaseNo">
<el-input v-model="state.queryForm.purchaseNo" placeholder="请输入采购编号" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="采购项目名称" prop="projectName">
<el-input v-model="state.queryForm.projectName" placeholder="请输入采购项目名称" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="项目类别" prop="projectType">
<el-select v-model="state.queryForm.projectType" placeholder="请选择项目类别" clearable style="width: 200px">
<el-option label="货物" value="A" />
<el-option label="工程" value="B" />
<el-option label="服务" value="C" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="state.queryForm.status" placeholder="请选择状态" clearable style="width: 200px">
<el-option label="暂存" value="-1" />
<el-option label="运行中" value="0" />
<el-option label="完成" value="1" />
<el-option label="作废" value="2" />
</el-select>
</el-form-item>
<el-form-item label="是否集采" prop="isCentralized">
<el-select v-model="state.queryForm.isCentralized" placeholder="请选择是否集采" clearable style="width: 200px">
<el-option label="否" value="0" />
<el-option label="是" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 内容卡片 -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
采购申请管理
</span>
<div class="header-actions">
<el-button icon="Files" link type="primary"> 采购申请汇总 </el-button>
<el-button icon="FolderAdd" type="primary" @click="handleAdd"> 新增 </el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
>
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
</el-table-column>
<el-table-column prop="purchaseNo" label="申请单编号" min-width="180">
<template #header>
<el-icon><DocumentCopy /></el-icon>
<span style="margin-left: 4px">申请单编号</span>
</template>
<template #default="scope">
<div class="purchase-no-cell">
<el-tooltip :content="scope.row.purchaseNo" placement="top" :show-after="0" v-if="scope.row.purchaseNo">
<span class="purchase-no-text">{{ scope.row.purchaseNo }}</span>
</el-tooltip>
<el-button
v-if="scope.row.purchaseNo"
type="primary"
link
size="small"
class="copy-btn"
@click.stop="copyToClipboard(scope.row.purchaseNo)"
>
<el-icon><CopyDocument /></el-icon>
</el-button>
</div>
</template>
</el-table-column>
<el-table-column prop="projectName" label="采购项目名称" min-width="200" show-overflow-tooltip>
<template #header>
<el-icon><Document /></el-icon>
<span style="margin-left: 4px">采购项目名称</span>
</template>
</el-table-column>
<el-table-column prop="applyDate" label="填报日期" width="120" align="center" show-overflow-tooltip>
<template #header>
<el-icon><Calendar /></el-icon>
<span style="margin-left: 4px">填报日期</span>
</template>
</el-table-column>
<el-table-column prop="deptName" label="需求部门" min-width="150" show-overflow-tooltip>
<template #header>
<el-icon><OfficeBuilding /></el-icon>
<span style="margin-left: 4px">需求部门</span>
</template>
</el-table-column>
<el-table-column prop="projectType" label="项目类别" min-width="200" align="left" show-overflow-tooltip>
<template #header>
<el-icon><Collection /></el-icon>
<span style="margin-left: 4px">项目类别</span>
</template>
<template #default="scope">
<div>
<el-tag v-if="scope.row.projectType === 'A'" type="success" style="margin-right: 8px">货物</el-tag>
<el-tag v-else-if="scope.row.projectType === 'B'" type="warning" style="margin-right: 8px">工程</el-tag>
<el-tag v-else-if="scope.row.projectType === 'C'" type="info" style="margin-right: 8px">服务</el-tag>
<span v-if="scope.row.categoryName" style="color: #606266; font-size: 12px">
{{ scope.row.categoryName }}
</span>
<span v-else-if="scope.row.categoryCode" style="color: #909399; font-size: 12px">
{{ scope.row.categoryCode }}
</span>
</div>
</template>
</el-table-column>
<el-table-column prop="budget" label="项目预算(元)" width="130" align="right">
<template #header>
<el-icon><Money /></el-icon>
<span style="margin-left: 4px">项目预算</span>
</template>
<template #default="scope">
{{ scope.row.budget ? Number(scope.row.budget).toLocaleString() : '-' }}
</template>
</el-table-column>
<el-table-column prop="isSpecial" label="是否特殊" width="100" align="center">
<template #header>
<el-icon><Warning /></el-icon>
<span style="margin-left: 4px">是否特殊</span>
</template>
<template #default="scope">
<el-tag v-if="String(scope.row.isSpecial) === '1'" type="warning">紧急</el-tag>
<el-tag v-else-if="String(scope.row.isSpecial) === '2'" type="danger">单一</el-tag>
<el-tag v-else-if="String(scope.row.isSpecial) === '3'" type="info">进口</el-tag>
<el-tag v-else-if="String(scope.row.isSpecial) === '0'" type="info"></el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="isCentralized" label="是否集采" width="100" align="center">
<template #header>
<el-icon><CircleCheck /></el-icon>
<span style="margin-left: 4px">是否集采</span>
</template>
<template #default="scope">
<el-tag v-if="String(scope.row.isCentralized) === '1'" type="success"></el-tag>
<el-tag v-else-if="String(scope.row.isCentralized) === '0'" type="info"></el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="purchaseMode" label="采购形式" width="140" align="center">
<template #header>
<el-icon><Shop /></el-icon>
<span style="margin-left: 4px">采购形式</span>
</template>
<template #default="scope">
<div class="purchase-mode-cell">
<el-tag v-if="scope.row.purchaseMode === '1'" type="success">部门自行采购</el-tag>
<el-tag v-else-if="scope.row.purchaseMode === '2'" type="primary">学校统一采购</el-tag>
<span v-else>-</span>
<el-tag v-if="scope.row.purchaseMode === '1' && scope.row.purchaseChannel === '2'" type="warning" size="small" class="entrust-tag"
></el-tag
>
</div>
</template>
</el-table-column>
<el-table-column prop="purchaseType" label="采购方式" width="120" align="center">
<template #header>
<el-icon><Shop /></el-icon>
<span style="margin-left: 4px">采购方式</span>
</template>
<template #default="scope">
<el-tag v-if="scope.row.purchaseTypeLabel" type="primary">{{ scope.row.purchaseTypeLabel }}</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="status" label="审核状态" width="100" align="center">
<template #header>
<el-icon><InfoFilled /></el-icon>
<span style="margin-left: 4px">审核状态</span>
</template>
<template #default="scope">
<el-tooltip v-if="scope.row.flowInstId" content="点击查看审批过程" placement="top">
<el-tag v-if="scope.row.status === '-2'" type="info" class="status-tag-clickable" @click="handleShowFlowComment(scope.row)"
>撤回</el-tag
>
<el-tag v-else-if="scope.row.status === '-1'" type="warning" class="status-tag-clickable" @click="handleShowFlowComment(scope.row)"
>暂存</el-tag
>
<el-tag v-else-if="scope.row.status === '0'" type="primary" class="status-tag-clickable" @click="handleShowFlowComment(scope.row)"
>运行中</el-tag
>
<el-tag v-else-if="scope.row.status === '1'" type="success" class="status-tag-clickable" @click="handleShowFlowComment(scope.row)"
>完成</el-tag
>
<el-tag v-else-if="scope.row.status === '2'" type="danger" class="status-tag-clickable" @click="handleShowFlowComment(scope.row)"
>作废</el-tag
>
<el-tag v-else-if="scope.row.status === '3'" type="info" class="status-tag-clickable" @click="handleShowFlowComment(scope.row)"
>终止</el-tag
>
<span v-else>-</span>
</el-tooltip>
<template v-else>
<el-tag v-if="scope.row.status === '-2'" type="info">撤回</el-tag>
<el-tag v-else-if="scope.row.status === '-1'" type="warning">暂存</el-tag>
<el-tag v-else-if="scope.row.status === '0'" type="primary">运行中</el-tag>
<el-tag v-else-if="scope.row.status === '1'" type="success">完成</el-tag>
<el-tag v-else-if="scope.row.status === '2'" type="danger">作废</el-tag>
<el-tag v-else-if="scope.row.status === '3'" type="info">终止</el-tag>
<span v-else>-</span>
</template>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="150">
<template #default="scope">
<div class="op-cell">
<el-button type="primary" link icon="View" @click="handleView(scope.row)"> 查看 </el-button>
<ActionDropdown :items="getActionMenuItems(scope.row)" @command="(command) => handleMoreCommand(command, scope.row)" />
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-if="state.pagination && state.pagination.total && state.pagination.total > 0"
:total="state.pagination.total"
:current="state.pagination.current"
:size="state.pagination.size"
@sizeChange="sizeChangeHandle"
@currentChange="currentChangeHandle"
/>
</el-card>
</div>
<!-- 新增/编辑/查看统一使用 form.vue 弹窗iframe 引入 add.vue -->
<FormDialog ref="formDialogRef" :dict-data="dictData" @refresh="getDataList" />
<!-- 履约验收弹窗 -->
<PurchasingAcceptModal ref="acceptModalRef" />
<!-- 查看审批过程申请单审批 / 文件审批 -->
<el-dialog
v-model="showFlowComment"
v-if="showFlowComment"
:title="currFlowCommentType === 'file' ? '查看文件审批过程' : '查看审批过程'"
top="20px"
width="90%"
append-to-body
destroy-on-close
@close="
currFlowJob = null;
currFlowCommentType = 'apply';
"
>
<FlowCommentTimeline v-if="currFlowJob" :key="String(currFlowJob.flowInstId) + currFlowCommentType" :curr-job="currFlowJob" />
</el-dialog>
<!-- 实施采购iframe 嵌入 implement.vue供列表与流程页面使用 -->
<ImplementForm ref="implementFormRef" />
<!-- 文件归档弹窗 -->
<FileArchiveDialog ref="fileArchiveDialogRef" />
<!-- 更新材料弹窗 -->
<UpdateFilesDialog ref="updateFilesDialogRef" @refresh="getDataList" />
<!-- 合同弹窗 -->
<ContractDialog ref="contractDialogRef" @refresh="getDataList" />
<!-- 补充材料弹窗 -->
<SupplementFilesDialog ref="supplementFilesDialogRef" @refresh="getDataList" />
<!-- 招标文件审核弹窗 -->
<!-- <DocAuditDialog ref="docAuditDialogRef" @refresh="getDataList" />-->
<!-- 采购代表弹窗 -->
<el-dialog v-model="representorDialogVisible" title="设置采购代表" width="500px" destroy-on-close>
<el-form label-width="100px">
<el-form-item label="身份">
<el-radio-group v-model="representorForm.identity">
<el-radio label="purchase_rep">采购代表</el-radio>
<el-radio label="judge">评委</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="选择方式">
<el-radio-group v-model="representorForm.mode">
<el-radio label="single">指定采购代表人</el-radio>
<el-radio label="multi">部门多人系统抽取</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="representorForm.mode === 'single'" label="采购代表人">
<el-select v-model="representorForm.teacherNo" placeholder="请选择" clearable filterable style="width: 100%">
<el-option
v-for="m in representorDeptMembers"
:key="m.userId || m.teacherNo || m.id"
:label="m.realName || m.name || m.teacherNo"
:value="m.userId || m.teacherNo || m.id"
/>
</el-select>
</el-form-item>
<el-form-item v-else label="部门多人">
<el-select
v-model="representorForm.multiIds"
multiple
placeholder="请选择多人,系统将自动抽取一人"
clearable
filterable
style="width: 100%"
>
<el-option
v-for="m in representorDeptMembers"
:key="m.userId || m.teacherNo || m.id"
:label="m.realName || m.name || m.teacherNo"
:value="m.userId || m.teacherNo || m.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="representorDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="representorSubmitting" @click="handleSaveRepresentor">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="PurchasingRequisition">
import { ref, reactive, defineAsyncComponent, onMounted, computed } from 'vue';
import { useRouter } from 'vue-router';
import { BasicTableProps, useTable } from '/@/hooks/table';
import {
getPage,
delObj,
submitObj,
getApplyTemplateDownloadUrl,
getFileApplyTemplateDownloadUrl,
getDeptMembers,
saveRepresentor,
listDownloadUrls,
updateFiles,
} from '/@/api/purchase/purchasingrequisition';
import { useMessage, useMessageBox } from '/@/hooks/message';
import { useAuth } from '/@/hooks/auth';
import { getDicts } from '/@/api/admin/dict';
import { getTree } from '/@/api/purchase/purchasingcategory';
import {
List,
Document,
DocumentCopy,
Search,
Money,
CircleCheck,
InfoFilled,
Calendar,
OfficeBuilding,
Warning,
DocumentChecked,
Edit,
Delete,
Upload,
FolderOpened,
Download,
User,
RefreshRight,
Shop,
CopyDocument,
} from '@element-plus/icons-vue';
import other from '/@/utils/other';
import { Session } from '/@/utils/storage';
import { getByPurchaseId } from '/@/api/purchase/purchasingcontract';
// 角色常量
const PURCHASE_DEPT_AUDIT_ROLE_CODE = 'PURCHASE_DEPT_AUDIT';
const roleCode = computed(() => Session.getRoleCode() || '');
const isDeptAuditRole = computed(() => roleCode.value === PURCHASE_DEPT_AUDIT_ROLE_CODE);
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const ImplementForm = defineAsyncComponent(() => import('./implementForm.vue'));
const ActionDropdown = defineAsyncComponent(() => import('/@/components/tools/action-dropdown.vue'));
const PurchasingAcceptModal = defineAsyncComponent(() => import('./accept/PurchasingAcceptModal.vue'));
const FlowCommentTimeline = defineAsyncComponent(() => import('/@/views/jsonflow/comment/timeline.vue'));
const FileArchiveDialog = defineAsyncComponent(() => import('./FileArchiveDialog.vue'));
const UpdateFilesDialog = defineAsyncComponent(() => import('./UpdateFilesDialog.vue'));
const ContractDialog = defineAsyncComponent(() => import('./contract/ContractDialog.vue'));
const SupplementFilesDialog = defineAsyncComponent(() => import('./SupplementFilesDialog.vue'));
// const DocAuditDialog = defineAsyncComponent(() => import('./docAudit/DocAuditDialog.vue'));
// 字典数据和品目树数据
const dictData = ref({
fundSourceList: [] as any[],
isCentralizedList: [] as any[],
isSpecialList: [] as any[],
purchaseTypeDeptList: [] as any[],
purchaseModeSchoolList: [] as any[],
purchaseTypeUnionList: [] as any[],
categoryTreeData: [] as any[],
});
// 定义变量内容
const router = useRouter();
const tableRef = ref();
const formDialogRef = ref();
const acceptModalRef = ref();
const searchFormRef = ref();
const showSearch = ref(true);
const docAuditDialogRef = ref();
const { hasAuth } = useAuth();
/** 审批过程弹窗:是否显示、当前行对应的流程 job供 Comment 组件用)、类型(申请单/文件) */
const showFlowComment = ref(false);
const currFlowJob = ref<{ id?: number; flowInstId?: number } | null>(null);
const currFlowCommentType = ref<'apply' | 'file'>('apply');
const implementFormRef = ref();
const fileArchiveDialogRef = ref();
const updateFilesDialogRef = ref();
const contractDialogRef = ref();
const supplementFilesDialogRef = ref();
/** 采购代表弹窗 */
const representorDialogVisible = ref(false);
const representorCurrentRow = ref<any>(null);
const representorForm = reactive({
mode: 'single' as 'single' | 'multi',
teacherNo: '',
multiIds: [] as string[],
identity: 'purchase_rep' as 'purchase_rep' | 'judge',
});
const representorDeptMembers = ref<any[]>([]);
const representorSubmitting = ref(false);
const openRepresentorDialog = async (row: any) => {
representorCurrentRow.value = row;
representorForm.mode = 'single';
representorForm.teacherNo = '';
representorForm.multiIds = [];
representorDialogVisible.value = true;
try {
const res = await getDeptMembers();
representorDeptMembers.value = res?.data || [];
} catch (_) {
representorDeptMembers.value = [];
}
};
const handleSaveRepresentor = async () => {
const row = representorCurrentRow.value;
const id = row?.id ?? row?.purchaseId;
if (id == null || id === '') {
useMessage().warning('无法获取申请单ID');
return;
}
if (representorForm.mode === 'single' && !representorForm.teacherNo) {
useMessage().warning('请选择采购代表人');
return;
}
if (representorForm.mode === 'multi' && !representorForm.multiIds.length) {
useMessage().warning('请选择部门多人');
return;
}
representorSubmitting.value = true;
try {
const teacherNo = representorForm.mode === 'single' ? representorForm.teacherNo : undefined;
const multiIds = representorForm.mode === 'multi' ? representorForm.multiIds.join(',') : undefined;
await saveRepresentor(Number(id), teacherNo, multiIds, representorForm.identity);
useMessage().success('保存采购代表成功');
representorDialogVisible.value = false;
getDataList();
} catch (e: any) {
useMessage().error(e?.msg || '保存采购代表失败');
} finally {
representorSubmitting.value = false;
}
};
/**
* 定义响应式表格数据
*/
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: getPage,
queryForm: {
purchaseNo: '',
projectName: '',
projectType: '',
status: '',
isCentralized: '',
},
createdIsNeed: true,
});
/**
* 使用 useTable 定义表格相关操作
*/
const { getDataList, tableStyle, sizeChangeHandle, currentChangeHandle } = useTable(state);
/**
* 重置搜索表单
*/
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
/**
* 新增采购申请 - 统一通过 form.vue 弹窗iframe 引入 add.vue
*/
const handleAdd = () => {
formDialogRef.value?.openDialog('add');
};
/**
/**
* 点击审核状态:若有流程实例则打开「查看审批过程」弹窗(参考 hi-job.vue
* @param row - 当前行数据(需含 flowInstId
*/
/** 点击审核状态:打开申请单审批过程 */
const handleShowFlowComment = (row: any) => {
if (!row?.flowInstId) {
useMessage().info('暂存状态无审批过程');
return;
}
currFlowCommentType.value = 'apply';
currFlowJob.value = { id: row.id, flowInstId: row.flowInstId };
showFlowComment.value = true;
};
/** 点击文件审批状态:打开文件审批过程 */
const handleShowFileFlowComment = (row: any) => {
if (!row?.fileFlowInstId) {
useMessage().info('未发起文件审批流程');
return;
}
currFlowCommentType.value = 'file';
const flowInstId = typeof row.fileFlowInstId === 'string' ? parseInt(row.fileFlowInstId, 10) : row.fileFlowInstId;
currFlowJob.value = { id: row.id, flowInstId: Number.isNaN(flowInstId) ? row.fileFlowInstId : flowInstId };
showFlowComment.value = true;
};
/**
* 打开查看对话框
* @param row - 当前行数据
*/
const handleView = (row: any) => {
formDialogRef.value?.openDialog('view', row);
};
/**
* 打开编辑对话框
* @param row - 当前行数据
*/
const handleEdit = (row: any) => {
formDialogRef.value?.openDialog('edit', row);
};
/**
* 履约验收
* @param row - 当前行数据
*/
const handleAccept = (row: any) => {
acceptModalRef.value?.open(row);
};
/** 打开实施采购(仅暂存状态可点;通过 iframe 嵌入 implement.vue */
const handleImplement = (row: any) => {
implementFormRef.value?.openDialog(row);
};
/** 复制到剪贴板 */
const copyToClipboard = async (text: string) => {
if (!text) return;
try {
await navigator.clipboard.writeText(text);
useMessage().success('复制成功');
} catch (err) {
// 降级方案
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
useMessage().success('复制成功');
}
};
/** 打开招标文件审核 */
const handleDocAudit = (row: any) => {
docAuditDialogRef.value?.open(row);
};
/**
* 删除当前行
* @param row - 当前行数据
*/
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?');
} catch {
return;
}
try {
await delObj({ id: row.id });
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
/** 暂存状态下提交采购申请(启动流程) */
const handleSubmit = async (row: any) => {
try {
await useMessageBox().confirm('确定要提交该采购申请并启动流程吗?');
} catch {
return;
}
try {
await submitObj({ id: row.id });
useMessage().success('提交成功');
getDataList();
} catch (err: any) {
useMessage().error(err?.msg || '提交失败');
}
};
/** 操作栏「更多」菜单项配置 */
const getActionMenuItems = (row: any) => {
const isTemp = row?.status === '-1';
const isRunning = row?.status === '0';
const isCompleted = row?.status === '1';
const items = [
{
command: 'edit',
label: '编辑',
icon: Edit,
visible: () => isTemp,
},
{
command: 'submit',
label: '提交',
icon: Upload,
visible: () => isTemp,
},
{
command: 'delete',
label: '删除',
icon: Delete,
visible: () => isTemp,
},
{
command: 'accept',
label: '履约验收',
icon: DocumentChecked,
visible: () => isCompleted && hasAuth('purchase_accept'),
},
{
command: 'implement',
label: '实施采购',
icon: Upload,
visible: () => isCompleted && hasAuth('purchase_implement'),
},
{
command: 'archive',
label: '文件归档',
icon: FolderOpened,
visible: () => isCompleted && hasAuth('purchase_archive'),
},
{
command: 'downloadApply',
label: '下载审批表',
icon: Download,
visible: () => isCompleted,
},
{
command: 'representor',
label: '采购代表',
icon: User,
visible: () => isCompleted && isDeptAuditRole.value,
},
{
command: 'updateFiles',
label: '更新材料',
icon: RefreshRight,
visible: () => (isCompleted || isRunning) && hasAuth('purchase_purchasingapply_edit'),
},
{
command: 'contract',
label: '采购合同',
icon: DocumentChecked,
visible: () => isCompleted,
},
{
command: 'supplementFiles',
label: '补充材料',
icon: Upload,
visible: () => isCompleted && row?.purchaseMode === '1' && row?.purchaseChannel === '1' && hasAuth('purchase_supplement'),
},
{
command: 'submitSupplementFiles',
label: '提交补充材料',
icon: Upload,
visible: () => isCompleted && row?.purchaseMode === '1' && row?.purchaseChannel === '1' && row?.supplementFlowStatus === '-1' && hasAuth('purchase_supplement'),
},
// {
// command: 'downloadFileApply',
// label: '下载文件审批表',
// icon: Download,
// visible: () => true,
// },
// {
// command: 'docAudit',
// label: '招标文件审核',
// icon: DocumentChecked,
// visible: () => row?.implementType === '2' && row?.agentId,
// },
];
// 过滤出有权限且可见的菜单项
return items.filter((item) => {
if (item.visible === undefined) return true;
if (typeof item.visible === 'boolean') return item.visible;
if (typeof item.visible === 'function') return item.visible();
return false;
});
};
/** 处理更多操作下拉菜单命令 */
const handleMoreCommand = (command: string, row: any) => {
switch (command) {
case 'edit':
handleEdit(row);
break;
case 'submit':
handleSubmit(row);
break;
case 'delete':
handleDelete(row);
break;
case 'accept':
handleAccept(row);
break;
case 'implement':
handleImplement(row);
break;
case 'archive':
handleArchive(row);
break;
case 'downloadApply':
handleDownloadApply(row);
break;
case 'downloadFileApply':
handleDownloadFileApply(row);
break;
case 'docAudit':
handleDocAudit(row);
break;
case 'representor':
openRepresentorDialog(row);
break;
case 'updateFiles':
handleUpdateFiles(row);
break;
case 'contract':
handleContract(row);
break;
case 'supplementFiles':
handleSupplementFiles(row);
break;
case 'submitSupplementFiles':
handleSubmitSupplementFiles(row);
break;
}
};
/** 更新材料 */
const handleUpdateFiles = (row: any) => {
const id = row?.id ?? row?.purchaseId;
if (!id) {
useMessage().warning('无法获取采购申请ID');
return;
}
updateFilesDialogRef.value?.open(id, row.purchaseNo);
};
/** 采购合同 */
const handleContract = async (row: any) => {
const id = row?.id ?? row?.purchaseId;
if (!id) {
useMessage().warning('无法获取采购申请ID');
return;
}
try {
const res = await getByPurchaseId(id);
contractDialogRef.value?.open(row, res?.data || null);
} catch (e) {
useMessage().error('获取合同信息失败');
}
};
/** 补充材料 */
const handleSupplementFiles = (row: any) => {
const id = row?.id ?? row?.purchaseId;
if (!id) {
useMessage().warning('无法获取采购申请ID');
return;
}
supplementFilesDialogRef.value?.open(String(id));
};
/** 提交补充材料 */
const handleSubmitSupplementFiles = async (row: any) => {
const id = row?.id ?? row?.purchaseId;
if (!id) {
useMessage().warning('无法获取采购申请ID');
return;
}
supplementFilesDialogRef.value?.open(String(id));
};
/** 下载审批表 */
const handleDownloadApply = (row: any) => {
const id = row?.id ?? row?.purchaseId;
if (id == null || id === '') {
useMessage().warning('无法获取申请单ID');
return;
}
const url = getApplyTemplateDownloadUrl(id);
const fileName = `审批表_${row?.purchaseNo || id}.docx`;
other.downBlobFile(url, {}, fileName);
};
/** 下载文件审批表 */
const handleDownloadFileApply = (row: any) => {
const id = row?.id ?? row?.purchaseId;
if (id == null || id === '') {
useMessage().warning('无法获取申请单ID');
return;
}
const url = getFileApplyTemplateDownloadUrl(id);
const fileName = `文件审批表_${row?.purchaseNo || id}.docx`;
other.downBlobFile(url, {}, fileName);
};
/** 文件归档:打开弹窗查看文件列表,支持打包下载 */
const handleArchive = (row: any) => {
const id = row?.id ?? row?.purchaseId;
if (id == null || id === '') {
useMessage().warning('无法获取申请单ID');
return;
}
fileArchiveDialogRef.value?.open(String(id), row?.purchaseNo);
};
// 获取字典数据和品目树数据
const loadDictData = async () => {
try {
const [fundSourceRes, isCentralizedRes, isSpecialRes, purchaseTypeDeptRes, purchaseModeSchoolRes, purchaseTypeUnionRes, categoryTreeRes] =
await Promise.all([
getDicts('PURCHASE_FUND_SOURCE'),
getDicts('PURCHASE_IS_CEN'),
getDicts('PURCHASE_IS_SPEC'),
getDicts('PURCHASE_TYPE_DEPT'),
getDicts('PURCHASE_MODE_SCHOOL'),
getDicts('PURCHASE_TYPE_UNION'),
getTree(),
]);
// 处理资金来源字典
if (fundSourceRes.data && Array.isArray(fundSourceRes.data)) {
dictData.value.fundSourceList = fundSourceRes.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code,
}));
} else {
dictData.value.fundSourceList = [
{ label: '切块经费', value: '0' },
{ label: '设备购置费', value: '1' },
{ label: '专项经费', value: '2' },
{ label: '代办费', value: '3' },
{ label: '培训经费', value: '4' },
{ label: '日常公用经费', value: '5' },
{ label: '技能大赛经费', value: '6' },
{ label: '基本建设资金', value: '7' },
{ label: '暂存款', value: '8' },
{ label: '会议费', value: '9' },
];
}
// 处理是否集采字典
if (isCentralizedRes.data && Array.isArray(isCentralizedRes.data)) {
dictData.value.isCentralizedList = isCentralizedRes.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code,
}));
} else {
dictData.value.isCentralizedList = [
{ label: '否', value: '0' },
{ label: '政府集中采购', value: '1' },
{ label: '学校集中采购', value: '2' },
];
}
// 处理是否特殊情况字典
if (isSpecialRes.data && Array.isArray(isSpecialRes.data)) {
dictData.value.isSpecialList = isSpecialRes.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code,
}));
} else {
dictData.value.isSpecialList = [
{ label: '否', value: '0' },
{ label: '紧急', value: '1' },
{ label: '单一', value: '2' },
{ label: '进口', value: '3' },
];
}
// 处理部门采购方式字典
if (purchaseTypeDeptRes.data && Array.isArray(purchaseTypeDeptRes.data)) {
dictData.value.purchaseTypeDeptList = purchaseTypeDeptRes.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code,
}));
}
// 处理学校采购形式字典
if (purchaseModeSchoolRes.data && Array.isArray(purchaseModeSchoolRes.data)) {
dictData.value.purchaseModeSchoolList = purchaseModeSchoolRes.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code,
}));
} else {
dictData.value.purchaseModeSchoolList = [
{ label: '政府采购', value: '1' },
{ label: '学校自主采购', value: '2' },
];
}
// 处理学校统一采购方式字典
if (purchaseTypeUnionRes.data && Array.isArray(purchaseTypeUnionRes.data)) {
dictData.value.purchaseTypeUnionList = purchaseTypeUnionRes.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code,
}));
}
// 处理品目树数据
if (categoryTreeRes.data && Array.isArray(categoryTreeRes.data)) {
dictData.value.categoryTreeData = categoryTreeRes.data;
} else {
dictData.value.categoryTreeData = [];
}
} catch (err) {
console.error('加载字典数据失败', err);
// 设置默认值
dictData.value.fundSourceList = [
{ label: '切块经费', value: '0' },
{ label: '设备购置费', value: '1' },
{ label: '专项经费', value: '2' },
{ label: '代办费', value: '3' },
{ label: '培训经费', value: '4' },
{ label: '日常公用经费', value: '5' },
{ label: '技能大赛经费', value: '6' },
{ label: '基本建设资金', value: '7' },
{ label: '暂存款', value: '8' },
{ label: '会议费', value: '9' },
];
dictData.value.isCentralizedList = [
{ label: '否', value: '0' },
{ label: '政府集中采购', value: '1' },
{ label: '学校集中采购', value: '2' },
];
dictData.value.isSpecialList = [
{ label: '否', value: '0' },
{ label: '紧急', value: '1' },
{ label: '单一', value: '2' },
{ label: '进口', value: '3' },
];
dictData.value.purchaseModeSchoolList = [
{ label: '政府采购', value: '1' },
{ label: '学校自主采购', value: '2' },
];
dictData.value.categoryTreeData = [];
}
};
// 页面加载时获取字典数据和品目树数据
onMounted(() => {
loadDictData();
});
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
.op-cell {
display: flex;
align-items: center;
justify-content: center;
}
.status-tag-clickable {
cursor: pointer;
}
:deep(.el-tag) {
border-radius: 4px;
}
:deep(.el-tag--success) {
--el-tag-bg-color: #e8f5e9;
--el-tag-border-color: #c8e6c9;
--el-tag-text-color: #2e7d32;
}
:deep(.el-tag--warning) {
--el-tag-bg-color: #fff8e1;
--el-tag-border-color: #ffecb3;
--el-tag-text-color: #f57c00;
}
:deep(.el-tag--danger) {
--el-tag-bg-color: #ffebee;
--el-tag-border-color: #ffcdd2;
--el-tag-text-color: #c62828;
}
:deep(.el-tag--primary) {
--el-tag-bg-color: #e3f2fd;
--el-tag-border-color: #bbdefb;
--el-tag-text-color: #1565c0;
}
:deep(.el-tag--info) {
--el-tag-bg-color: #f5f5f5;
--el-tag-border-color: #e0e0e0;
--el-tag-text-color: #616161;
}
.purchase-no-cell {
display: flex;
align-items: center;
gap: 4px;
.purchase-no-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 120px;
}
.copy-btn {
flex-shrink: 0;
}
}
.purchase-mode-cell {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
.entrust-tag {
font-size: 11px;
padding: 0 4px;
height: 18px;
line-height: 16px;
}
}
</style>