更新
This commit is contained in:
@@ -252,6 +252,19 @@ export function saveRepresentor(id: number, representorTeacherNo?: string, repre
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机抽取部门参与人
|
||||||
|
* @param applyId 采购申请ID
|
||||||
|
* @param memberTeacherNos 参与随机抽取的人员工号列表(逗号分隔)
|
||||||
|
*/
|
||||||
|
export function randomSelectRepresentor(applyId: string, memberTeacherNos: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingapply/randomSelectRepresentor',
|
||||||
|
method: 'post',
|
||||||
|
data: { applyId, memberTeacherNos },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件归档:按文件类型打包下载该申请单下所有附件的下载地址(GET 请求,浏览器直接下载 zip)
|
* 文件归档:按文件类型打包下载该申请单下所有附件的下载地址(GET 请求,浏览器直接下载 zip)
|
||||||
* @param purchaseId 采购申请ID
|
* @param purchaseId 采购申请ID
|
||||||
|
|||||||
@@ -208,13 +208,109 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 部门参与人选择(部门负责人审核时显示) -->
|
||||||
|
<el-card v-if="showRepresentorSection" shadow="never" class="representor-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon><User /></el-icon>
|
||||||
|
部门参与人
|
||||||
|
</span>
|
||||||
|
<el-tag v-if="applyData.representorTeacherNo" type="success">已设置</el-tag>
|
||||||
|
<el-tag v-else type="warning">待设置</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form label-width="120px">
|
||||||
|
<el-form-item label="选择方式">
|
||||||
|
<el-radio-group v-model="representorSelectMode" :disabled="isViewMode">
|
||||||
|
<el-radio label="designate">指定一人</el-radio>
|
||||||
|
<el-radio label="random">随机抽取</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 指定一人 -->
|
||||||
|
<el-form-item v-if="representorSelectMode === 'designate'" label="选择参与人">
|
||||||
|
<el-select
|
||||||
|
v-model="selectedRepresentor"
|
||||||
|
placeholder="请选择参与人"
|
||||||
|
filterable
|
||||||
|
:disabled="isViewMode"
|
||||||
|
style="width: 300px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="member in deptMembers"
|
||||||
|
:key="member.teacherNo"
|
||||||
|
:label="`${member.realName} (${member.teacherNo})`"
|
||||||
|
:value="member.teacherNo"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 随机抽取 -->
|
||||||
|
<el-form-item v-if="representorSelectMode === 'random'" label="选择候选人">
|
||||||
|
<el-select
|
||||||
|
v-model="randomCandidates"
|
||||||
|
placeholder="请选择参与随机抽取的人员(可多选)"
|
||||||
|
multiple
|
||||||
|
filterable
|
||||||
|
:disabled="isViewMode"
|
||||||
|
style="width: 400px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="member in deptMembers"
|
||||||
|
:key="member.teacherNo"
|
||||||
|
:label="`${member.realName} (${member.teacherNo})`"
|
||||||
|
:value="member.teacherNo"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="randomSelectLoading"
|
||||||
|
:disabled="randomCandidates.length < 2 || isViewMode"
|
||||||
|
style="margin-left: 12px"
|
||||||
|
@click="handleRandomSelect"
|
||||||
|
>
|
||||||
|
随机抽取
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 已选中的参与人 -->
|
||||||
|
<el-form-item v-if="currentRepresentor" label="已选中参与人">
|
||||||
|
<el-tag type="success" size="large">
|
||||||
|
{{ currentRepresentor.realName }} ({{ currentRepresentor.teacherNo }})
|
||||||
|
</el-tag>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 参与人身份 -->
|
||||||
|
<el-form-item label="参与人身份" required>
|
||||||
|
<el-radio-group v-model="representorType" :disabled="isViewMode">
|
||||||
|
<el-radio label="purchase_rep">采购代表</el-radio>
|
||||||
|
<el-radio label="judge">评委</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 保存按钮 -->
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="saveRepresentorLoading"
|
||||||
|
:disabled="!canSaveRepresentor || isViewMode"
|
||||||
|
@click="handleSaveRepresentor"
|
||||||
|
>
|
||||||
|
保存参与人信息
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="BidFileAudit">
|
<script setup lang="ts" name="BidFileAudit">
|
||||||
import { ref, reactive, computed, onMounted, watch } from 'vue';
|
import { ref, reactive, computed, onMounted, watch } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { Document, FolderOpened, Upload, Link, Guide } from '@element-plus/icons-vue';
|
import { Document, FolderOpened, Upload, Link, Guide, User } from '@element-plus/icons-vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { Session } from '/@/utils/storage';
|
import { Session } from '/@/utils/storage';
|
||||||
import { useUserInfo } from '/@/stores/userInfo';
|
import { useUserInfo } from '/@/stores/userInfo';
|
||||||
@@ -232,6 +328,7 @@ import {
|
|||||||
} from '/@/api/purchase/bidfile';
|
} from '/@/api/purchase/bidfile';
|
||||||
import { getRequirementFiles } from '/@/api/purchase/purchasingfiles';
|
import { getRequirementFiles } from '/@/api/purchase/purchasingfiles';
|
||||||
import { currElTabIsSave } from '/@/api/order/order-key-vue';
|
import { currElTabIsSave } from '/@/api/order/order-key-vue';
|
||||||
|
import { getDeptMembers, saveRepresentor, randomSelectRepresentor } from '/@/api/purchase/purchasingrequisition';
|
||||||
|
|
||||||
// ==================== Props & Emits ====================
|
// ==================== Props & Emits ====================
|
||||||
|
|
||||||
@@ -252,7 +349,8 @@ const currentUserRoleCodes = computed(() => userStore.userInfos.roleCodes || [])
|
|||||||
const ROLE_LABEL_MAP: Record<string, string> = {
|
const ROLE_LABEL_MAP: Record<string, string> = {
|
||||||
PURCHASE_AGENT: '招标代理',
|
PURCHASE_AGENT: '招标代理',
|
||||||
PURCHASE_ASSET: '资产管理处',
|
PURCHASE_ASSET: '资产管理处',
|
||||||
PURCHASE_DEPT_APPLY: '部门负责人',
|
PURCHASE_DEPT_APPLY: '部门经办人',
|
||||||
|
PURCHASE_DEPT_AUDIT: '部门负责人',
|
||||||
PURCHASE_FILE_AUDIT: '内审部门',
|
PURCHASE_FILE_AUDIT: '内审部门',
|
||||||
PURCHASE_CENTER: '采购中心',
|
PURCHASE_CENTER: '采购中心',
|
||||||
};
|
};
|
||||||
@@ -295,6 +393,7 @@ const isViewMode = computed(() => {
|
|||||||
const isAgent = computed(() => currentUserRoleCodes.value.includes('PURCHASE_AGENT'));
|
const isAgent = computed(() => currentUserRoleCodes.value.includes('PURCHASE_AGENT'));
|
||||||
const isAsset = computed(() => currentUserRoleCodes.value.includes('PURCHASE_ASSET'));
|
const isAsset = computed(() => currentUserRoleCodes.value.includes('PURCHASE_ASSET'));
|
||||||
const isDeptApply = computed(() => currentUserRoleCodes.value.includes('PURCHASE_DEPT_APPLY'));
|
const isDeptApply = computed(() => currentUserRoleCodes.value.includes('PURCHASE_DEPT_APPLY'));
|
||||||
|
const isDeptAudit = computed(() => currentUserRoleCodes.value.includes('PURCHASE_DEPT_AUDIT'));
|
||||||
const isFileAudit = computed(() => currentUserRoleCodes.value.includes('PURCHASE_FILE_AUDIT'));
|
const isFileAudit = computed(() => currentUserRoleCodes.value.includes('PURCHASE_FILE_AUDIT'));
|
||||||
|
|
||||||
// 是否显示上传区域
|
// 是否显示上传区域
|
||||||
@@ -311,6 +410,12 @@ const showFlowTargetSection = computed(() => {
|
|||||||
return isAsset.value && isFlowEmbed.value;
|
return isAsset.value && isFlowEmbed.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 是否显示部门参与人选择区域(仅部门负责人在审核时显示)
|
||||||
|
const showRepresentorSection = computed(() => {
|
||||||
|
if (isViewMode.value) return false;
|
||||||
|
return isDeptAudit.value && isFlowEmbed.value;
|
||||||
|
});
|
||||||
|
|
||||||
// 采购申请数据
|
// 采购申请数据
|
||||||
const applyData = ref<any>({});
|
const applyData = ref<any>({});
|
||||||
const purchaseTypeLabel = computed(() => {
|
const purchaseTypeLabel = computed(() => {
|
||||||
@@ -351,6 +456,24 @@ const uploadRules = {
|
|||||||
// 流转去向(资产管理处审核时使用)
|
// 流转去向(资产管理处审核时使用)
|
||||||
const flowTarget = ref<string>('');
|
const flowTarget = ref<string>('');
|
||||||
|
|
||||||
|
// 部门参与人选择相关(部门负责人审核时使用)
|
||||||
|
const representorSelectMode = ref<'designate' | 'random'>('designate');
|
||||||
|
const deptMembers = ref<any[]>([]);
|
||||||
|
const deptMembersLoading = ref(false);
|
||||||
|
const selectedRepresentor = ref<string>('');
|
||||||
|
const randomCandidates = ref<string[]>([]);
|
||||||
|
const currentRepresentor = ref<any>(null);
|
||||||
|
const representorType = ref<string>('purchase_rep');
|
||||||
|
const saveRepresentorLoading = ref(false);
|
||||||
|
const randomSelectLoading = ref(false);
|
||||||
|
|
||||||
|
const canSaveRepresentor = computed(() => {
|
||||||
|
if (representorSelectMode.value === 'designate') {
|
||||||
|
return !!selectedRepresentor.value;
|
||||||
|
}
|
||||||
|
return !!currentRepresentor.value;
|
||||||
|
});
|
||||||
|
|
||||||
// ==================== 计算属性 ====================
|
// ==================== 计算属性 ====================
|
||||||
|
|
||||||
const BID_FILE_TYPE = '130';
|
const BID_FILE_TYPE = '130';
|
||||||
@@ -634,6 +757,93 @@ const registerFlowCallbacks = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== 部门参与人选择 ====================
|
||||||
|
|
||||||
|
const loadDeptMembers = async () => {
|
||||||
|
if (!showRepresentorSection.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
deptMembersLoading.value = true;
|
||||||
|
const res = await getDeptMembers();
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
deptMembers.value = res.data;
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.msg || '加载部门人员失败');
|
||||||
|
} finally {
|
||||||
|
deptMembersLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRandomSelect = async () => {
|
||||||
|
if (randomCandidates.value.length < 2) {
|
||||||
|
ElMessage.warning('请至少选择2名候选人');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
randomSelectLoading.value = true;
|
||||||
|
const res = await randomSelectRepresentor(
|
||||||
|
effectivePurchaseId.value,
|
||||||
|
randomCandidates.value.join(',')
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
currentRepresentor.value = res.data;
|
||||||
|
ElMessage.success(`随机抽取成功:${res.data.realName}`);
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || '随机抽取失败');
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.msg || '随机抽取失败');
|
||||||
|
} finally {
|
||||||
|
randomSelectLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveRepresentor = async () => {
|
||||||
|
if (!canSaveRepresentor.value) {
|
||||||
|
ElMessage.warning('请先选择参与人');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!representorType.value) {
|
||||||
|
ElMessage.warning('请选择参与人身份');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveRepresentorLoading.value = true;
|
||||||
|
let res: any;
|
||||||
|
|
||||||
|
if (representorSelectMode.value === 'designate') {
|
||||||
|
res = await saveRepresentor(
|
||||||
|
Number(effectivePurchaseId.value),
|
||||||
|
selectedRepresentor.value,
|
||||||
|
undefined,
|
||||||
|
representorType.value
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
res = await saveRepresentor(
|
||||||
|
Number(effectivePurchaseId.value),
|
||||||
|
currentRepresentor.value?.teacherNo,
|
||||||
|
randomCandidates.value.join(','),
|
||||||
|
representorType.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('保存参与人信息成功');
|
||||||
|
await loadApplyData();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || '保存失败');
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.msg || '保存失败');
|
||||||
|
} finally {
|
||||||
|
saveRepresentorLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ==================== 生命周期 ====================
|
// ==================== 生命周期 ====================
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -642,6 +852,9 @@ onMounted(async () => {
|
|||||||
if (isAgent.value) {
|
if (isAgent.value) {
|
||||||
await loadRequirementFiles();
|
await loadRequirementFiles();
|
||||||
}
|
}
|
||||||
|
if (showRepresentorSection.value) {
|
||||||
|
await loadDeptMembers();
|
||||||
|
}
|
||||||
registerFlowCallbacks();
|
registerFlowCallbacks();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -654,6 +867,9 @@ watch(
|
|||||||
if (isAgent.value) {
|
if (isAgent.value) {
|
||||||
await loadRequirementFiles();
|
await loadRequirementFiles();
|
||||||
}
|
}
|
||||||
|
if (showRepresentorSection.value) {
|
||||||
|
await loadDeptMembers();
|
||||||
|
}
|
||||||
registerFlowCallbacks();
|
registerFlowCallbacks();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user