更新采购申请
This commit is contained in:
@@ -86,7 +86,7 @@ export function assignAgent(applyId: number | string, mode: 'random' | 'designat
|
|||||||
return request({
|
return request({
|
||||||
url: '/purchase/purchasingapply/assign-agent',
|
url: '/purchase/purchasingapply/assign-agent',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: { id: Number(applyId), mode, agentId: agentId || undefined }
|
data: { id: String(applyId), mode, agentId: agentId || undefined }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ export function sendToAgent(applyId: number | string) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/purchase/purchasingapply/sendToAgent',
|
url: '/purchase/purchasingapply/sendToAgent',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: { id: Number(applyId) }
|
data: { id: String(applyId) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ export function revokeAgent(applyId: number | string) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/purchase/purchasingapply/revokeAgent',
|
url: '/purchase/purchasingapply/revokeAgent',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: { id: Number(applyId) }
|
data: { id: String(applyId) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,3 +334,16 @@ export function getDocList(applyId: number | string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件ID下载采购附件(返回blob)
|
||||||
|
* @param fileId 文件ID
|
||||||
|
*/
|
||||||
|
export function downloadFileById(fileId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingfiles/downloadById',
|
||||||
|
method: 'get',
|
||||||
|
params: { fileId },
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,14 +107,6 @@ export const staticRoutes: Array<RouteRecordRaw> = [
|
|||||||
isAuth: false, // 不需要认证,纯页面展示
|
isAuth: false, // 不需要认证,纯页面展示
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/purchase/purchasingrequisition/implement',
|
|
||||||
name: 'purchasingrequisition.implement',
|
|
||||||
component: () => import('/@/views/purchase/purchasingrequisition/implement.vue'),
|
|
||||||
meta: {
|
|
||||||
isAuth: false, // 供流程 iframe 嵌入
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...staticRoutesFlow
|
...staticRoutesFlow
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1819,7 +1819,7 @@ const handleSubmit = async () => {
|
|||||||
type: 'purchasingrequisition:submitSuccess'
|
type: 'purchasingrequisition:submitSuccess'
|
||||||
}, '*');
|
}, '*');
|
||||||
} else {
|
} else {
|
||||||
router.push('/finance/purchasingrequisition');
|
router.push('/purchase/purchasingrequisition');
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// 全局拦截器已经显示了错误提示,这里不需要再次显示
|
// 全局拦截器已经显示了错误提示,这里不需要再次显示
|
||||||
@@ -1891,7 +1891,7 @@ const handleTempStore = async () => {
|
|||||||
type: 'purchasingrequisition:submitSuccess'
|
type: 'purchasingrequisition:submitSuccess'
|
||||||
}, '*');
|
}, '*');
|
||||||
} else {
|
} else {
|
||||||
router.push('/finance/purchasingrequisition');
|
router.push('/purchase/purchasingrequisition');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { Session } from '/@/utils/storage'
|
import { Session } from '/@/utils/storage'
|
||||||
import { getAgentRequirementFiles, getAgentApplyDetail, uploadAgentDoc, reuploadAgentDoc, getDocList } from '/@/api/purchase/purchasingrequisition'
|
import { getAgentRequirementFiles, getAgentApplyDetail, uploadAgentDoc, reuploadAgentDoc, getDocList, downloadFileById } from '/@/api/purchase/purchasingrequisition'
|
||||||
import type { UploadInstance, UploadProps, UploadUserFile } from 'element-plus'
|
import type { UploadInstance, UploadProps, UploadUserFile } from 'element-plus'
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
@@ -159,10 +159,24 @@ const loadRequirementFiles = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownload = (row: any) => {
|
const handleDownload = async (row: any) => {
|
||||||
// TODO: 实现下载功能
|
try {
|
||||||
const downloadUrl = `/purchase/purchasingdoc/download/${row.id}`
|
const res = await downloadFileById(row.id)
|
||||||
window.open(downloadUrl, '_blank')
|
// 从响应头获取文件名,或使用原始文件名
|
||||||
|
const fileName = row.fileName || row.fileTitle || 'download'
|
||||||
|
// 创建 blob 并下载
|
||||||
|
const blob = new Blob([res])
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = fileName
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
} catch (e: any) {
|
||||||
|
useMessage().error(e?.msg || '下载失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const dialogTitle = computed(() => {
|
|||||||
|
|
||||||
const iframeSrc = computed(() => {
|
const iframeSrc = computed(() => {
|
||||||
const baseUrl = window.location.origin + window.location.pathname
|
const baseUrl = window.location.origin + window.location.pathname
|
||||||
let src = `${baseUrl}#/finance/purchasingrequisition/add`
|
let src = `${baseUrl}#/purchase/purchasingrequisition/add`
|
||||||
if (mode.value !== 'add' && rowId.value) {
|
if (mode.value !== 'add' && rowId.value) {
|
||||||
src += `?mode=${mode.value}&id=${rowId.value}`
|
src += `?mode=${mode.value}&id=${rowId.value}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,29 +82,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 底部按钮:编辑步骤一时隐藏 -->
|
|
||||||
<div v-if="!isEditingStep1" class="implement-footer">
|
|
||||||
<el-button @click="handleClose">取消</el-button>
|
|
||||||
<!-- 只有步骤一完成后才显示确定按钮 -->
|
|
||||||
<el-button v-if="step1Completed" type="primary" :loading="implementSubmitting" @click="handleImplementSubmit">确定</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="PurchasingImplement">
|
<script setup lang="ts" name="PurchasingImplement">
|
||||||
import { ref, computed, onMounted, watch, onUnmounted } from 'vue'
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { getObj, assignAgent, sendToAgent, revokeAgent, saveImplementType } from '/@/api/purchase/purchasingrequisition'
|
||||||
import { getDeptMembers, getObj, assignAgent, sendToAgent, revokeAgent, saveImplementType } from '/@/api/purchase/purchasingrequisition'
|
|
||||||
import { getPage as getAgentPage } from '/@/api/purchase/purchaseagent'
|
import { getPage as getAgentPage } from '/@/api/purchase/purchaseagent'
|
||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { Session } from '/@/utils/storage'
|
import { Session } from '/@/utils/storage'
|
||||||
import * as orderVue from '/@/api/order/order-key-vue'
|
|
||||||
|
|
||||||
// ==================== 常量定义(与后端枚举保持一致) ====================
|
// ==================== 常量定义(与后端枚举保持一致) ====================
|
||||||
|
|
||||||
/** 部门审核角色编码:仅该角色下显示采购代表相关页面和功能,流转至部门审核时需填写采购代表 */
|
|
||||||
const PURCHASE_DEPT_AUDIT_ROLE_CODE = 'PURCHASE_DEPT_AUDIT'
|
|
||||||
|
|
||||||
/** 实施采购方式(与后端 ImplementTypeEnum 一致) */
|
/** 实施采购方式(与后端 ImplementTypeEnum 一致) */
|
||||||
const IMPLEMENT_TYPE = {
|
const IMPLEMENT_TYPE = {
|
||||||
/** 自行组织采购 */
|
/** 自行组织采购 */
|
||||||
@@ -135,41 +124,16 @@ const YES_NO = {
|
|||||||
YES: '1',
|
YES: '1',
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
const roleCode = computed(() => Session.getRoleCode() || '')
|
const emit = defineEmits<{
|
||||||
const isDeptAuditRole = computed(() => roleCode.value === PURCHASE_DEPT_AUDIT_ROLE_CODE)
|
(e: 'close'): void
|
||||||
|
(e: 'saved'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
// 与编辑界面一致:支持流程 dynamic-link 传入 currJob/currElTab,申请单 ID 优先取 currJob.orderId
|
/** 申请单 ID */
|
||||||
const props = defineProps({
|
const applyId = ref<string | number | null>(null)
|
||||||
currJob: { type: Object, default: null },
|
|
||||||
currElTab: { type: Object, default: null }
|
|
||||||
})
|
|
||||||
const emit = defineEmits(['handleJob'])
|
|
||||||
|
|
||||||
/** 是否被流程 handle 页面通过 dynamic-link 嵌入 */
|
|
||||||
const isFlowEmbed = computed(() => !!props.currJob)
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
/** 申请单 ID(数值,用于 getObj 等):与 add 一致,优先流程 currJob.orderId,否则 route.query.id */
|
|
||||||
const applyId = computed(() => {
|
|
||||||
const raw = applyIdRaw.value
|
|
||||||
if (raw == null || raw === '') return null
|
|
||||||
const n = Number(raw)
|
|
||||||
return Number.isNaN(n) ? null : n
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 申请单 ID 原始字符串(用于 getApplyFiles 的 purchaseId,与编辑页一致,避免类型/精度问题) */
|
|
||||||
const applyIdRaw = computed(() => {
|
|
||||||
if (props.currJob?.orderId != null && props.currJob?.orderId !== '') {
|
|
||||||
return String(props.currJob.orderId)
|
|
||||||
}
|
|
||||||
const id = route.query.id
|
|
||||||
return id != null && id !== '' ? String(id) : ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const applyRow = ref<any>(null)
|
const applyRow = ref<any>(null)
|
||||||
const implementType = ref<string>(IMPLEMENT_TYPE.SELF_ORGANIZED)
|
const implementType = ref<string>(IMPLEMENT_TYPE.SELF_ORGANIZED)
|
||||||
const implementSubmitting = ref(false)
|
|
||||||
|
|
||||||
/** 步骤控制 */
|
/** 步骤控制 */
|
||||||
const step1Completed = ref(false)
|
const step1Completed = ref(false)
|
||||||
@@ -177,11 +141,6 @@ const saveTypeSubmitting = ref(false)
|
|||||||
const isEditingStep1 = ref(false)
|
const isEditingStep1 = ref(false)
|
||||||
const originalImplementType = ref<string>('')
|
const originalImplementType = ref<string>('')
|
||||||
|
|
||||||
const representorMode = ref<'single' | 'multi'>('single')
|
|
||||||
const representorTeacherNo = ref<string>('')
|
|
||||||
const representorsMulti = ref<string[]>([])
|
|
||||||
const deptMembers = ref<any[]>([])
|
|
||||||
|
|
||||||
/** 分配代理相关 */
|
/** 分配代理相关 */
|
||||||
const agentMode = ref<'designated' | 'random'>('designated')
|
const agentMode = ref<'designated' | 'random'>('designated')
|
||||||
const selectedAgentId = ref<string>('')
|
const selectedAgentId = ref<string>('')
|
||||||
@@ -194,19 +153,6 @@ const sendToAgentSubmitting = ref(false)
|
|||||||
const revokeAgentSubmitting = ref(false)
|
const revokeAgentSubmitting = ref(false)
|
||||||
let rollInterval: ReturnType<typeof setInterval> | null = null
|
let rollInterval: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
/** 是否可以分配代理:委托代理采购 且 (学校统一采购 或 部门自行采购且委托采购中心采购) 且 步骤一已完成 */
|
|
||||||
const canAssignAgent = computed(() => {
|
|
||||||
// 自行组织采购不需要分配代理
|
|
||||||
if (implementType.value !== IMPLEMENT_TYPE.ENTRUST_AGENT) return false
|
|
||||||
// 步骤一必须完成
|
|
||||||
if (!step1Completed.value) return false
|
|
||||||
const row = applyRow.value
|
|
||||||
if (!row) return false
|
|
||||||
// 学校统一采购 或 部门自行采购且委托采购中心采购
|
|
||||||
return row.purchaseMode === PURCHASE_MODE.SCHOOL_UNIFIED
|
|
||||||
|| (row.purchaseMode === PURCHASE_MODE.DEPT_SELF && row.purchaseChannel === PURCHASE_CHANNEL.ENTRUST_CENTER)
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 是否显示步骤二:委托代理采购 且 步骤一已完成 且 不在编辑步骤一 */
|
/** 是否显示步骤二:委托代理采购 且 步骤一已完成 且 不在编辑步骤一 */
|
||||||
const showStep2 = computed(() => {
|
const showStep2 = computed(() => {
|
||||||
return step1Completed.value && !isEditingStep1.value && implementType.value === IMPLEMENT_TYPE.ENTRUST_AGENT
|
return step1Completed.value && !isEditingStep1.value && implementType.value === IMPLEMENT_TYPE.ENTRUST_AGENT
|
||||||
@@ -219,11 +165,9 @@ const step2Completed = computed(() => {
|
|||||||
|
|
||||||
/** 是否可以发送招标代理:委托代理采购 且 已分配代理 且 未发送 */
|
/** 是否可以发送招标代理:委托代理采购 且 已分配代理 且 未发送 */
|
||||||
const canSendToAgent = computed(() => {
|
const canSendToAgent = computed(() => {
|
||||||
// 自行组织采购不需要发送
|
|
||||||
if (implementType.value !== IMPLEMENT_TYPE.ENTRUST_AGENT) return false
|
if (implementType.value !== IMPLEMENT_TYPE.ENTRUST_AGENT) return false
|
||||||
const row = applyRow.value
|
const row = applyRow.value
|
||||||
if (!row) return false
|
if (!row) return false
|
||||||
// 已分配代理 且 未发送
|
|
||||||
return !!row.agentId && row.agentSent !== YES_NO.YES
|
return !!row.agentId && row.agentSent !== YES_NO.YES
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -231,7 +175,6 @@ const canSendToAgent = computed(() => {
|
|||||||
const canReassignAgent = computed(() => {
|
const canReassignAgent = computed(() => {
|
||||||
const row = applyRow.value
|
const row = applyRow.value
|
||||||
if (!row) return false
|
if (!row) return false
|
||||||
// 未发送招标代理时可重新分配
|
|
||||||
return row.agentSent !== YES_NO.YES
|
return row.agentSent !== YES_NO.YES
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -239,7 +182,6 @@ const canReassignAgent = computed(() => {
|
|||||||
const canRevokeAgent = computed(() => {
|
const canRevokeAgent = computed(() => {
|
||||||
const row = applyRow.value
|
const row = applyRow.value
|
||||||
if (!row) return false
|
if (!row) return false
|
||||||
// 已分配代理 且 已发送招标代理时可撤回
|
|
||||||
return !!row.agentId && row.agentSent === YES_NO.YES
|
return !!row.agentId && row.agentSent === YES_NO.YES
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -260,7 +202,6 @@ const loadAgentList = async () => {
|
|||||||
const startRollingAnimation = (finalAgentName: string) => {
|
const startRollingAnimation = (finalAgentName: string) => {
|
||||||
if (agentList.value.length === 0) return
|
if (agentList.value.length === 0) return
|
||||||
|
|
||||||
// 清除之前的动画
|
|
||||||
if (rollInterval) {
|
if (rollInterval) {
|
||||||
clearInterval(rollInterval)
|
clearInterval(rollInterval)
|
||||||
rollInterval = null
|
rollInterval = null
|
||||||
@@ -270,15 +211,14 @@ const startRollingAnimation = (finalAgentName: string) => {
|
|||||||
assignedAgentName.value = ''
|
assignedAgentName.value = ''
|
||||||
|
|
||||||
let currentIndex = 0
|
let currentIndex = 0
|
||||||
const totalDuration = 2000 // 总动画时间 2秒
|
const totalDuration = 2000
|
||||||
const intervalTime = 80 // 滚动间隔
|
const intervalTime = 80
|
||||||
|
|
||||||
rollInterval = setInterval(() => {
|
rollInterval = setInterval(() => {
|
||||||
rollingAgentName.value = agentList.value[currentIndex].agentName
|
rollingAgentName.value = agentList.value[currentIndex].agentName
|
||||||
currentIndex = (currentIndex + 1) % agentList.value.length
|
currentIndex = (currentIndex + 1) % agentList.value.length
|
||||||
}, intervalTime)
|
}, intervalTime)
|
||||||
|
|
||||||
// 2秒后停止并显示最终结果
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (rollInterval) {
|
if (rollInterval) {
|
||||||
clearInterval(rollInterval)
|
clearInterval(rollInterval)
|
||||||
@@ -297,8 +237,7 @@ const handleAssignAgentRandom = async () => {
|
|||||||
}
|
}
|
||||||
assignAgentSubmitting.value = true
|
assignAgentSubmitting.value = true
|
||||||
try {
|
try {
|
||||||
const res = await assignAgent(Number(id), 'random')
|
const res = await assignAgent(id, 'random')
|
||||||
// 后端返回分配结果后,展示滚动动画
|
|
||||||
const finalAgentName = res?.data?.agentName || res?.agentName || ''
|
const finalAgentName = res?.data?.agentName || res?.agentName || ''
|
||||||
if (finalAgentName) {
|
if (finalAgentName) {
|
||||||
startRollingAnimation(finalAgentName)
|
startRollingAnimation(finalAgentName)
|
||||||
@@ -325,7 +264,7 @@ const handleAssignAgentDesignated = async () => {
|
|||||||
}
|
}
|
||||||
assignAgentSubmitting.value = true
|
assignAgentSubmitting.value = true
|
||||||
try {
|
try {
|
||||||
await assignAgent(Number(id), 'designated', selectedAgentId.value)
|
await assignAgent(id, 'designated', selectedAgentId.value)
|
||||||
useMessage().success('指定代理成功')
|
useMessage().success('指定代理成功')
|
||||||
await loadData()
|
await loadData()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -344,7 +283,7 @@ const handleSendToAgent = async () => {
|
|||||||
}
|
}
|
||||||
sendToAgentSubmitting.value = true
|
sendToAgentSubmitting.value = true
|
||||||
try {
|
try {
|
||||||
await sendToAgent(Number(id))
|
await sendToAgent(id)
|
||||||
useMessage().success('已发送招标代理')
|
useMessage().success('已发送招标代理')
|
||||||
await loadData()
|
await loadData()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -363,7 +302,7 @@ const handleRevokeAgent = async () => {
|
|||||||
}
|
}
|
||||||
revokeAgentSubmitting.value = true
|
revokeAgentSubmitting.value = true
|
||||||
try {
|
try {
|
||||||
await revokeAgent(Number(id))
|
await revokeAgent(id)
|
||||||
useMessage().success('已撤回招标代理')
|
useMessage().success('已撤回招标代理')
|
||||||
await loadData()
|
await loadData()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -375,6 +314,7 @@ const handleRevokeAgent = async () => {
|
|||||||
|
|
||||||
/** 步骤一:保存实施采购方式 */
|
/** 步骤一:保存实施采购方式 */
|
||||||
const handleSaveImplementType = async () => {
|
const handleSaveImplementType = async () => {
|
||||||
|
|
||||||
const id = applyRow.value?.id ?? applyId.value
|
const id = applyRow.value?.id ?? applyId.value
|
||||||
if (!id) {
|
if (!id) {
|
||||||
useMessage().warning('无法获取申请单ID')
|
useMessage().warning('无法获取申请单ID')
|
||||||
@@ -386,14 +326,11 @@ const handleSaveImplementType = async () => {
|
|||||||
}
|
}
|
||||||
saveTypeSubmitting.value = true
|
saveTypeSubmitting.value = true
|
||||||
try {
|
try {
|
||||||
await saveImplementType(Number(id), implementType.value)
|
await saveImplementType(id, implementType.value)
|
||||||
useMessage().success('保存成功')
|
useMessage().success('保存成功')
|
||||||
step1Completed.value = true
|
step1Completed.value = true
|
||||||
originalImplementType.value = implementType.value
|
originalImplementType.value = implementType.value
|
||||||
// 流程嵌入场景:通知流程 Tab 已保存
|
emit('saved')
|
||||||
if (isFlowEmbed.value && props.currJob && props.currElTab?.id) {
|
|
||||||
orderVue.currElTabIsSave(props.currJob, props.currElTab.id, true, emit)
|
|
||||||
}
|
|
||||||
// 如果是委托代理采购,加载代理列表
|
// 如果是委托代理采购,加载代理列表
|
||||||
if (implementType.value === IMPLEMENT_TYPE.ENTRUST_AGENT) {
|
if (implementType.value === IMPLEMENT_TYPE.ENTRUST_AGENT) {
|
||||||
await loadAgentList()
|
await loadAgentList()
|
||||||
@@ -430,10 +367,11 @@ const handleReSaveImplementType = async () => {
|
|||||||
}
|
}
|
||||||
saveTypeSubmitting.value = true
|
saveTypeSubmitting.value = true
|
||||||
try {
|
try {
|
||||||
await saveImplementType(Number(id), implementType.value)
|
await saveImplementType(id, implementType.value)
|
||||||
useMessage().success('修改成功')
|
useMessage().success('修改成功')
|
||||||
isEditingStep1.value = false
|
isEditingStep1.value = false
|
||||||
originalImplementType.value = implementType.value
|
originalImplementType.value = implementType.value
|
||||||
|
emit('saved')
|
||||||
// 如果从委托代理采购改为自行组织采购,清空代理相关状态
|
// 如果从委托代理采购改为自行组织采购,清空代理相关状态
|
||||||
if (implementType.value === IMPLEMENT_TYPE.SELF_ORGANIZED) {
|
if (implementType.value === IMPLEMENT_TYPE.SELF_ORGANIZED) {
|
||||||
assignedAgentName.value = ''
|
assignedAgentName.value = ''
|
||||||
@@ -443,10 +381,6 @@ const handleReSaveImplementType = async () => {
|
|||||||
if (implementType.value === IMPLEMENT_TYPE.ENTRUST_AGENT) {
|
if (implementType.value === IMPLEMENT_TYPE.ENTRUST_AGENT) {
|
||||||
await loadAgentList()
|
await loadAgentList()
|
||||||
}
|
}
|
||||||
// 流程嵌入场景:通知流程 Tab 已保存
|
|
||||||
if (isFlowEmbed.value && props.currJob && props.currElTab?.id) {
|
|
||||||
orderVue.currElTabIsSave(props.currJob, props.currElTab.id, true, emit)
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
useMessage().error(e?.msg || '修改失败')
|
useMessage().error(e?.msg || '修改失败')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -454,60 +388,30 @@ const handleReSaveImplementType = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInIframe = () => typeof window !== 'undefined' && window.self !== window.top
|
|
||||||
|
|
||||||
const postMessage = (type: string, payload?: any) => {
|
|
||||||
if (typeof window !== 'undefined' && window.parent) {
|
|
||||||
window.parent.postMessage({ type, ...payload }, '*')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
const id = applyId.value
|
const id = applyId.value
|
||||||
if (!id) {
|
if (!id) {
|
||||||
useMessage().warning('缺少申请单ID')
|
useMessage().warning('缺少申请单ID')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const needDeptMembers = isDeptAuditRole.value
|
|
||||||
try {
|
try {
|
||||||
const requests: [ReturnType<typeof getObj>, ReturnType<typeof getDeptMembers>?] = [getObj(id)]
|
const detailRes = await getObj(id)
|
||||||
if (needDeptMembers) requests.push(getDeptMembers())
|
|
||||||
const results = await Promise.all(requests)
|
|
||||||
const detailRes = results[0]
|
|
||||||
const membersRes = needDeptMembers ? results[1] : null
|
|
||||||
|
|
||||||
applyRow.value = detailRes?.data ? { ...detailRes.data, id: detailRes.data.id ?? id } : { id }
|
applyRow.value = detailRes?.data ? { ...detailRes.data, id: detailRes.data.id ?? id } : { id }
|
||||||
|
|
||||||
const row = applyRow.value
|
const row = applyRow.value
|
||||||
if (row?.implementType) {
|
if (row?.implementType) {
|
||||||
implementType.value = row.implementType
|
implementType.value = row.implementType
|
||||||
originalImplementType.value = row.implementType
|
originalImplementType.value = row.implementType
|
||||||
// 已有 implementType 说明步骤一已完成
|
|
||||||
step1Completed.value = true
|
step1Completed.value = true
|
||||||
}
|
}
|
||||||
// 回显需求部门初审-采购代表人方式与人员
|
|
||||||
if (row?.representorTeacherNo) {
|
|
||||||
representorMode.value = 'single'
|
|
||||||
representorTeacherNo.value = row.representorTeacherNo ?? ''
|
|
||||||
representorsMulti.value = []
|
|
||||||
} else if (row?.representors) {
|
|
||||||
representorMode.value = 'multi'
|
|
||||||
representorTeacherNo.value = ''
|
|
||||||
const parts = typeof row.representors === 'string' ? row.representors.split(',') : []
|
|
||||||
representorsMulti.value = parts.map((s: string) => s.trim()).filter(Boolean)
|
|
||||||
} else {
|
|
||||||
representorTeacherNo.value = ''
|
|
||||||
representorsMulti.value = []
|
|
||||||
}
|
|
||||||
deptMembers.value = needDeptMembers && membersRes?.data ? membersRes.data : []
|
|
||||||
|
|
||||||
// 加载代理列表并回显已分配代理(委托代理采购时)
|
// 加载代理列表并回显已分配代理(委托代理采购时)
|
||||||
if (step1Completed.value && implementType.value === IMPLEMENT_TYPE.ENTRUST_AGENT) {
|
if (step1Completed.value && implementType.value === IMPLEMENT_TYPE.ENTRUST_AGENT) {
|
||||||
// 先判断是否可以分配代理(学校统一采购 或 部门自行采购且委托采购中心采购)
|
|
||||||
const canLoadAgent = row.purchaseMode === PURCHASE_MODE.SCHOOL_UNIFIED
|
const canLoadAgent = row.purchaseMode === PURCHASE_MODE.SCHOOL_UNIFIED
|
||||||
|| (row.purchaseMode === PURCHASE_MODE.DEPT_SELF && row.purchaseChannel === PURCHASE_CHANNEL.ENTRUST_CENTER)
|
|| (row.purchaseMode === PURCHASE_MODE.DEPT_SELF && row.purchaseChannel === PURCHASE_CHANNEL.ENTRUST_CENTER)
|
||||||
if (canLoadAgent) {
|
if (canLoadAgent) {
|
||||||
await loadAgentList()
|
await loadAgentList()
|
||||||
// 回显已分配代理
|
|
||||||
if (row?.agentId) {
|
if (row?.agentId) {
|
||||||
selectedAgentId.value = row.agentId
|
selectedAgentId.value = row.agentId
|
||||||
assignedAgentName.value = row.agentName || ''
|
assignedAgentName.value = row.agentName || ''
|
||||||
@@ -515,19 +419,28 @@ const loadData = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
applyRow.value = { id }
|
console.log(_)
|
||||||
deptMembers.value = []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
/** 打开弹窗 */
|
||||||
postMessage('purchasingimplement:close')
|
const open = async (row: { id: string | number }) => {
|
||||||
if (!isInIframe()) {
|
applyId.value = row?.id ?? null
|
||||||
window.history.back()
|
applyRow.value = null
|
||||||
}
|
implementType.value = IMPLEMENT_TYPE.SELF_ORGANIZED
|
||||||
|
step1Completed.value = false
|
||||||
|
isEditingStep1.value = false
|
||||||
|
agentMode.value = 'designated'
|
||||||
|
selectedAgentId.value = ''
|
||||||
|
agentList.value = []
|
||||||
|
assignedAgentName.value = ''
|
||||||
|
rollingAgentName.value = ''
|
||||||
|
|
||||||
|
await loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleImplementSubmit = async () => {
|
/** 确定 */
|
||||||
|
const handleConfirm = async () => {
|
||||||
const row = applyRow.value
|
const row = applyRow.value
|
||||||
if (!row?.id && !applyId.value) return
|
if (!row?.id && !applyId.value) return
|
||||||
const id = row?.id ?? applyId.value
|
const id = row?.id ?? applyId.value
|
||||||
@@ -539,11 +452,11 @@ const handleImplementSubmit = async () => {
|
|||||||
useMessage().warning('请选择实施采购方式')
|
useMessage().warning('请选择实施采购方式')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 自动保存步骤一
|
|
||||||
saveTypeSubmitting.value = true
|
saveTypeSubmitting.value = true
|
||||||
try {
|
try {
|
||||||
await saveImplementType(Number(id), implementType.value)
|
await saveImplementType(id, implementType.value)
|
||||||
step1Completed.value = true
|
step1Completed.value = true
|
||||||
|
emit('saved')
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
useMessage().error(e?.msg || '保存实施采购方式失败')
|
useMessage().error(e?.msg || '保存实施采购方式失败')
|
||||||
return
|
return
|
||||||
@@ -552,61 +465,26 @@ const handleImplementSubmit = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implementSubmitting.value = true
|
|
||||||
try {
|
|
||||||
useMessage().success('实施采购已保存')
|
useMessage().success('实施采购已保存')
|
||||||
postMessage('purchasingimplement:saved')
|
emit('close')
|
||||||
// 流程嵌入场景:通知流程当前 Tab 已保存
|
|
||||||
if (isFlowEmbed.value && props.currJob && props.currElTab?.id) {
|
|
||||||
orderVue.currElTabIsSave(props.currJob, props.currElTab.id, true, emit)
|
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
|
||||||
useMessage().error(err?.msg || '实施采购失败')
|
|
||||||
} finally {
|
|
||||||
implementSubmitting.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 流程嵌入时供 handle.vue 调用的"保存"回调:与页面按钮保存逻辑保持一致 */
|
|
||||||
async function flowSubmitForm() {
|
|
||||||
await handleImplementSubmit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 流程切换工单时重新加载数据(与 add 编辑页一致)
|
|
||||||
watch(
|
|
||||||
() => props.currJob?.orderId ?? props.currJob?.id,
|
|
||||||
(newVal, oldVal) => {
|
|
||||||
if (newVal !== oldVal && applyId.value) {
|
|
||||||
loadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await loadData()
|
|
||||||
if (isInIframe()) {
|
|
||||||
document.documentElement.classList.add('iframe-mode')
|
|
||||||
document.body.classList.add('iframe-mode')
|
|
||||||
}
|
|
||||||
// 流程嵌入:注册 tab 保存回调,供审批页调用(与采购申请编辑页保持一致)
|
|
||||||
if (isFlowEmbed.value && props.currJob && props.currElTab?.id) {
|
|
||||||
orderVue.currElTabIsExist(props.currJob, props.currElTab.id)
|
|
||||||
await orderVue.currElTabIsView({}, props.currJob, props.currElTab.id, flowSubmitForm)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// 清理滚动动画定时器
|
|
||||||
if (rollInterval) {
|
if (rollInterval) {
|
||||||
clearInterval(rollInterval)
|
clearInterval(rollInterval)
|
||||||
rollInterval = null
|
rollInterval = null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
open,
|
||||||
|
handleConfirm,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.implement-page {
|
.implement-page {
|
||||||
padding: 20px;
|
padding: 0;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -617,18 +495,6 @@ onUnmounted(() => {
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.implement-form-tip {
|
|
||||||
margin-top: 12px;
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
.implement-footer {
|
|
||||||
margin-top: 24px;
|
|
||||||
padding-top: 16px;
|
|
||||||
border-top: 1px solid var(--el-border-color-lighter);
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-section {
|
.step-section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
|||||||
@@ -2,66 +2,73 @@
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="visible"
|
v-model="visible"
|
||||||
title="实施采购"
|
title="实施采购"
|
||||||
width="50%"
|
width="800px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
class="implement-iframe-dialog"
|
|
||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
>
|
>
|
||||||
<div class="implement-iframe-content">
|
<ImplementContent
|
||||||
<iframe
|
ref="implementContentRef"
|
||||||
ref="iframeRef"
|
@close="handleContentClose"
|
||||||
:src="iframeSrc"
|
@saved="handleContentSaved"
|
||||||
frameborder="0"
|
|
||||||
class="implement-iframe"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="confirming" @click="handleConfirm">确定</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ImplementForm">
|
<script setup lang="ts" name="ImplementForm">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, nextTick } from 'vue'
|
||||||
|
import ImplementContent from './implement.vue'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'refresh'): void
|
(e: 'refresh'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const iframeRef = ref<HTMLIFrameElement>()
|
const implementContentRef = ref<InstanceType<typeof ImplementContent>>()
|
||||||
const applyId = ref<string | number>('')
|
const confirming = ref(false)
|
||||||
|
const pendingRow = ref<{ id: string | number } | null>(null)
|
||||||
const iframeSrc = computed(() => {
|
|
||||||
const baseUrl = window.location.origin + window.location.pathname
|
|
||||||
return `${baseUrl}#/purchase/purchasingrequisition/implement?id=${applyId.value}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
visible.value = false
|
visible.value = false
|
||||||
window.removeEventListener('message', handleMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMessage = (event: MessageEvent) => {
|
const handleContentClose = () => {
|
||||||
if (event.data?.type === 'purchasingimplement:submitSuccess') {
|
visible.value = false
|
||||||
handleClose()
|
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
} else if (event.data?.type === 'purchasingimplement:close') {
|
}
|
||||||
handleClose()
|
|
||||||
} else if (event.data?.type === 'purchasingimplement:saved') {
|
const handleContentSaved = () => {
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleConfirm = async () => {
|
||||||
|
confirming.value = true
|
||||||
|
try {
|
||||||
|
await implementContentRef.value?.handleConfirm()
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} finally {
|
||||||
|
confirming.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const openDialog = (row: { id: string | number }) => {
|
const openDialog = async (row: { id: string | number }) => {
|
||||||
applyId.value = row?.id ?? ''
|
pendingRow.value = row
|
||||||
visible.value = true
|
visible.value = true
|
||||||
window.addEventListener('message', handleMessage)
|
// 等待 dialog 及内部组件挂载完成后再调用 open
|
||||||
|
await nextTick()
|
||||||
|
if (pendingRow.value) {
|
||||||
|
implementContentRef.value?.open(pendingRow.value)
|
||||||
|
pendingRow.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(visible, (val) => {
|
|
||||||
if (!val) {
|
|
||||||
window.removeEventListener('message', handleMessage)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
openDialog,
|
openDialog,
|
||||||
@@ -69,27 +76,9 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.implement-iframe-content {
|
.dialog-footer {
|
||||||
width: 100%;
|
display: flex;
|
||||||
height: 65vh;
|
justify-content: flex-end;
|
||||||
min-height: 480px;
|
gap: 12px;
|
||||||
max-height: calc(100vh - 180px);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.implement-iframe {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 480px;
|
|
||||||
border: none;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.implement-iframe-dialog .el-dialog__body {
|
|
||||||
padding: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user