fix
This commit is contained in:
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* 招标文件处理统一API
|
||||
* 整合招标代理和审核部门的接口调用
|
||||
*/
|
||||
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取列表数据(根据模式调用不同接口)
|
||||
* @param mode 模式:agent-招标代理,audit-审核部门
|
||||
* @param params 分页参数
|
||||
*/
|
||||
export function getDocProcessList(mode: string, params?: any) {
|
||||
const url = mode === 'agent' ? '/purchase/purchasingdoc/agent/list' : '/purchase/purchasingdoc/audit/page';
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取采购需求文件列表(招标代理专用)
|
||||
* @param applyId 采购申请ID
|
||||
*/
|
||||
export function getRequirementFiles(applyId: number | string) {
|
||||
return request({
|
||||
url: `/purchase/purchasingdoc/agent/requirement/${applyId}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取采购需求文件列表(审核人员专用)
|
||||
* @param applyId 采购申请ID
|
||||
*/
|
||||
export function getRequirementFilesForAudit(applyId: number | string) {
|
||||
return request({
|
||||
url: `/purchase/purchasingdoc/audit/requirement/${applyId}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取招标文件列表
|
||||
* @param applyId 采购申请ID
|
||||
*/
|
||||
export function getDocList(applyId: number | string) {
|
||||
return request({
|
||||
url: `/purchase/purchasingdoc/list/${applyId}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传招标文件(招标代理)
|
||||
* @param data 文件数据
|
||||
*/
|
||||
export function uploadDoc(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/upload',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新上传招标文件
|
||||
* @param data 文件数据
|
||||
*/
|
||||
export function reuploadDoc(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/reupload',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取招标文件下载地址
|
||||
* @param id 招标文件ID
|
||||
*/
|
||||
export function getDocDownloadUrl(id: number | string) {
|
||||
return `/purchase/purchasingdoc/download/${id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载招标文件(返回blob)
|
||||
* @param id 招标文件ID
|
||||
*/
|
||||
export function downloadDocById(id: number | string) {
|
||||
return request({
|
||||
url: `/purchase/purchasingdoc/download/${id}`,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件ID下载采购附件(返回blob)
|
||||
* @param fileId 文件ID
|
||||
*/
|
||||
export function downloadFileById(fileId: string | number) {
|
||||
return request({
|
||||
url: '/purchase/purchasingfiles/downloadById',
|
||||
method: 'get',
|
||||
params: { fileId },
|
||||
responseType: 'blob',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认无误
|
||||
* @param data 审核信息
|
||||
*/
|
||||
export function confirmDoc(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/confirm',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 退回修改
|
||||
* @param data 审核信息
|
||||
*/
|
||||
export function returnDoc(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/return',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认流程结束
|
||||
* @param applyId 采购申请ID
|
||||
*/
|
||||
export function completeDoc(applyId: number | string) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/complete',
|
||||
method: 'post',
|
||||
params: { applyId },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审核记录
|
||||
* @param applyId 采购申请ID
|
||||
*/
|
||||
export function getAuditRecords(applyId: number | string) {
|
||||
return request({
|
||||
url: `/purchase/purchasingdoc/audit-records/${applyId}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可执行操作
|
||||
* @param applyId 采购申请ID
|
||||
*/
|
||||
export function getAvailableActions(applyId: number | string) {
|
||||
return request({
|
||||
url: `/purchase/purchasingdoc/actions/${applyId}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取采购申请附件列表
|
||||
* @param purchaseId 采购申请ID
|
||||
*/
|
||||
export function getApplyFiles(purchaseId: string | number) {
|
||||
return request({
|
||||
url: '/purchase/purchasingfiles/applyFiles',
|
||||
method: 'post',
|
||||
params: { purchaseId },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件上传地址
|
||||
*/
|
||||
export function getFileUploadUrl() {
|
||||
const baseUrl = import.meta.env.VITE_API_URL || '';
|
||||
return `${baseUrl}/purchase/purchasingfiles/upload`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存草稿(招标代理)
|
||||
* @param data 文件信息
|
||||
*/
|
||||
export function saveDraft(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/save-draft',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交草稿(招标代理)
|
||||
* @param data 文件信息
|
||||
*/
|
||||
export function submitDraft(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/submit-draft',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 补充上传(资产管理处)
|
||||
* @param data 文件信息(含fileRemark)
|
||||
*/
|
||||
export function supplyUpload(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/supply-upload',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交至需求部门(资产管理处)
|
||||
* @param data 审核信息
|
||||
*/
|
||||
export function submitToDept(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/submit-to-dept',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交至内审部门(资产管理处)
|
||||
* @param data 审核信息
|
||||
*/
|
||||
export function submitToAudit(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/submit-to-audit',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交至资产管理处(需求部门/内审部门)
|
||||
* @param data 审核信息
|
||||
*/
|
||||
export function submitToAsset(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/submit-to-asset',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 定稿(资产管理处)
|
||||
* @param data 审核信息
|
||||
*/
|
||||
export function finalizeDoc(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/finalize',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取采购代表设置信息
|
||||
* @param applyId 采购申请ID
|
||||
*/
|
||||
export function getReviewerSetting(applyId: string | number) {
|
||||
return request({
|
||||
url: `/purchase/purchasingdoc/reviewer/${applyId}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置采购代表
|
||||
* @param data 设置信息
|
||||
*/
|
||||
export function setReviewerSetting(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/reviewer/set',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机抽取采购代表
|
||||
* @param data 候选人列表
|
||||
*/
|
||||
export function randomSelectReviewer(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingdoc/reviewer/random',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
@@ -33,3 +33,35 @@ export function getFileTypes() {
|
||||
export function getDeptSelfMeetingFiletype() {
|
||||
return '140';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 120 采购需求表文件类型
|
||||
*/
|
||||
export function getPurchaseRequirementFiletype() {
|
||||
return '120';
|
||||
}
|
||||
|
||||
/**
|
||||
* 按文件类型获取采购附件列表
|
||||
* @param purchaseId 采购申请ID
|
||||
* @param fileType 文件类型(可选)
|
||||
*/
|
||||
export function getFilesByType(purchaseId: string, fileType?: string) {
|
||||
return request({
|
||||
url: '/purchase/purchasingfiles/listByType',
|
||||
method: 'get',
|
||||
params: { purchaseId, fileType },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取采购需求文件列表(招标代理可见)
|
||||
* @param purchaseId 采购申请ID
|
||||
*/
|
||||
export function getRequirementFiles(purchaseId: string) {
|
||||
return request({
|
||||
url: '/purchase/purchasingfiles/listByType',
|
||||
method: 'get',
|
||||
params: { purchaseId, fileType: '120' },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ const init = () => {
|
||||
let flowInstId = props.currJob.flowInstId;
|
||||
let runJobId = props.currJob.id;
|
||||
src.value = props.currElTab.path + `?token=${token}&tenantId=${tenantId}&flowInstId=${flowInstId}&runJobId=${runJobId}`;
|
||||
console.log(src.value)
|
||||
};
|
||||
|
||||
function handleJob(jobBtn) {
|
||||
|
||||
@@ -21,6 +21,7 @@ const modules: Record<string, () => Promise<unknown>> = import.meta.glob([
|
||||
'../../views/purchase/*/*.vue',
|
||||
'../../views/purchase/purchasingrequisition/add.vue',
|
||||
'../../views/purchase/purchasingrequisition/implement.vue',
|
||||
'../../views/purchase/purchasingrequisition/bidfile-audit.vue',
|
||||
]);
|
||||
|
||||
/**
|
||||
|
||||
@@ -107,6 +107,14 @@ export const staticRoutes: Array<RouteRecordRaw> = [
|
||||
isAuth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/purchase/purchasingrequisition/bidfile-audit',
|
||||
name: 'purchase.purchasingrequisition.bidfile-audit',
|
||||
component: () => import('/@/views/purchase/purchasingrequisition/bidfile-audit.vue'),
|
||||
meta: {
|
||||
isAuth: true,
|
||||
},
|
||||
},
|
||||
|
||||
...staticRoutesFlow,
|
||||
];
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<el-table :data="records" stripe v-loading="loading" max-height="400">
|
||||
<el-table-column prop="operateTypeDesc" label="操作类型" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getOperateTypeStyle(scope.row.operateType)">
|
||||
{{ scope.row.operateTypeDesc }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operateRoleDesc" label="操作角色" width="100" />
|
||||
<el-table-column prop="operateByName" label="操作人" width="100" />
|
||||
<el-table-column prop="currentVersion" label="文件版本" width="80" align="center" />
|
||||
<el-table-column prop="remark" label="批注意见" min-width="200" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.remark || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operateTime" label="操作时间" width="160" />
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { getAuditRecords } from '/@/api/purchase/docProcess';
|
||||
|
||||
const props = defineProps<{
|
||||
applyId: number | string;
|
||||
}>();
|
||||
|
||||
const records = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const loadRecords = async () => {
|
||||
if (!props.applyId) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getAuditRecords(props.applyId);
|
||||
records.value = res.data || [];
|
||||
} catch (e) {
|
||||
records.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const refresh = () => {
|
||||
loadRecords();
|
||||
};
|
||||
|
||||
const getOperateTypeStyle = (type: string) => {
|
||||
const styleMap: Record<string, string> = {
|
||||
UPLOAD: 'primary',
|
||||
CONFIRM: 'success',
|
||||
RETURN: 'warning',
|
||||
COMPLETE: 'success',
|
||||
};
|
||||
return styleMap[type] || 'info';
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.applyId,
|
||||
() => {
|
||||
loadRecords();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
defineExpose({ refresh });
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,397 +0,0 @@
|
||||
<template>
|
||||
<div class="reviewer-setting">
|
||||
<el-form :model="formData" label-width="100px" v-loading="loading">
|
||||
<!-- 当前采购代表 -->
|
||||
<el-form-item label="当前代表" v-if="currentRepresentor.teacherName">
|
||||
<el-tag type="success">{{ currentRepresentor.teacherName }} ({{ currentRepresentor.teacherNo }})</el-tag>
|
||||
<el-tag v-if="currentRepresentor.representorType" type="info" style="margin-left: 8px">{{
|
||||
getRepresentorTypeLabel(currentRepresentor.representorType)
|
||||
}}</el-tag>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 选择方式 -->
|
||||
<el-form-item label="选择方式">
|
||||
<el-radio-group v-model="formData.selectMode" @change="handleSelectModeChange">
|
||||
<el-radio label="DESIGNATED">指定人员</el-radio>
|
||||
<el-radio label="RANDOM">随机抽取</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 指定人员模式 -->
|
||||
<el-form-item v-if="formData.selectMode === 'DESIGNATED'" label="选择人员" required>
|
||||
<org-selector v-model:orgList="selectedUserList" type="user" :multiple="false" @update:orgList="handleUserChange" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 随机抽取模式 -->
|
||||
<template v-if="formData.selectMode === 'RANDOM'">
|
||||
<el-form-item label="选择候选人" required>
|
||||
<org-selector v-model:orgList="candidateUserList" type="user" :multiple="true" @update:orgList="handleCandidateChange" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 候选人列表 -->
|
||||
<el-form-item label="候选人列表" v-if="candidates.length > 0">
|
||||
<el-table :data="candidates" stripe size="small" max-height="200">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="teacherNo" label="工号" width="120" />
|
||||
<el-table-column prop="teacherName" label="姓名" />
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 随机抽取结果 -->
|
||||
<el-form-item label="抽取结果">
|
||||
<div class="random-roller">
|
||||
<span v-if="rollingName" class="rolling">{{ rollingName }}</span>
|
||||
<span v-else-if="selectedCandidate.teacherName" class="selected">
|
||||
已抽取:{{ selectedCandidate.teacherName }} ({{ selectedCandidate.teacherNo }})
|
||||
</span>
|
||||
<span v-else class="placeholder">点击下方按钮进行随机抽取</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 随机抽取按钮 -->
|
||||
<el-form-item v-if="candidates.length > 1">
|
||||
<el-button type="primary" :loading="rolling" @click="handleRandomSelect">
|
||||
{{ rolling ? '抽取中...' : '随机抽取' }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 人员类型选择 -->
|
||||
<el-form-item label="人员类型">
|
||||
<el-select v-model="formData.representorType" placeholder="请选择人员类型" clearable style="width: 200px">
|
||||
<el-option v-for="item in representorTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 保存按钮 -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" :disabled="!canSave" @click="handleSave"> 保存设置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ReviewerSetting">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { getReviewerSetting, setReviewerSetting, randomSelectReviewer } from '/@/api/purchase/docProcess';
|
||||
import orgSelector from '/@/components/OrgSelector/index.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
applyId: string | number;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'saved'): void;
|
||||
}>();
|
||||
|
||||
// 常量
|
||||
const SELECT_MODE = {
|
||||
DESIGNATED: 'DESIGNATED',
|
||||
RANDOM: 'RANDOM',
|
||||
} as const;
|
||||
|
||||
// 字典数据
|
||||
const { PURCHASE_REPRESENTOR_TYPE: representorTypeDict } = useDict('PURCHASE_REPRESENTOR_TYPE');
|
||||
|
||||
// 人员类型选项
|
||||
const representorTypeOptions = computed(() => {
|
||||
return (representorTypeDict.value || []).map((item: any) => ({
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
}));
|
||||
});
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
selectMode: 'DESIGNATED' as 'DESIGNATED' | 'RANDOM',
|
||||
teacherNo: '',
|
||||
teacherName: '',
|
||||
candidates: [] as Array<{ teacherNo: string; teacherName: string }>,
|
||||
representorType: '',
|
||||
});
|
||||
|
||||
// 当前采购代表(已有设置)
|
||||
const currentRepresentor = ref({
|
||||
teacherNo: '',
|
||||
teacherName: '',
|
||||
representorType: '',
|
||||
});
|
||||
|
||||
// 用户选择相关
|
||||
const selectedUserList = ref<any[]>([]);
|
||||
const candidateUserList = ref<any[]>([]);
|
||||
|
||||
// 候选人列表
|
||||
const candidates = ref<Array<{ teacherNo: string; teacherName: string }>>([]);
|
||||
|
||||
// 随机抽取相关
|
||||
const rolling = ref(false);
|
||||
const rollingName = ref('');
|
||||
const selectedCandidate = ref<{ teacherNo: string; teacherName: string }>({ teacherNo: '', teacherName: '' });
|
||||
let rollInterval: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
|
||||
// 是否可以保存
|
||||
const canSave = computed(() => {
|
||||
if (formData.value.selectMode === SELECT_MODE.DESIGNATED) {
|
||||
return formData.value.teacherNo && formData.value.teacherName;
|
||||
} else {
|
||||
return formData.value.teacherNo && formData.value.teacherName && candidates.value.length > 0;
|
||||
}
|
||||
});
|
||||
|
||||
// 处理选择方式变化
|
||||
const handleSelectModeChange = () => {
|
||||
formData.value.teacherNo = '';
|
||||
formData.value.teacherName = '';
|
||||
formData.value.candidates = [];
|
||||
selectedUserList.value = [];
|
||||
candidateUserList.value = [];
|
||||
candidates.value = [];
|
||||
selectedCandidate.value = { teacherNo: '', teacherName: '' };
|
||||
rollingName.value = '';
|
||||
};
|
||||
|
||||
// 获取人员类型标签
|
||||
const getRepresentorTypeLabel = (value: string): string => {
|
||||
const option = representorTypeOptions.value.find((item: any) => item.value === value);
|
||||
return option ? option.label : value;
|
||||
};
|
||||
|
||||
// 处理指定人员变化
|
||||
const handleUserChange = (list: any[]) => {
|
||||
if (list && list.length > 0) {
|
||||
const user = list[0];
|
||||
formData.value.teacherNo = user.username || user.userName || '';
|
||||
formData.value.teacherName = user.name || user.realName || '';
|
||||
} else {
|
||||
formData.value.teacherNo = '';
|
||||
formData.value.teacherName = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 处理候选人变化
|
||||
const handleCandidateChange = (list: any[]) => {
|
||||
candidates.value = (list || []).map((user) => ({
|
||||
teacherNo: user.username || user.userName || '',
|
||||
teacherName: user.name || user.realName || '',
|
||||
}));
|
||||
// 重置已选结果
|
||||
if (candidates.value.length > 0) {
|
||||
selectedCandidate.value = { ...candidates.value[0] };
|
||||
formData.value.teacherNo = selectedCandidate.value.teacherNo;
|
||||
formData.value.teacherName = selectedCandidate.value.teacherName;
|
||||
} else {
|
||||
selectedCandidate.value = { teacherNo: '', teacherName: '' };
|
||||
formData.value.teacherNo = '';
|
||||
formData.value.teacherName = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 随机抽取动画
|
||||
const startRollingAnimation = (finalCandidate: { teacherNo: string; teacherName: string }) => {
|
||||
if (candidates.value.length === 0) return;
|
||||
|
||||
if (rollInterval) {
|
||||
clearInterval(rollInterval);
|
||||
rollInterval = null;
|
||||
}
|
||||
|
||||
rollingName.value = '';
|
||||
rolling.value = true;
|
||||
|
||||
let currentIndex = 0;
|
||||
const totalDuration = 2000;
|
||||
const intervalTime = 80;
|
||||
|
||||
rollInterval = setInterval(() => {
|
||||
rollingName.value = candidates.value[currentIndex].teacherName;
|
||||
currentIndex = (currentIndex + 1) % candidates.value.length;
|
||||
}, intervalTime);
|
||||
|
||||
setTimeout(() => {
|
||||
if (rollInterval) {
|
||||
clearInterval(rollInterval);
|
||||
rollInterval = null;
|
||||
}
|
||||
rolling.value = false;
|
||||
rollingName.value = '';
|
||||
selectedCandidate.value = finalCandidate;
|
||||
formData.value.teacherNo = finalCandidate.teacherNo;
|
||||
formData.value.teacherName = finalCandidate.teacherName;
|
||||
}, totalDuration);
|
||||
};
|
||||
|
||||
// 执行随机抽取
|
||||
const handleRandomSelect = async () => {
|
||||
if (candidates.value.length < 2) {
|
||||
useMessage().warning('请至少选择2位候选人');
|
||||
return;
|
||||
}
|
||||
|
||||
rolling.value = true;
|
||||
try {
|
||||
const res = await randomSelectReviewer({
|
||||
applyId: props.applyId,
|
||||
selectMode: SELECT_MODE.RANDOM,
|
||||
candidates: candidates.value,
|
||||
});
|
||||
const result = res?.data || res;
|
||||
if (result?.teacherNo) {
|
||||
startRollingAnimation({
|
||||
teacherNo: result.teacherNo,
|
||||
teacherName: result.teacherName || '',
|
||||
});
|
||||
}
|
||||
} catch (e: any) {
|
||||
rolling.value = false;
|
||||
useMessage().error(e?.msg || '随机抽取失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 保存设置
|
||||
const handleSave = async () => {
|
||||
if (!canSave.value) {
|
||||
useMessage().warning('请完善设置信息');
|
||||
return;
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
try {
|
||||
const params: any = {
|
||||
applyId: props.applyId,
|
||||
selectMode: formData.value.selectMode,
|
||||
teacherNo: formData.value.teacherNo,
|
||||
teacherName: formData.value.teacherName,
|
||||
representorType: formData.value.representorType,
|
||||
};
|
||||
|
||||
if (formData.value.selectMode === SELECT_MODE.RANDOM) {
|
||||
params.candidates = candidates.value;
|
||||
}
|
||||
|
||||
await setReviewerSetting(params);
|
||||
useMessage().success('保存成功');
|
||||
emit('saved');
|
||||
await loadData();
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '保存失败');
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
if (!props.applyId) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getReviewerSetting(props.applyId);
|
||||
const data = res?.data || res;
|
||||
if (data) {
|
||||
currentRepresentor.value = {
|
||||
teacherNo: data.teacherNo || '',
|
||||
teacherName: data.teacherName || '',
|
||||
representorType: data.representorType || '',
|
||||
};
|
||||
formData.value.selectMode = data.selectMode || SELECT_MODE.DESIGNATED;
|
||||
formData.value.teacherNo = data.teacherNo || '';
|
||||
formData.value.teacherName = data.teacherName || '';
|
||||
formData.value.representorType = data.representorType || '';
|
||||
|
||||
if (data.candidateList && data.candidateList.length > 0) {
|
||||
candidates.value = data.candidateList;
|
||||
formData.value.candidates = data.candidateList;
|
||||
// 回显候选人
|
||||
candidateUserList.value = data.candidateList.map((c: any) => ({
|
||||
username: c.teacherNo,
|
||||
userName: c.teacherNo,
|
||||
name: c.teacherName,
|
||||
realName: c.teacherName,
|
||||
}));
|
||||
}
|
||||
|
||||
if (data.teacherNo && data.selectMode === SELECT_MODE.DESIGNATED) {
|
||||
selectedUserList.value = [
|
||||
{
|
||||
username: data.teacherNo,
|
||||
userName: data.teacherNo,
|
||||
name: data.teacherName,
|
||||
realName: data.teacherName,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (data.selectMode === SELECT_MODE.RANDOM && data.teacherNo) {
|
||||
selectedCandidate.value = {
|
||||
teacherNo: data.teacherNo,
|
||||
teacherName: data.teacherName || '',
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
// 忽略错误
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (rollInterval) {
|
||||
clearInterval(rollInterval);
|
||||
rollInterval = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.reviewer-setting {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.random-roller {
|
||||
padding: 12px 16px;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
min-height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.rolling {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--el-color-primary);
|
||||
animation: blink 0.1s infinite;
|
||||
}
|
||||
|
||||
.selected {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--el-color-success);
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,241 +0,0 @@
|
||||
<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 v-if="mode === 'audit'" label="审核状态" prop="docAuditStatus">
|
||||
<el-select v-model="state.queryForm.docAuditStatus" placeholder="请选择审核状态" clearable style="width: 200px">
|
||||
<el-option label="待上传" value="PENDING_UPLOAD" />
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="资产管理处审核中" value="ASSET_REVIEWING" />
|
||||
<el-option label="需求部门审核中" value="DEPT_REVIEWING" />
|
||||
<el-option label="内审部门审核中" value="AUDIT_REVIEWING" />
|
||||
<el-option label="资产管理处确认中" value="ASSET_CONFIRMING" />
|
||||
<el-option label="已完成" value="COMPLETED" />
|
||||
<el-option label="已退回" value="RETURNED" />
|
||||
</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">
|
||||
<component :is="titleIcon" />
|
||||
</el-icon>
|
||||
{{ pageTitle }}
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<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="140" show-overflow-tooltip />
|
||||
<el-table-column prop="projectName" label="项目名称" min-width="200" show-overflow-tooltip />
|
||||
<!-- 需求部门 - 仅审核模式显示 -->
|
||||
<el-table-column v-if="mode === 'audit'" prop="deptName" label="需求部门" min-width="150" show-overflow-tooltip />
|
||||
<!-- 预算金额 - 仅审核模式显示 -->
|
||||
<el-table-column v-if="mode === 'audit'" prop="budget" label="预算金额(元)" width="120" align="right">
|
||||
<template #default="scope">
|
||||
{{ scope.row.budget ? Number(scope.row.budget).toLocaleString() : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 文件状态 -->
|
||||
<el-table-column :prop="statusProp" label="文件状态" width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(getRowStatus(scope.row))">
|
||||
{{ getStatusLabel(getRowStatus(scope.row)) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 当前版本 - 仅审核模式显示 -->
|
||||
<el-table-column v-if="mode === 'audit'" prop="currentDocVersion" label="当前版本" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.currentDocVersion || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" align="center" fixed="right" width="120">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link icon="View" @click="handleProcess(scope.row)">
|
||||
{{ actionLabel }}
|
||||
</el-button>
|
||||
</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>
|
||||
|
||||
<!-- 处理弹窗 -->
|
||||
<!-- {{mode}}-->
|
||||
<DocProcessDialog ref="docProcessDialogRef" :mode="mode" @refresh="getDataList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PurchasingDocProcess">
|
||||
import { ref, reactive, computed, defineAsyncComponent, onMounted } from 'vue';
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { getDocProcessList } from '/@/api/purchase/docProcess';
|
||||
import { Search, DocumentCopy, DocumentChecked, List } from '@element-plus/icons-vue';
|
||||
import { useUserInfo } from '/@/stores/userInfo';
|
||||
|
||||
// 引入组件
|
||||
const DocProcessDialog = defineAsyncComponent(() => import('./DocProcessDialog.vue'));
|
||||
|
||||
const userInfoStore = useUserInfo();
|
||||
const docProcessDialogRef = ref();
|
||||
const searchFormRef = ref();
|
||||
const showSearch = ref(true);
|
||||
|
||||
// 从用户角色自动判断模式
|
||||
const mode = computed(() => {
|
||||
const roleCodes = userInfoStore.userInfos.roleCodes || [];
|
||||
// 有 PURCHASE_AGENT 角色则显示代理模式
|
||||
if (roleCodes.includes('PURCHASE_AGENT')) {
|
||||
return 'agent';
|
||||
}
|
||||
// 其他情况显示审核模式
|
||||
return 'audit';
|
||||
});
|
||||
|
||||
// 页面标题
|
||||
const pageTitle = computed(() => {
|
||||
return mode.value === 'agent' ? '招标代理工作台' : '招标文件审核';
|
||||
});
|
||||
|
||||
// 标题图标
|
||||
const titleIcon = computed(() => {
|
||||
return mode.value === 'agent' ? DocumentCopy : DocumentChecked;
|
||||
});
|
||||
|
||||
// 操作按钮标签
|
||||
const actionLabel = computed(() => {
|
||||
return mode.value === 'agent' ? '处理' : '审核';
|
||||
});
|
||||
|
||||
// 状态字段
|
||||
const statusProp = computed(() => {
|
||||
return mode.value === 'agent' ? 'status' : 'docAuditStatus';
|
||||
});
|
||||
|
||||
// 查询表单
|
||||
const getQueryForm = () => {
|
||||
const base: any = {
|
||||
purchaseNo: '',
|
||||
projectName: '',
|
||||
};
|
||||
if (mode.value === 'audit') {
|
||||
base.docAuditStatus = '';
|
||||
}
|
||||
return base;
|
||||
};
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
pageList: (params?: any) => getDocProcessList(mode.value, params),
|
||||
queryForm: getQueryForm(),
|
||||
createdIsNeed: true,
|
||||
});
|
||||
|
||||
const { getDataList, tableStyle, sizeChangeHandle, currentChangeHandle } = useTable(state);
|
||||
|
||||
const handleReset = () => {
|
||||
searchFormRef.value?.resetFields();
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 获取行状态(兼容两种字段名)
|
||||
const getRowStatus = (row: any) => {
|
||||
return mode.value === 'agent' ? row.status : row.docAuditStatus;
|
||||
};
|
||||
|
||||
const getStatusType = (status: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
PENDING_UPLOAD: 'info',
|
||||
DRAFT: 'info',
|
||||
ASSET_REVIEWING: 'warning',
|
||||
DEPT_REVIEWING: 'warning',
|
||||
AUDIT_REVIEWING: 'warning',
|
||||
ASSET_CONFIRMING: 'primary',
|
||||
COMPLETED: 'success',
|
||||
RETURNED: 'danger',
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
const getStatusLabel = (status: string) => {
|
||||
const labelMap: Record<string, string> = {
|
||||
PENDING_UPLOAD: '待上传',
|
||||
DRAFT: '草稿',
|
||||
ASSET_REVIEWING: '资产管理处审核中',
|
||||
DEPT_REVIEWING: '需求部门审核中',
|
||||
AUDIT_REVIEWING: '内审部门审核中',
|
||||
ASSET_CONFIRMING: '资产管理处确认中',
|
||||
COMPLETED: '已完成',
|
||||
RETURNED: '已退回',
|
||||
};
|
||||
return labelMap[status] || '-';
|
||||
};
|
||||
|
||||
const handleProcess = (row: any) => {
|
||||
docProcessDialogRef.value?.open(row);
|
||||
};
|
||||
|
||||
// 监听路由参数变化,重新加载数据
|
||||
onMounted(() => {
|
||||
// 重置查询表单
|
||||
state.queryForm = getQueryForm();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
@@ -89,6 +89,7 @@
|
||||
|
||||
<script setup lang="ts" name="PurchasingImplement">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { getObj, assignAgent, sendToAgent, revokeAgent, saveImplementType } from '/@/api/purchase/purchasingrequisition';
|
||||
import { getPage as getAgentPage } from '/@/api/purchase/purchaseagent';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
@@ -276,13 +277,30 @@ const handleAssignAgentDesignated = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 发送招标代理 */
|
||||
/** 发送招标代理 - 带确认弹窗 */
|
||||
const handleSendToAgent = async () => {
|
||||
const id = applyRow.value?.id ?? applyId.value;
|
||||
if (!id) {
|
||||
useMessage().warning('无法获取申请单ID');
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认弹窗
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'是否确认发送至招标代理启动招标文件审核流程?',
|
||||
'确认发送',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
);
|
||||
} catch {
|
||||
// 用户取消
|
||||
return;
|
||||
}
|
||||
|
||||
sendToAgentSubmitting.value = true;
|
||||
try {
|
||||
await sendToAgent(id);
|
||||
|
||||
Reference in New Issue
Block a user