更新履约评价
This commit is contained in:
@@ -67,6 +67,73 @@ export function putObj(obj?: Object) {
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 履约验收流程接口 ==========
|
||||
|
||||
/**
|
||||
* 第一步:保存履约验收公共配置,按分期次数自动生成批次
|
||||
*/
|
||||
export function saveCommonConfig(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/saveCommonConfig',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取履约验收公共配置及批次列表
|
||||
*/
|
||||
export function getCommonConfigWithBatches(purchaseId: string) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/commonConfigWithBatches',
|
||||
method: 'get',
|
||||
params: { purchaseId }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 第二步:更新单个批次
|
||||
*/
|
||||
export function updateBatch(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/updateBatch',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验收详情(含验收内容、验收小组)
|
||||
*/
|
||||
export function getDetail(purchaseId: string, batch?: number) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/detail',
|
||||
method: 'get',
|
||||
params: { purchaseId, batch }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许填报方式(金额<30万)
|
||||
*/
|
||||
export function canFillForm(purchaseId: string) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/canFillForm',
|
||||
method: 'get',
|
||||
params: { purchaseId }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据品目类型获取验收项配置
|
||||
*/
|
||||
export function getAcceptanceItems(acceptanceType: string) {
|
||||
return request({
|
||||
url: `/purchase/acceptanceItemConfig/listByType/${acceptanceType}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 工具函数 ==========
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="160px" class="accept-batch-form">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="验收方式" prop="acceptType">
|
||||
<el-radio-group v-model="form.acceptType" :disabled="readonly">
|
||||
<el-radio label="1">填写履约验收评价表</el-radio>
|
||||
<el-radio label="2" :disabled="!canFill">上传履约验收评价表</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="!canFill" class="el-form-item__tip">金额≥30万,仅支持上传模版</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="验收日期" prop="acceptDate">
|
||||
<el-date-picker
|
||||
v-model="form.acceptDate"
|
||||
type="date"
|
||||
placeholder="请选择"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 填报方式:验收内容表格 -->
|
||||
<template v-if="form.acceptType === '1' && canFill">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="验收内容" prop="acceptContents">
|
||||
<el-table :data="form.acceptContents" border size="small" max-height="260" class="accept-content-table">
|
||||
<el-table-column prop="itemName" label="验收项" >
|
||||
<template #default="{ row }">
|
||||
{{row.itemName}}
|
||||
<el-input
|
||||
v-if="row.type === 'input'"
|
||||
v-model="row.remark"
|
||||
placeholder="请输入"
|
||||
size="small"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="isQualified" label="合格/不合格" width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-radio-group v-model="row.isQualified" size="small" :disabled="readonly">
|
||||
<el-radio label="1">合格</el-radio>
|
||||
<el-radio label="0">不合格</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<!-- 上传方式 -->
|
||||
<template v-if="form.acceptType === '2'">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="履约验收模版" prop="templateFileIds">
|
||||
<UploadFile
|
||||
v-model="templateFileIdsStr"
|
||||
:limit="1"
|
||||
:data="{ purchaseId: purchaseId || '', fileType: '110' }"
|
||||
upload-file-url="/purchase/purchasingfiles/upload"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<!-- 验收小组 -->
|
||||
<el-col :span="24">
|
||||
<el-form-item label="验收小组" prop="acceptTeam">
|
||||
<div class="team-list">
|
||||
<div v-for="(m, idx) in form.acceptTeam" :key="idx" class="team-row">
|
||||
<el-input v-model="m.name" placeholder="姓名" size="small" style="width:120px" :disabled="readonly" />
|
||||
<el-input v-model="m.deptName" placeholder="部门" size="small" style="width:160px" :disabled="readonly" />
|
||||
<el-button v-if="!readonly && form.acceptTeam.length > 3" type="danger" link size="small" @click="removeTeam(idx)">删除</el-button>
|
||||
</div>
|
||||
<el-button v-if="!readonly" type="primary" link size="small" @click="addTeam">+ 增加成员</el-button>
|
||||
</div>
|
||||
<div class="el-form-item__tip">至少3人,且为单数</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入" :disabled="readonly" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: Record<string, any>
|
||||
canFill: boolean
|
||||
readonly?: boolean
|
||||
purchaseId?: string
|
||||
acceptanceItems?: any[]
|
||||
}>(),
|
||||
{ readonly: false, canFill: true, purchaseId: '' }
|
||||
)
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const templateFileIdsStr = ref('')
|
||||
|
||||
const form = reactive({
|
||||
acceptType: '1',
|
||||
acceptDate: '',
|
||||
acceptContents: [] as any[],
|
||||
acceptTeam: [
|
||||
{ name: '', deptCode: '', deptName: '' },
|
||||
{ name: '', deptCode: '', deptName: '' },
|
||||
{ name: '', deptCode: '', deptName: '' },
|
||||
] as any[],
|
||||
templateFileIds: [] as string[],
|
||||
remark: '',
|
||||
...props.modelValue,
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (val) => Object.assign(form, val || {}), { deep: true })
|
||||
watch(form, () => emit('update:modelValue', { ...form }), { deep: true })
|
||||
|
||||
watch(() => props.acceptanceItems, (items) => {
|
||||
if (items?.length && form.acceptContents.length === 0) {
|
||||
form.acceptContents = items.map((it: any) => ({
|
||||
configId: it.id,
|
||||
itemName: it.itemName,
|
||||
type: it.type,
|
||||
isQualified: '1',
|
||||
remark: '',
|
||||
}))
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(templateFileIdsStr, (s) => {
|
||||
const arr = s ? s.split(',').map((x: string) => x.trim()).filter(Boolean) : []
|
||||
if (JSON.stringify(form.templateFileIds) !== JSON.stringify(arr)) {
|
||||
form.templateFileIds = arr
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => form.templateFileIds, (arr) => {
|
||||
if (Array.isArray(arr) && arr.length) templateFileIdsStr.value = arr.join(',')
|
||||
}, { immediate: true, deep: true })
|
||||
|
||||
const addTeam = () => {
|
||||
form.acceptTeam.push({ name: '', deptCode: '', deptName: '' })
|
||||
}
|
||||
|
||||
const removeTeam = (idx: number) => {
|
||||
form.acceptTeam.splice(idx, 1)
|
||||
}
|
||||
|
||||
const rules: FormRules = {
|
||||
acceptType: [{ required: true, message: '请选择验收方式', trigger: 'change' }],
|
||||
acceptDate: [{ required: true, message: '请选择验收日期', trigger: 'change' }],
|
||||
}
|
||||
|
||||
const validate = () => formRef.value?.validate()
|
||||
|
||||
defineExpose({ validate, form })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.accept-batch-form :deep(.el-form-item) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.accept-content-table :deep(.el-table__body td) {
|
||||
padding: 10px 0;
|
||||
width: 200px;
|
||||
}
|
||||
.team-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.team-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.el-form-item__tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px" class="accept-common-form compact-form">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="项目名称">
|
||||
<el-input :model-value="projectName || form.projectName" readonly placeholder="-" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="需求部门">
|
||||
<el-input :model-value="deptName || form.deptName" readonly placeholder="-" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="是否签订合同" prop="hasContract">
|
||||
<el-radio-group v-model="form.hasContract">
|
||||
<el-radio label="0">否</el-radio>
|
||||
<el-radio label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="form.hasContract === '1'">
|
||||
<el-form-item label="合同" prop="contractId">
|
||||
<el-input v-model="form.contractId" placeholder="请选择合同(待对接合同接口)" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="是否分期验收" prop="isInstallment">
|
||||
<el-radio-group v-model="form.isInstallment">
|
||||
<el-radio label="0">否</el-radio>
|
||||
<el-radio label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="form.isInstallment === '1'">
|
||||
<el-form-item label="分期次数" prop="totalPhases">
|
||||
<el-input-number v-model="form.totalPhases" :min="1" :max="99" placeholder="请输入" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="供应商名称" prop="supplierName">
|
||||
<el-input v-model="form.supplierName" placeholder="选择合同后自动带出" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="供应商联系人及电话" prop="supplierContact">
|
||||
<el-input v-model="form.supplierContact" placeholder="请输入" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="采购人员" prop="purchaserId">
|
||||
<org-selector v-model:orgList="purchaserList" type="user" :multiple="false" @update:orgList="onPurchaserChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="资产管理员" prop="assetAdminId">
|
||||
<org-selector v-model:orgList="assetAdminList" type="user" :multiple="false" @update:orgList="onAssetAdminChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Record<string, any>
|
||||
projectName?: string
|
||||
deptName?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const purchaserList = ref<any[]>([])
|
||||
const assetAdminList = ref<any[]>([])
|
||||
|
||||
const form = reactive({
|
||||
hasContract: '0',
|
||||
contractId: '',
|
||||
isInstallment: '0',
|
||||
totalPhases: 1,
|
||||
supplierName: '',
|
||||
supplierContact: '',
|
||||
purchaserId: '',
|
||||
purchaserName: '',
|
||||
assetAdminId: '',
|
||||
assetAdminName: '',
|
||||
...props.modelValue,
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (val) => {
|
||||
Object.assign(form, val || {})
|
||||
// 人员选择回显
|
||||
if (form.purchaserId && form.purchaserName) {
|
||||
purchaserList.value = [{ id: form.purchaserId, name: form.purchaserName, type: 'user' }]
|
||||
} else {
|
||||
purchaserList.value = []
|
||||
}
|
||||
if (form.assetAdminId && form.assetAdminName) {
|
||||
assetAdminList.value = [{ id: form.assetAdminId, name: form.assetAdminName, type: 'user' }]
|
||||
} else {
|
||||
assetAdminList.value = []
|
||||
}
|
||||
}, { deep: true, immediate: true })
|
||||
watch(form, () => emit('update:modelValue', { ...form }), { deep: true })
|
||||
|
||||
const onPurchaserChange = (list: any[]) => {
|
||||
if (list?.length) {
|
||||
const u = list[0]
|
||||
form.purchaserId = u.userId || u.id || ''
|
||||
form.purchaserName = u.name || u.realName || ''
|
||||
} else {
|
||||
form.purchaserId = ''
|
||||
form.purchaserName = ''
|
||||
}
|
||||
}
|
||||
|
||||
const onAssetAdminChange = (list: any[]) => {
|
||||
if (list?.length) {
|
||||
const u = list[0]
|
||||
form.assetAdminId = u.userId || u.id || ''
|
||||
form.assetAdminName = u.name || u.realName || ''
|
||||
} else {
|
||||
form.assetAdminId = ''
|
||||
form.assetAdminName = ''
|
||||
}
|
||||
}
|
||||
|
||||
const rules: FormRules = {
|
||||
isInstallment: [{ required: true, message: '请选择是否分期验收', trigger: 'change' }],
|
||||
totalPhases: [{ required: true, message: '请输入分期次数', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
const validate = () => formRef.value?.validate()
|
||||
|
||||
defineExpose({ validate, form })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.accept-common-form {
|
||||
padding: 0 4px;
|
||||
}
|
||||
.accept-common-form :deep(.el-form-item) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
/* 紧凑表单样式 */
|
||||
.compact-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
padding-right: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.el-input__inner),
|
||||
:deep(.el-textarea__inner) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,405 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="履约验收"
|
||||
width="85%"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
class="purchasing-accept-modal"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div v-loading="loading" class="modal-body">
|
||||
<div class="main-tabs">
|
||||
<div class="main-tab-nav">
|
||||
<div
|
||||
class="main-tab-item"
|
||||
:class="{ active: mainTab === 'common' }"
|
||||
@click="mainTab = 'common'"
|
||||
>
|
||||
公共信息
|
||||
</div>
|
||||
<div
|
||||
class="main-tab-item"
|
||||
:class="{ active: mainTab === 'batch' }"
|
||||
@click="mainTab = 'batch'"
|
||||
>
|
||||
分期验收{{ batches.length > 0 ? ` (${batches.length})` : '' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-tab-content">
|
||||
<div v-show="mainTab === 'common'" class="tab-content">
|
||||
<AcceptCommonForm
|
||||
ref="commonFormRef"
|
||||
v-model="commonForm"
|
||||
:project-name="applyInfo?.projectName"
|
||||
:dept-name="applyInfo?.deptName"
|
||||
/>
|
||||
</div>
|
||||
<div v-show="mainTab === 'batch'" class="tab-content">
|
||||
<div v-if="batches.length > 0">
|
||||
<div class="batch-tabs">
|
||||
<div
|
||||
v-for="b in batches"
|
||||
:key="b.id"
|
||||
class="batch-tab-item"
|
||||
:class="{ active: String(b.batch) === activeTab, disabled: !canEditBatch(b.batch) }"
|
||||
@click="canEditBatch(b.batch) && (activeTab = String(b.batch))"
|
||||
>
|
||||
<span>第{{ b.batch }}期</span>
|
||||
<el-tag v-if="isBatchCompleted(b)" type="success" size="small">已填</el-tag>
|
||||
<el-tag v-else-if="!canEditBatch(b.batch)" type="info" size="small">需先完成上一期</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="batch-panel">
|
||||
<AcceptBatchForm
|
||||
v-for="b in batches"
|
||||
v-show="String(b.batch) === activeTab"
|
||||
:key="b.id"
|
||||
v-model="batchForms[b.batch]"
|
||||
:can-fill="canFillForm"
|
||||
:readonly="false"
|
||||
:purchase-id="String(purchaseId)"
|
||||
:acceptance-items="acceptanceItems"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="tip-box">
|
||||
<el-alert type="info" :closable="false" show-icon>
|
||||
请先在「公共信息」中填写并点击「保存公共配置」,系统将按分期次数自动生成验收批次
|
||||
</el-alert>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="handleClose">关 闭</el-button>
|
||||
<el-button
|
||||
v-if="mainTab === 'common' || batches.length === 0"
|
||||
type="primary"
|
||||
@click="saveCommonConfig"
|
||||
:loading="saving"
|
||||
>
|
||||
保存公共配置
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="mainTab === 'batch' && activeBatchId"
|
||||
type="primary"
|
||||
@click="saveCurrentBatch"
|
||||
:loading="saving"
|
||||
>
|
||||
保存第{{ activeTab }}期
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch, nextTick } from 'vue'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import {
|
||||
saveCommonConfig as apiSaveCommonConfig,
|
||||
getCommonConfigWithBatches,
|
||||
updateBatch,
|
||||
canFillForm as apiCanFillForm,
|
||||
getAcceptanceItems,
|
||||
getDetail,
|
||||
} from '/@/api/purchase/purchasingAccept'
|
||||
import AcceptCommonForm from './AcceptCommonForm.vue'
|
||||
import AcceptBatchForm from './AcceptBatchForm.vue'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const purchaseId = ref<string | number>('')
|
||||
const applyInfo = ref<any>(null)
|
||||
const rowProjectType = ref<string>('A')
|
||||
const canFillForm = ref(true)
|
||||
const acceptanceItems = ref<any[]>([])
|
||||
const batches = ref<any[]>([])
|
||||
const mainTab = ref('common')
|
||||
const activeTab = ref('1')
|
||||
const commonFormRef = ref()
|
||||
const commonForm = reactive<Record<string, any>>({})
|
||||
const batchForms = reactive<Record<number, any>>({})
|
||||
|
||||
const activeBatchId = computed(() => {
|
||||
const b = batches.value.find((x: any) => String(x.batch) === activeTab.value)
|
||||
return b?.id || ''
|
||||
})
|
||||
|
||||
const canEditBatch = (batch: number) => {
|
||||
if (batch === 1) return true
|
||||
for (let i = 1; i < batch; i++) {
|
||||
if (!isBatchCompletedByIdx(i)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const isBatchCompleted = (b: any) => {
|
||||
return !!(b.acceptType && b.acceptDate)
|
||||
}
|
||||
|
||||
const isBatchCompletedByIdx = (batch: number) => {
|
||||
const b = batches.value.find((x: any) => x.batch === batch)
|
||||
return b ? isBatchCompleted(b) : false
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
if (!purchaseId.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
const [configRes, canFillRes] = await Promise.all([
|
||||
getCommonConfigWithBatches(String(purchaseId.value)),
|
||||
apiCanFillForm(String(purchaseId.value)),
|
||||
])
|
||||
const config = configRes?.data
|
||||
canFillForm.value = !!canFillRes?.data
|
||||
|
||||
if (config?.common) {
|
||||
Object.assign(commonForm, {
|
||||
hasContract: config.common.hasContract || '0',
|
||||
contractId: config.common.contractId || '',
|
||||
isInstallment: config.common.isInstallment || '0',
|
||||
totalPhases: config.common.totalPhases || 1,
|
||||
supplierName: config.common.supplierName || '',
|
||||
supplierContact: config.common.supplierContact || '',
|
||||
purchaserId: config.common.purchaserId || '',
|
||||
purchaserName: config.common.purchaserName || '',
|
||||
assetAdminId: config.common.assetAdminId || '',
|
||||
assetAdminName: config.common.assetAdminName || '',
|
||||
})
|
||||
applyInfo.value = config.common
|
||||
}
|
||||
|
||||
const projectType = applyInfo.value?.projectType || rowProjectType.value || 'A'
|
||||
const typeMap: Record<string, string> = { A: 'A', B: 'B', C: 'C' }
|
||||
const at = typeMap[projectType] || 'A'
|
||||
const itemsRes = await getAcceptanceItems(at)
|
||||
acceptanceItems.value = itemsRes?.data || []
|
||||
|
||||
if (config?.batches?.length) {
|
||||
batches.value = config.batches.sort((a: any, b: any) => (a.batch || 0) - (b.batch || 0))
|
||||
activeTab.value = String(batches.value[0]?.batch || '1')
|
||||
mainTab.value = 'batch'
|
||||
for (const b of batches.value) {
|
||||
if (!batchForms[b.batch]) batchForms[b.batch] = {}
|
||||
}
|
||||
await loadBatchDetails()
|
||||
} else {
|
||||
batches.value = []
|
||||
}
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const loadBatchDetails = async () => {
|
||||
for (const b of batches.value) {
|
||||
try {
|
||||
const res = await getDetail(String(purchaseId.value), b.batch)
|
||||
const d = res?.data
|
||||
if (d?.accept) {
|
||||
const itemMap = (acceptanceItems.value || []).reduce((acc: any, it: any) => {
|
||||
acc[it.id] = it
|
||||
return acc
|
||||
}, {})
|
||||
batchForms[b.batch] = {
|
||||
acceptType: d.accept.acceptType || '1',
|
||||
acceptDate: d.accept.acceptDate || '',
|
||||
remark: d.accept.remark || '',
|
||||
templateFileIds: d.accept.templateFileIds || [],
|
||||
acceptContents: (d.contents || []).map((c: any) => {
|
||||
const cfg = itemMap[c.configId]
|
||||
return {
|
||||
configId: c.configId,
|
||||
itemName: cfg?.itemName || '',
|
||||
type: cfg?.type,
|
||||
isQualified: c.isQualified || '1',
|
||||
remark: c.remark || '',
|
||||
}
|
||||
}),
|
||||
acceptTeam: (d.team || []).map((t: any) => ({
|
||||
name: t.name,
|
||||
deptCode: t.deptCode,
|
||||
deptName: t.deptName,
|
||||
})),
|
||||
}
|
||||
if (batchForms[b.batch].acceptTeam.length < 3) {
|
||||
while (batchForms[b.batch].acceptTeam.length < 3) {
|
||||
batchForms[b.batch].acceptTeam.push({ name: '', deptCode: '', deptName: '' })
|
||||
}
|
||||
}
|
||||
if (acceptanceItems.value.length && (!batchForms[b.batch].acceptContents || batchForms[b.batch].acceptContents.length === 0)) {
|
||||
batchForms[b.batch].acceptContents = acceptanceItems.value.map((it: any) => ({
|
||||
configId: it.id,
|
||||
itemName: it.itemName,
|
||||
type: it.type,
|
||||
isQualified: '1',
|
||||
remark: '',
|
||||
}))
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
const saveCommonConfig = async () => {
|
||||
const valid = await commonFormRef.value?.validate?.().catch(() => false)
|
||||
if (!valid) return
|
||||
if (commonForm.isInstallment === '1' && (!commonForm.totalPhases || commonForm.totalPhases < 1)) {
|
||||
useMessage().error('请填写分期次数')
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
await apiSaveCommonConfig({
|
||||
purchaseId: String(purchaseId.value),
|
||||
hasContract: commonForm.hasContract,
|
||||
contractId: commonForm.contractId,
|
||||
isInstallment: commonForm.isInstallment,
|
||||
totalPhases: commonForm.isInstallment === '1' ? commonForm.totalPhases : 1,
|
||||
supplierName: commonForm.supplierName,
|
||||
supplierContact: commonForm.supplierContact,
|
||||
purchaserId: commonForm.purchaserId,
|
||||
purchaserName: commonForm.purchaserName,
|
||||
assetAdminId: commonForm.assetAdminId,
|
||||
assetAdminName: commonForm.assetAdminName,
|
||||
})
|
||||
useMessage().success('保存成功')
|
||||
await loadData()
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const saveCurrentBatch = async () => {
|
||||
const b = batches.value.find((x: any) => String(x.batch) === activeTab.value)
|
||||
if (!b?.id) return
|
||||
const form = batchForms[Number(activeTab.value)]
|
||||
if (!form) return
|
||||
|
||||
const team = (form.acceptTeam || []).filter((m: any) => m?.name)
|
||||
if (team.length < 3 || team.length % 2 === 0) {
|
||||
useMessage().error('验收小组至少3人且为单数')
|
||||
return
|
||||
}
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
await updateBatch({
|
||||
id: b.id,
|
||||
purchaseId: String(purchaseId.value),
|
||||
acceptType: form.acceptType,
|
||||
acceptDate: form.acceptDate,
|
||||
remark: form.remark,
|
||||
templateFileIds: form.templateFileIds || [],
|
||||
acceptContents: form.acceptContents || [],
|
||||
acceptTeam: team,
|
||||
})
|
||||
useMessage().success('保存成功')
|
||||
await loadData()
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
const open = (row: any) => {
|
||||
purchaseId.value = row?.id ?? ''
|
||||
rowProjectType.value = row?.projectType || 'A'
|
||||
visible.value = true
|
||||
batches.value = []
|
||||
Object.keys(batchForms).forEach((k) => delete batchForms[Number(k)])
|
||||
nextTick(() => loadData())
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-body {
|
||||
padding: 0;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.main-tab-nav {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
.main-tab-item {
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
color: var(--el-text-color-regular);
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.main-tab-item:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
.main-tab-item.active {
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 600;
|
||||
border-bottom-color: var(--el-color-primary);
|
||||
}
|
||||
.main-tab-content {
|
||||
padding-top: 4px;
|
||||
}
|
||||
.tab-content {
|
||||
min-height: 200px;
|
||||
display: block;
|
||||
}
|
||||
.tip-box {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.batch-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.batch-tab-item {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.batch-tab-item:hover:not(.disabled) {
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
.batch-tab-item.active {
|
||||
background: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
.batch-tab-item.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.batch-panel {
|
||||
min-height: 200px;
|
||||
}
|
||||
</style>
|
||||
@@ -191,7 +191,7 @@
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="300">
|
||||
<el-table-column label="操作" align="center" fixed="right" width="360">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="View"
|
||||
@@ -216,6 +216,13 @@
|
||||
@click="handleDelete(scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="DocumentChecked"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleAccept(scope.row)">
|
||||
履约验收
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -257,6 +264,9 @@
|
||||
ref="formDialogRef"
|
||||
:dict-data="dictData"
|
||||
@refresh="getDataList" />
|
||||
|
||||
<!-- 履约验收弹窗 -->
|
||||
<PurchasingAcceptModal ref="acceptModalRef" @refresh="getDataList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -268,10 +278,11 @@ import { getPage, delObj } from "/@/api/finance/purchasingrequisition";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { getDicts } from '/@/api/admin/dict';
|
||||
import { getTree } from '/@/api/finance/purchasingcategory';
|
||||
import { List, Document, DocumentCopy, Search, Collection, Money, CircleCheck, InfoFilled, Calendar, OfficeBuilding, Warning } from '@element-plus/icons-vue'
|
||||
import { List, Document, DocumentCopy, Search, Collection, Money, CircleCheck, InfoFilled, Calendar, OfficeBuilding, Warning, DocumentChecked } from '@element-plus/icons-vue'
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const PurchasingAcceptModal = defineAsyncComponent(() => import('./accept/PurchasingAcceptModal.vue'));
|
||||
|
||||
// 字典数据和品目树数据
|
||||
const dictData = ref({
|
||||
@@ -288,6 +299,7 @@ const dictData = ref({
|
||||
const router = useRouter()
|
||||
const tableRef = ref()
|
||||
const formDialogRef = ref()
|
||||
const acceptModalRef = ref()
|
||||
const searchFormRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const showAddIframe = ref(false)
|
||||
@@ -361,6 +373,13 @@ const handleIframeMessage = (event: MessageEvent) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 履约验收
|
||||
*/
|
||||
const handleAccept = (row: any) => {
|
||||
acceptModalRef.value?.open(row);
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除当前行
|
||||
* @param row - 当前行数据
|
||||
|
||||
Reference in New Issue
Block a user