feat(erp): 新增项目验收和收货确认模块

- 新增项目验收确认编辑页面,包含表单录入、编号生成、项目选择等功能
- 新增项目收货确认编辑页面,支持表单验证、编号生成及流程启动
- 新增项目验收确认相关API接口,支持列表查询、获取详情、新增、修改、删除及提交流程
- 新增项目收货确认相关API接口,实现列表获取、详情查询、新增、更新、删除及流程发起
- 新增项目选择对话框组件,支持多条件筛选和单多选模式
- 实现前端权限控制,仅允许项目经理或超级管理员提交或暂存确认单据
- 实现审批状态映射,支持草稿、审批中、驳回、撤销等状态展示与操作
- 优化页面初始化逻辑,支持path和query参数兼容获取ID和任务ID
- 实现附件上传功能,限制最多上传5个文件
- 统一表单校验规则,确保必填字段完整性
- 新增流水号生成逻辑,调用统一编码规则服务实现编号自动生成
- 支持流程审批记录查看及流程提交验证弹窗显示
- 支持页面类型区分编辑、查看、审批,禁用相应操作以保证业务流程正确
dev
zangch@mesnac.com 3 months ago
parent bb7ff1d940
commit fe777549d4

@ -0,0 +1,99 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ProjectAcceptanceVO, ProjectAcceptanceForm, ProjectAcceptanceQuery } from '@/api/oa/erp/projectAcceptance/types';
/**
*
* @param query
* @returns {*}
*/
export const listProjectAcceptance = (query?: ProjectAcceptanceQuery): AxiosPromise<ProjectAcceptanceVO[]> => {
return request({
url: '/oa/erp/projectAcceptance/list',
method: 'get',
params: query
});
};
/**
*
* @param acceptanceId
*/
export const getProjectAcceptance = (acceptanceId: string | number): AxiosPromise<ProjectAcceptanceVO> => {
return request({
url: '/oa/erp/projectAcceptance/' + acceptanceId,
method: 'get'
});
};
/**
*
* @param data
*/
export const addProjectAcceptance = (data: ProjectAcceptanceForm) => {
return request({
url: '/oa/erp/projectAcceptance',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateProjectAcceptance = (data: ProjectAcceptanceForm) => {
return request({
url: '/oa/erp/projectAcceptance',
method: 'put',
data: data
});
};
/**
*
* @param acceptanceId
*/
export const delProjectAcceptance = (acceptanceId: string | number | Array<string | number>) => {
return request({
url: '/oa/erp/projectAcceptance/' + acceptanceId,
method: 'delete'
});
};
/**
* ID
* @param projectId
*/
export const prepareProjectAcceptanceByProjectId = (projectId: string | number): AxiosPromise<ProjectAcceptanceVO> => {
return request({
url: '/oa/erp/projectAcceptance/prepareByProjectId/' + projectId,
method: 'get'
});
};
/**
*
* @param data
*/
export const submitProjectAcceptanceAndFlowStart = (data: ProjectAcceptanceForm): AxiosPromise<ProjectAcceptanceVO> => {
return request({
url: '/oa/erp/projectAcceptance/submitAndFlowStart',
method: 'post',
data: data
});
};
/**
*
* @param query
* @returns {*}
*/
export function getErpProjectAcceptanceList (query) {
return request({
url: '/oa/erp/projectAcceptance/getErpProjectAcceptanceList',
method: 'get',
params: query
});
};

@ -0,0 +1,229 @@
export interface ProjectAcceptanceVO {
/**
* ID
*/
acceptanceId: string | number;
/**
* ID
*/
projectId: string | number;
/**
*
*/
projectCode: string;
/**
*
*/
projectName: string;
/**
*
*/
managerId: string | number;
/**
*
*/
projectManagerName?: string;
/**
*
*/
acceptanceDate: string;
/**
*
*/
ossId: string | number;
/**
*
*/
acceptanceCode?: string;
/**
*
*/
chargeId: string | number;
/**
*
*/
chargeName?: string;
/**
*
*/
deputyId: string | number;
/**
*
*/
deputyName?: string;
/**
*
*/
remark: string;
/**
*
*/
flowStatus?: string;
/**
* (1 2 3)
*/
acceptanceStatus?: string;
}
export interface ProjectAcceptanceForm extends BaseEntity {
/**
* ID
*/
acceptanceId?: string | number;
/**
* ID
*/
projectId?: string | number;
/**
*
*/
projectCode?: string;
/**
*
*/
projectName?: string;
/**
*
*/
managerId?: string | number;
/**
*
*/
projectManagerName?: string;
/**
*
*/
acceptanceDate?: string;
/**
*
*/
ossId?: string | number;
/**
*
*/
acceptanceCode?: string;
/**
*
*/
chargeId?: string | number;
/**
*
*/
chargeName?: string;
/**
*
*/
deputyId?: string | number;
/**
*
*/
deputyName?: string;
/**
*
*/
remark?: string;
/**
*
*/
flowStatus?: string;
/**
* (1 2 3)
*/
acceptanceStatus?: string;
/**
*
*/
flowCode?: string;
/**
*
*/
variables?: any;
/**
*
*/
bizExt?: any;
}
export interface ProjectAcceptanceQuery extends PageQuery {
/**
* ID
*/
projectId?: string | number;
/**
*
*/
projectCode?: string;
/**
*
*/
projectName?: string;
/**
*
*/
managerId?: string | number;
/**
*
*/
acceptanceDate?: string;
/**
*
*/
ossId?: string | number;
/**
*
*/
chargeId?: string | number;
/**
*
*/
deputyId?: string | number;
/**
*
*/
params?: any;
}

@ -0,0 +1,99 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ProjectReceivingVO, ProjectReceivingForm, ProjectReceivingQuery } from '@/api/oa/erp/projectReceiving/types';
/**
*
* @param query
* @returns {*}
*/
export const listProjectReceiving = (query?: ProjectReceivingQuery): AxiosPromise<ProjectReceivingVO[]> => {
return request({
url: '/oa/erp/projectReceiving/list',
method: 'get',
params: query
});
};
/**
*
* @param query
* @returns {*}
*/
export const getErpProjectReceivingList = (query?: ProjectReceivingQuery): AxiosPromise<ProjectReceivingVO[]> => {
return request({
url: '/oa/erp/projectReceiving/getErpProjectReceivingList',
method: 'get',
params: query
});
};
/**
*
* @param data
*/
export const submitProjectReceivingAndFlowStart = (data: ProjectReceivingForm): AxiosPromise<ProjectReceivingVO> => {
return request({
url: '/oa/erp/projectReceiving/submitAndFlowStart',
method: 'post',
data: data
});
};
/**
* ID
* @param projectId
*/
export const prepareProjectReceivingByProjectId = (projectId: string | number): AxiosPromise<ProjectReceivingVO> => {
return request({
url: '/oa/erp/projectReceiving/prepareByProjectId/' + projectId,
method: 'get'
});
};
/**
*
* @param receivingId
*/
export const getProjectReceiving = (receivingId: string | number): AxiosPromise<ProjectReceivingVO> => {
return request({
url: '/oa/erp/projectReceiving/' + receivingId,
method: 'get'
});
};
/**
*
* @param data
*/
export const addProjectReceiving = (data: ProjectReceivingForm) => {
return request({
url: '/oa/erp/projectReceiving',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateProjectReceiving = (data: ProjectReceivingForm) => {
return request({
url: '/oa/erp/projectReceiving',
method: 'put',
data: data
});
};
/**
*
* @param receivingId
*/
export const delProjectReceiving = (receivingId: string | number | Array<string | number>) => {
return request({
url: '/oa/erp/projectReceiving/' + receivingId,
method: 'delete'
});
};

@ -0,0 +1,224 @@
export interface ProjectReceivingVO {
/**
* ID
*/
receivingId: string | number;
/**
*
*/
projectCode: string;
/**
*
*/
projectName: string;
/**
* ID
*/
projectId?: string | number;
/**
*
*/
managerId: string | number;
/**
*
*/
projectManagerName?: string;
/**
*
*/
arrivalDate: string;
/**
*
*/
ossId: string | number;
/**
*
*/
receivingCode?: string;
/**
*
*/
chargeId: string | number;
/**
*
*/
chargeName?: string;
/**
*
*/
deputyId: string | number;
/**
*
*/
deputyName?: string;
/**
*
*/
remark: string;
/**
*
*/
flowStatus?: string;
/**
* (1 2 3)
*/
receivingStatus?: string;
}
export interface ProjectReceivingForm extends BaseEntity {
/**
* ID
*/
receivingId?: string | number;
/**
*
*/
projectCode?: string;
/**
*
*/
projectName?: string;
/**
* ID
*/
projectId?: string | number;
/**
*
*/
managerId?: string | number;
/**
*
*/
projectManagerName?: string;
/**
*
*/
arrivalDate?: string;
/**
*
*/
ossId?: string | number;
/**
*
*/
receivingCode?: string;
/**
*
*/
chargeId?: string | number;
/**
*
*/
chargeName?: string;
/**
*
*/
deputyId?: string | number;
/**
*
*/
deputyName?: string;
/**
*
*/
remark?: string;
/**
*
*/
flowStatus?: string;
/**
* (1 2 3)
*/
receivingStatus?: string;
/**
*
*/
flowCode?: string;
/**
*
*/
variables?: any;
/**
*
*/
bizExt?: any;
}
export interface ProjectReceivingQuery extends PageQuery {
/**
*
*/
projectCode?: string;
/**
*
*/
projectName?: string;
/**
*
*/
managerId?: string | number;
/**
*
*/
arrivalDate?: string;
/**
*
*/
ossId?: string | number;
/**
*
*/
chargeId?: string | number;
/**
*
*/
deputyId?: string | number;
/**
*
*/
params?: any;
}

@ -0,0 +1,368 @@
<template>
<div>
<el-dialog
v-model="projectDialog.visible.value"
:title="projectDialog.title.value"
width="80%"
append-to-body
>
<el-card shadow="never" class="mb-2">
<el-form
ref="queryFormRef"
:model="queryParams"
:inline="true"
label-width="90px"
>
<el-form-item label="项目编号" prop="projectCode">
<el-input
v-model="queryParams.projectCode"
placeholder="请输入项目编号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input
v-model="queryParams.projectName"
placeholder="请输入项目名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="项目状态" prop="projectStatus">
<el-select
v-model="queryParams.projectStatus"
placeholder="请选择项目状态"
clearable
>
<el-option
v-for="dict in project_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="项目类别" prop="projectCategory">
<el-select
v-model="queryParams.projectCategory"
placeholder="请选择项目类别"
clearable
>
<el-option
v-for="dict in project_category"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="业务方向" prop="businessDirection">
<el-select
v-model="queryParams.businessDirection"
placeholder="请选择业务方向"
clearable
>
<el-option
v-for="dict in business_direction"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">
搜索
</el-button>
<el-button icon="Refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never">
<el-table
v-loading="loading"
border
:data="projectList"
@selection-change="handleSelectionChange"
@row-dblclick="handleRowDblClick"
>
<el-table-column
v-if="multiple"
type="selection"
width="55"
align="center"
/>
<el-table-column
label="项目编号"
prop="projectCode"
width="140"
align="center"
/>
<el-table-column
label="项目名称"
prop="projectName"
min-width="180"
align="center"
/>
<el-table-column
label="业务方向"
prop="businessDirection"
width="110"
align="center"
>
<template #default="scope">
<dict-tag
:options="business_direction"
:value="scope.row.businessDirection"
/>
</template>
</el-table-column>
<el-table-column
label="项目类别"
prop="projectCategory"
width="120"
align="center"
>
<template #default="scope">
<dict-tag
:options="project_category"
:value="scope.row.projectCategory"
/>
</template>
</el-table-column>
<el-table-column
label="项目经理"
prop="managerId"
width="120"
align="center"
>
<template #default="scope">
<span>{{ (scope.row as any).managerName }}</span>
</template>
</el-table-column>
<el-table-column
label="部门负责人"
prop="chargeId"
width="120"
align="center"
>
<template #default="scope">
<span>{{ (scope.row as any).chargeName }}</span>
</template>
</el-table-column>
<el-table-column
label="分管副总"
prop="deputyId"
width="120"
align="center"
>
<template #default="scope">
<span>{{ (scope.row as any).deputyName }}</span>
</template>
</el-table-column>
<el-table-column
label="项目状态"
prop="projectStatus"
width="110"
align="center"
>
<template #default="scope">
<dict-tag
:options="project_status"
:value="scope.row.projectStatus"
/>
</template>
</el-table-column>
<el-table-column
label="流程状态"
prop="flowStatus"
width="110"
align="center"
>
<template #default="scope">
<dict-tag
:options="wf_business_status"
:value="scope.row.flowStatus"
/>
</template>
</el-table-column>
<el-table-column
label="金额"
prop="amount"
width="120"
align="center"
/>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-card>
<template #footer>
<div class="dialog-footer">
<el-button @click="close"></el-button>
<el-button type="primary" @click="confirm"></el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { listProjectInfo } from '@/api/oa/erp/projectInfo'
import type {
ProjectInfoVO,
ProjectInfoQuery
} from '@/api/oa/erp/projectInfo/types'
import useDialog from '@/hooks/useDialog'
interface PropType {
/** 当前已选项目(单选或多选) */
modelValue?: ProjectInfoVO | ProjectInfoVO[] | undefined
/** 是否多选,默认单选 */
multiple?: boolean
}
const props = withDefaults(defineProps<PropType>(), {
multiple: false,
modelValue: undefined
})
const emit = defineEmits<{
(e: 'update:modelValue', val: ProjectInfoVO | ProjectInfoVO[] | undefined): void
(e: 'confirm', val: ProjectInfoVO | ProjectInfoVO[] | undefined): void
}>()
const { proxy } = getCurrentInstance() as any
const {
business_direction,
project_status,
project_category,
wf_business_status
} = toRefs<any>(
proxy?.useDict('business_direction', 'project_status', 'project_category', 'wf_business_status')
)
const projectDialog = useDialog({
title: '项目选择'
})
const loading = ref(false)
const total = ref(0)
const projectList = ref<ProjectInfoVO[]>([])
const selectedRows = ref<ProjectInfoVO[]>([])
const queryFormRef = ref<ElFormInstance>()
const queryParams = reactive<ProjectInfoQuery>({
pageNum: 1,
pageSize: 10,
projectCode: undefined,
projectName: undefined,
businessDirection: undefined,
projectCategory: undefined,
projectStatus: undefined,
flowStatus: undefined,
activeFlag: '1',
params: {}
})
const multiple = computed(() => props.multiple)
const initFromModelValue = () => {
selectedRows.value = []
if (!props.modelValue) return
if (Array.isArray(props.modelValue)) {
selectedRows.value = [...props.modelValue]
} else {
selectedRows.value = [props.modelValue]
}
}
const getList = async () => {
loading.value = true
try {
const res = await listProjectInfo(queryParams)
projectList.value = res.rows || []
total.value = res.total || 0
} finally {
loading.value = false
}
}
const handleQuery = () => {
queryParams.pageNum = 1
getList()
}
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
const handleSelectionChange = (selection: ProjectInfoVO[]) => {
if (!multiple.value) {
// selection
selectedRows.value = selection.slice(0, 1)
} else {
selectedRows.value = selection
}
}
const handleRowDblClick = (row: ProjectInfoVO) => {
if (multiple.value) {
return
}
selectedRows.value = [row]
confirm()
}
const confirm = () => {
const value: ProjectInfoVO | ProjectInfoVO[] | undefined = multiple.value
? selectedRows.value
: selectedRows.value[0]
emit('update:modelValue', value)
emit('confirm', value)
close()
}
const close = () => {
projectDialog.closeDialog()
}
watch(
() => projectDialog.visible.value,
async (val) => {
if (val) {
initFromModelValue()
await getList()
} else {
selectedRows.value = []
}
}
)
// 使open / close
const open = () => {
projectDialog.openDialog()
}
defineExpose({
open,
close
})
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
}
</style>

@ -0,0 +1,315 @@
<template>
<div class="p-2">
<el-card shadow="never" style="margin-top: 0">
<approvalButton
@submitForm="submitForm"
@approvalVerifyOpen="approvalVerifyOpen"
@handleApprovalRecord="handleApprovalRecord"
:buttonLoading="buttonLoading"
:id="form.acceptanceId"
:status="approvalStatus"
:pageType="pageTypeForButton"
:mode="false"
/>
</el-card>
<el-card shadow="never" style="margin-top: 0">
<el-form ref="formRef" :model="form" :disabled="formDisabled" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="验收确认编号" prop="acceptanceCode">
<el-input v-model="form.acceptanceCode" placeholder="自动生成" disabled />
<el-button class="ml-2" type="primary" @click="generateAcceptanceCode" :disabled="formDisabled || isCodeGenerated">生成编号</el-button>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-select v-model="selectedProjectId" filterable clearable placeholder="请选择项目" :disabled="routeParams.type !== 'add'" :remote-method="remoteSearchProject" @change="onProjectChange">
<el-option v-for="item in projectOptions" :key="item.projectId" :label="item.projectName + ' / ' + item.projectCode" :value="item.projectId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="form.projectCode" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目经理" prop="managerId">
<el-input v-model="form.projectManagerName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="验收日期" prop="acceptanceDate">
<el-date-picker v-model="form.acceptanceDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择验收日期" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="验收单附件" prop="ossId">
<FileUpload v-model="ossIdString" :limit="5" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门负责人" prop="chargeId">
<el-input v-model="form.chargeName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="分管副总" prop="deputyId">
<el-input v-model="form.deputyName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="确认该项目达到客户验收节点,可以进行发货款收取(如设计)。" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<ApprovalRecord ref="approvalRecordRef" />
<SubmitVerify ref="submitVerifyRef" @submit-callback="submitCallback" />
</div>
</template>
<script setup lang="ts">
import ApprovalButton from '@/components/Process/approvalButton.vue'
import ApprovalRecord from '@/components/Process/approvalRecord.vue'
import FileUpload from '@/components/FileUpload/index.vue'
import { useRoute } from 'vue-router'
import { useUserStore } from '@/store/modules/user'
import { getErpProjectInfoList } from '@/api/oa/erp/projectInfo'
import { getProjectAcceptance, addProjectAcceptance, updateProjectAcceptance, submitProjectAcceptanceAndFlowStart, prepareProjectAcceptanceByProjectId } from '@/api/oa/erp/projectAcceptance'
import { getRuleGenerateCode } from '@/api/system/codeRule'
import type { ProjectAcceptanceForm } from '@/api/oa/erp/projectAcceptance/types'
import SubmitVerify from '@/components/Process/submitVerify.vue'
const { proxy } = getCurrentInstance() as any
const route = useRoute()
// path :acceptanceId query id
const routeParams = reactive<{ id?: string | number; type?: string; taskId?: string | number }>({
id: (route.params.acceptanceId || route.query.id) as any,
type: (route.query.type as any) || 'add',
taskId: route.query.taskId as any
})
const userStore = useUserStore()
const isSuperAdmin = computed(() => {
const roles = userStore.roles || []
return roles.includes('admin') || roles.includes('superadmin')
})
const buttonLoading = ref(false)
const formRef = ref<ElFormInstance>()
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>()
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>()
const projectOptions = ref<any[]>([])
const selectedProjectId = ref<any>()
const initFormData: ProjectAcceptanceForm = {
acceptanceId: undefined,
projectId: undefined,
projectCode: undefined,
projectName: undefined,
managerId: undefined,
projectManagerName: undefined,
acceptanceDate: undefined,
ossId: undefined,
chargeId: undefined,
chargeName: undefined as any,
deputyId: undefined,
deputyName: undefined as any,
remark: undefined,
flowCode: 'OAPA',
variables: {},
bizExt: {}
}
const form = ref<ProjectAcceptanceForm>({ ...initFormData })
const isCodeGenerated = ref(false)
//
const approvalStatus = computed(() => {
const s = form.value.flowStatus as any
if (!s || s === '草稿') return 'draft'
if (s === '审批中') return 'waiting'
if (s === '驳回') return 'back'
if (s === '撤销') return 'cancel'
return s
})
const pageTypeForButton = computed(() => {
if (routeParams.taskId) {
return 'approval'
}
return routeParams.type
})
const rules = {
projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
projectCode: [{ required: true, message: '项目号不能为空', trigger: 'blur' }],
acceptanceDate: [{ required: true, message: '验收日期不能为空', trigger: 'change' }]
} as any
const ossIdString = computed({
get() {
const v = form.value.ossId as any
return v === undefined || v === null ? '' : String(v)
},
set(val: string) {
form.value.ossId = val || undefined as any
}
})
const preloadProjectList = async () => {
const res = await getErpProjectInfoList({})
projectOptions.value = res.data || []
}
const remoteSearchProject = async (query: string) => {
const res = await getErpProjectInfoList({ projectName: query })
projectOptions.value = res.data || []
}
const onProjectChange = async (val: any) => {
if (!val) return
const { data } = await prepareProjectAcceptanceByProjectId(val)
Object.assign(form.value, data)
form.value.projectId = val
applyLeaderFromManager()
}
const applyLeaderFromManager = () => {
}
const loadDetail = async () => {
if (!routeParams.id || Number(routeParams.id) === 0) return
const res = await getProjectAcceptance(routeParams.id as any)
Object.assign(form.value, res.data)
selectedProjectId.value = form.value.projectId
isCodeGenerated.value = !!form.value.acceptanceCode
}
const submitForm = (status: string, mode: boolean) => {
formRef.value?.validate(async (valid: boolean) => {
if (!valid) return
//
if (!form.value.managerId) {
proxy?.$modal.msgError('请先选择项目')
return
}
if (!isSuperAdmin.value && userStore.userId !== form.value.managerId) {
proxy?.$modal.msgError('只有项目经理才能提交或暂存项目验收确认')
return
}
buttonLoading.value = true
try {
if (status === 'draft') {
// draft
form.value.flowStatus = 'draft' as any
form.value.acceptanceStatus = '1' //
if (form.value.acceptanceId) {
await updateProjectAcceptance(form.value)
} else {
await addProjectAcceptance(form.value)
}
proxy?.$modal.msgSuccess('暂存成功')
proxy?.$tab.closePage(proxy.$route)
proxy?.$router.go(-1)
} else {
const variables: any = { managerId: form.value.managerId, managerName: form.value.projectManagerName }
const payload: ProjectAcceptanceForm = { ...form.value, flowCode: 'OAPA', variables, bizExt: { businessCode: form.value.acceptanceCode } } as any
await submitProjectAcceptanceAndFlowStart(payload)
proxy?.$modal.msgSuccess('已提交并发起流程')
proxy?.$tab.closePage(proxy.$route)
proxy?.$router.go(-1)
}
} finally {
buttonLoading.value = false
}
})
}
const submitCallback = async () => {
await proxy?.$tab.closePage(proxy.$route)
proxy?.$router.go(-1)
}
const approvalVerifyOpen = async () => {
await submitVerifyRef.value?.openDialog(routeParams.taskId as any)
}
const submitAndStartFlow = async () => {
formRef.value?.validate(async (valid: boolean) => {
if (!valid) return
buttonLoading.value = true
try {
const payload: ProjectAcceptanceForm = { ...form.value, flowCode: 'OAPA', variables: {}, bizExt: { businessCode: form.value.acceptanceCode } } as any
await submitProjectAcceptanceAndFlowStart(payload)
proxy?.$modal.msgSuccess('已提交并发起流程')
} finally {
buttonLoading.value = false
}
})
}
const handleApprovalRecord = () => {
if (form.value.acceptanceId) {
approvalRecordRef.value?.init(form.value.acceptanceId)
}
}
const formDisabled = computed(() => {
const s = form.value.flowStatus as any
const byStatus = !!s && s !== '草稿' && s !== '驳回'
return pageTypeForButton.value === 'view' || pageTypeForButton.value === 'approval' || byStatus
})
const generateAcceptanceCode = async () => {
//
if (formDisabled.value) return
if (isCodeGenerated.value) return
try {
const params = { codeRuleCode: '1008' } as any
const res = await getRuleGenerateCode(params)
// msg//
form.value.acceptanceCode = res?.msg as any
isCodeGenerated.value = !!form.value.acceptanceCode
if (form.value.acceptanceCode) {
proxy?.$modal.msgSuccess('验收确认编号生成成功')
} else {
proxy?.$modal.msgError('验收确认编号生成失败')
}
} catch (error) {
console.error('生成验收确认编号失败:', error)
proxy?.$modal.msgError('生成验收确认编号失败')
}
}
onMounted(async () => {
// ID使 path :acceptanceId使 query.id
routeParams.id = (route.params.acceptanceId || route.query.id) as any
routeParams.type = (route.query.type as any) || (routeParams.id && Number(routeParams.id) !== 0 ? 'update' : 'add')
routeParams.taskId = route.query.taskId as any
//
await preloadProjectList()
//
await loadDetail()
//
if (routeParams.type === 'add') {
const d = new Date()
const s = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`
form.value.acceptanceDate = s as any
}
})
</script>

@ -0,0 +1,325 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width='100px'>
<el-form-item label="项目号" prop="projectCode">
<el-input v-model="queryParams.projectCode" placeholder="请输入项目号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="验收日期" prop="acceptanceDate">
<el-date-picker
v-model="acceptanceDateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
clearable
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"></el-button>
<el-button icon="Refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['oa/erp:projectAcceptance:add']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['oa/erp:projectAcceptance:edit']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['oa/erp:projectAcceptance:remove']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['oa/erp:projectAcceptance:export']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" :columns="columns" :search="true" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="projectAcceptanceList" @selection-change="handleSelectionChange">
<el-table-column type="index" width="55" align="center" v-if="columns[0].visible" />
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="验收确认编号" align="center" prop="acceptanceCode" v-if="columns[1].visible"/>
<el-table-column label="项目号" align="center" prop="projectCode" v-if="columns[2].visible"/>
<el-table-column label="项目名称" align="center" prop="projectName" v-if="columns[3].visible"/>
<el-table-column label="项目经理" align="center" prop="projectManagerName" v-if="columns[4].visible">
<template #default="scope">
<span>{{ scope.row.projectManagerName }}</span>
</template>
</el-table-column>
<el-table-column label="验收日期" align="center" prop="acceptanceDate" width="180" v-if="columns[5].visible">
<template #default="scope">
<span>{{ parseTime(scope.row.acceptanceDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="验收单附件" align="center" v-if="columns[6].visible">
<template #default="scope">
<el-button v-if="scope.row.ossId" link type="primary" icon="Download" @click="downloadOss(scope.row.ossId)"></el-button>
<span v-else style="color:#999">暂无附件</span>
</template>
</el-table-column>
<el-table-column label="部门负责人" align="center" prop="chargeName" v-if="columns[7].visible"/>
<el-table-column label="分管副总" align="center" prop="deputyName" v-if="columns[8].visible"/>
<el-table-column label="备注" align="center" prop="remark" v-if="columns[9].visible"/>
<el-table-column label="流程状态" align="center" v-if="columns[10].visible">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.flowStatus || 'draft'" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="120" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top" v-if="!scope.row.flowStatus || scope.row.flowStatus === 'draft' || scope.row.flowStatus === '草稿' || scope.row.flowStatus === 'back' || scope.row.flowStatus === '驳回'">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['oa/erp:projectAcceptance:edit']"></el-button>
</el-tooltip>
<el-tooltip content="查看" placement="top" v-if="scope.row.flowStatus && scope.row.flowStatus !== 'draft' && scope.row.flowStatus !== '草稿' && scope.row.flowStatus !== 'back' && scope.row.flowStatus !== '驳回'">
<el-button link type="info" icon="DocumentChecked" @click="handleView(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['oa/erp:projectAcceptance:remove']"></el-button>
</el-tooltip>
<el-tooltip content="审批记录" placement="top" v-if="scope.row.acceptanceId">
<el-button link type="warning" icon="Histogram" @click="handleApprovalRecord(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 对话框逻辑已切换为编辑页流程
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>...</el-dialog>
-->
<ApprovalRecord ref="approvalRecordRef" />
</div>
</template>
<script setup name="ProjectAcceptance" lang="ts">
import { listProjectAcceptance, getProjectAcceptance, delProjectAcceptance, addProjectAcceptance, updateProjectAcceptance, submitProjectAcceptanceAndFlowStart, prepareProjectAcceptanceByProjectId } from '@/api/oa/erp/projectAcceptance';
import { ProjectAcceptanceVO, ProjectAcceptanceQuery, ProjectAcceptanceForm } from '@/api/oa/erp/projectAcceptance/types';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import { useRouter, useRoute } from 'vue-router';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const router = useRouter();
const route = useRoute();
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
const projectAcceptanceList = ref<ProjectAcceptanceVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const selectedRow = ref<ProjectAcceptanceVO | null>(null);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const projectAcceptanceFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
//
const columns = ref<FieldOption[]>([
{ key: 0, label: `序号`, visible: true },
{ key: 1, label: `验收确认编号`, visible: true },
{ key: 2, label: `项目号`, visible: true },
{ key: 3, label: `项目名称`, visible: true },
{ key: 4, label: `项目经理`, visible: true },
{ key: 5, label: `验收日期`, visible: true },
{ key: 6, label: `验收单附件`, visible: true },
{ key: 7, label: `部门负责人`, visible: true },
{ key: 8, label: `分管副总`, visible: true },
{ key: 9, label: `备注`, visible: true },
{ key: 10, label: `流程状态`, visible: true },
]);
const initFormData: ProjectAcceptanceForm = {
acceptanceId: undefined,
projectId: undefined,
projectCode: undefined,
projectName: undefined,
managerId: undefined,
acceptanceDate: undefined,
ossId: undefined,
chargeId: undefined,
deputyId: undefined,
remark: undefined,
}
const data = reactive<PageData<ProjectAcceptanceForm, ProjectAcceptanceQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
projectId: undefined,
projectCode: undefined,
projectName: undefined,
managerId: undefined,
acceptanceDate: undefined,
ossId: undefined,
chargeId: undefined,
deputyId: undefined,
params: {
}
},
rules: {
projectCode: [{ required: true, message: '项目号不能为空', trigger: 'blur' }],
projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
acceptanceDate: [{ required: true, message: '验收日期不能为空', trigger: 'change' }]
}
});
const { queryParams, form, rules } = toRefs(data);
const acceptanceDateRange = ref<string[]>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
/** 查询项目验收确认列表 */
const getList = async () => {
loading.value = true;
const res = await listProjectAcceptance(queryParams.value);
projectAcceptanceList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
// reset();
// dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
// form.value = {...initFormData};
// projectAcceptanceFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
if (acceptanceDateRange.value && acceptanceDateRange.value.length === 2) {
queryParams.value.params.beginTime = acceptanceDateRange.value[0];
queryParams.value.params.endTime = acceptanceDateRange.value[1];
} else {
if (queryParams.value.params) {
queryParams.value.params.beginTime = undefined as any;
queryParams.value.params.endTime = undefined as any;
}
}
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: ProjectAcceptanceVO[]) => {
ids.value = selection.map(item => item.acceptanceId);
single.value = selection.length != 1;
multiple.value = !selection.length;
selectedRow.value = selection.length === 1 ? selection[0] : null;
}
/** 新增按钮操作 */
const handleAdd = () => {
//
proxy?.$tab.closePage(route as any);
router.push({
path: '/project/projectAcceptance/edit',
query: {
type: 'add'
}
});
}
/** 修改按钮操作 */
const handleUpdate = async (row?: ProjectAcceptanceVO) => {
const target = row || (selectedRow.value as any);
if (!target) return;
// null'draft''稿''back'''
const canEdit = !target.flowStatus || target.flowStatus === 'draft' || target.flowStatus === '草稿' || target.flowStatus === 'back' || target.flowStatus === '驳回';
if (!canEdit) {
proxy?.$modal.msgWarning('当前记录已进入审批或已完成,禁止修改');
return;
}
const pid = target.projectId ?? '';
proxy?.$tab.closePage(route as any);
router.push({
path: '/project/projectAcceptance/edit',
query: {
id: target.acceptanceId,
type: 'update',
projectId: pid
}
});
}
const handleView = (row: ProjectAcceptanceVO) => {
proxy?.$tab.closePage(route as any);
router.push({
path: '/project/projectAcceptance/edit',
query: {
id: row.acceptanceId,
type: 'view'
}
});
}
/** 提交按钮 */
// const submitForm = () => { /* */ }
// const submitAndStartFlow = async () => { /* */ }
/** 删除按钮操作 */
const handleDelete = async (row?: ProjectAcceptanceVO) => {
const _acceptanceIds = row?.acceptanceId || ids.value;
await proxy?.$modal.confirm('是否确认删除项目验收确认编号为"' + _acceptanceIds + '"的数据项?').finally(() => loading.value = false);
await delProjectAcceptance(_acceptanceIds);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('oa/erp/projectAcceptance/export', {
...queryParams.value
}, `projectAcceptance_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
const downloadOss = (ossId: string | number) => {
proxy?.$download.oss(ossId as any);
};
const handleApprovalRecord = (row: ProjectAcceptanceVO) => {
approvalRecordRef.value?.init(row.acceptanceId as any);
};
</script>

@ -0,0 +1,314 @@
<template>
<div class="p-2">
<el-card shadow="never" style="margin-top: 0">
<approvalButton
@submitForm="submitForm"
@approvalVerifyOpen="approvalVerifyOpen"
@handleApprovalRecord="handleApprovalRecord"
:buttonLoading="buttonLoading"
:id="form.receivingId"
:status="approvalStatus"
:pageType="pageTypeForButton"
:mode="false"
/>
</el-card>
<el-card shadow="never" style="margin-top: 0">
<el-form ref="formRef" :model="form" :disabled="formDisabled" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="收货确认编号" prop="receivingCode">
<el-input v-model="form.receivingCode" placeholder="自动生成" disabled />
<el-button class="ml-2" type="primary" @click="generateReceivingCode" :disabled="formDisabled || isCodeGenerated">生成编号</el-button>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-select v-model="selectedProjectId" filterable clearable placeholder="请选择项目" :disabled="routeParams.type !== 'add'" :remote-method="remoteSearchProject" @change="onProjectChange">
<el-option v-for="item in projectOptions" :key="item.projectId" :label="item.projectName + ' / ' + item.projectCode" :value="item.projectId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="form.projectCode" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目经理" prop="managerId">
<el-input v-model="form.projectManagerName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="到货日期" prop="arrivalDate">
<el-date-picker v-model="form.arrivalDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择到货日期" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="收货单附件" prop="ossId">
<FileUpload v-model="ossIdString" :limit="5" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门负责人" prop="chargeId">
<el-input v-model="form.chargeName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="分管副总" prop="deputyId">
<el-input v-model="form.deputyName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="确认该项目达到客户收货节点,可以进行发货款收取(如设计)。" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<ApprovalRecord ref="approvalRecordRef" />
<SubmitVerify ref="submitVerifyRef" @submit-callback="submitCallback" />
</div>
</template>
<script setup lang="ts">
import ApprovalButton from '@/components/Process/approvalButton.vue'
import ApprovalRecord from '@/components/Process/approvalRecord.vue'
import FileUpload from '@/components/FileUpload/index.vue'
import { useRoute } from 'vue-router'
import { useUserStore } from '@/store/modules/user'
import { getErpProjectInfoList } from '@/api/oa/erp/projectInfo'
import { getProjectReceiving, addProjectReceiving, updateProjectReceiving, submitProjectReceivingAndFlowStart, prepareProjectReceivingByProjectId } from '@/api/oa/erp/projectReceiving'
import { getRuleGenerateCode } from '@/api/system/codeRule'
import type { ProjectReceivingForm } from '@/api/oa/erp/projectReceiving/types'
import SubmitVerify from '@/components/Process/submitVerify.vue'
const { proxy } = getCurrentInstance() as any
const route = useRoute()
// path :receivingId query id
const routeParams = reactive<{ id?: string | number; type?: string; taskId?: string | number }>({
id: (route.params.receivingId || route.query.id) as any,
type: (route.query.type as any) || 'add',
taskId: route.query.taskId as any
})
const userStore = useUserStore()
const isSuperAdmin = computed(() => {
const roles = userStore.roles || []
return roles.includes('admin') || roles.includes('superadmin')
})
const buttonLoading = ref(false)
const formRef = ref<ElFormInstance>()
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>()
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>()
const projectOptions = ref<any[]>([])
const selectedProjectId = ref<any>()
const initFormData: ProjectReceivingForm = {
receivingId: undefined,
projectId: undefined,
projectCode: undefined,
projectName: undefined,
managerId: undefined,
projectManagerName: undefined,
arrivalDate: undefined,
ossId: undefined,
chargeId: undefined,
chargeName: undefined as any,
deputyId: undefined,
deputyName: undefined as any,
remark: undefined,
flowCode: 'OAPR',
variables: {},
bizExt: {}
}
const form = ref<ProjectReceivingForm>({ ...initFormData })
const isCodeGenerated = ref(false)
//
const approvalStatus = computed(() => {
const s = form.value.flowStatus as any
if (!s || s === '草稿') return 'draft'
if (s === '审批中') return 'waiting'
if (s === '驳回') return 'back'
if (s === '撤销') return 'cancel'
return s
})
const pageTypeForButton = computed(() => {
if (routeParams.taskId) {
return 'approval'
}
return routeParams.type
})
const rules = {
projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
projectCode: [{ required: true, message: '项目号不能为空', trigger: 'blur' }],
arrivalDate: [{ required: true, message: '到货日期不能为空', trigger: 'change' }]
} as any
const ossIdString = computed({
get() {
const v = form.value.ossId as any
return v === undefined || v === null ? '' : String(v)
},
set(val: string) {
form.value.ossId = val || undefined as any
}
})
const remoteSearchProject = async (query: string) => {
const res = await getErpProjectInfoList({ projectName: query })
projectOptions.value = res.data || []
}
const preloadProjectList = async () => {
const res = await getErpProjectInfoList({})
projectOptions.value = res.data || []
}
const setNowIfAdd = () => {
const d = new Date()
const s = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`
form.value.arrivalDate = s as any
}
const onProjectChange = async (val: any) => {
if (!val) return
const { data } = await prepareProjectReceivingByProjectId(val)
Object.assign(form.value, data)
form.value.projectId = val as any
applyLeaderFromManager()
}
const applyLeaderFromManager = () => {
}
const loadDetail = async () => {
if (!routeParams.id || Number(routeParams.id) === 0) return
const res = await getProjectReceiving(routeParams.id as any)
Object.assign(form.value, res.data)
selectedProjectId.value = form.value.projectId
isCodeGenerated.value = !!form.value.receivingCode
}
const submitForm = (status: string, mode: boolean) => {
formRef.value?.validate(async (valid: boolean) => {
if (!valid) return
//
if (!form.value.managerId) {
proxy?.$modal.msgError('请先选择项目')
return
}
if (!isSuperAdmin.value && userStore.userId !== form.value.managerId) {
proxy?.$modal.msgError('只有项目经理才能提交或暂存项目收货确认')
return
}
buttonLoading.value = true
try {
if (status === 'draft') {
// draft
form.value.flowStatus = 'draft' as any
form.value.receivingStatus = '1' //
if (form.value.receivingId) {
await updateProjectReceiving(form.value)
} else {
await addProjectReceiving(form.value)
}
proxy?.$modal.msgSuccess('暂存成功')
proxy?.$tab.closePage(proxy.$route)
proxy?.$router.go(-1)
} else {
const variables: any = { managerId: form.value.managerId, managerName: form.value.projectManagerName }
const payload: ProjectReceivingForm = { ...form.value, flowCode: 'OAPR', variables, bizExt: { businessCode: form.value.receivingCode } } as any
await submitProjectReceivingAndFlowStart(payload)
proxy?.$modal.msgSuccess('已提交并发起流程')
proxy?.$tab.closePage(proxy.$route)
proxy?.$router.go(-1)
}
} finally {
buttonLoading.value = false
}
})
}
const submitCallback = async () => {
await proxy?.$tab.closePage(proxy.$route)
proxy?.$router.go(-1)
}
const approvalVerifyOpen = async () => {
await submitVerifyRef.value?.openDialog(routeParams.taskId as any)
}
const submitAndStartFlow = async () => {
formRef.value?.validate(async (valid: boolean) => {
if (!valid) return
buttonLoading.value = true
try {
const payload: ProjectReceivingForm = { ...form.value, flowCode: 'OAPR', variables: {}, bizExt: { businessCode: form.value.receivingCode } } as any
await submitProjectReceivingAndFlowStart(payload)
proxy?.$modal.msgSuccess('已提交并发起流程')
} finally {
buttonLoading.value = false
}
})
}
const handleApprovalRecord = () => {
if (form.value.receivingId) {
approvalRecordRef.value?.init(form.value.receivingId)
}
}
const formDisabled = computed(() => {
const s = form.value.flowStatus as any
const byStatus = !!s && s !== '草稿' && s !== '驳回'
return pageTypeForButton.value === 'view' || pageTypeForButton.value === 'approval' || byStatus
})
const generateReceivingCode = async () => {
//
if (formDisabled.value) return
if (isCodeGenerated.value) return
try {
const params = { codeRuleCode: '1009' } as any
const res = await getRuleGenerateCode(params)
// msg//
form.value.receivingCode = res?.msg as any
isCodeGenerated.value = !!form.value.receivingCode
if (form.value.receivingCode) {
proxy?.$modal.msgSuccess('收货确认编号生成成功')
} else {
proxy?.$modal.msgError('收货确认编号生成失败')
}
} catch (error) {
console.error('生成收货确认编号失败:', error)
proxy?.$modal.msgError('生成收货确认编号失败')
}
}
onMounted(async () => {
// ID使 path :receivingId使 query.id
routeParams.id = (route.params.receivingId || route.query.id) as any
routeParams.type = (route.query.type as any) || (routeParams.id && Number(routeParams.id) !== 0 ? 'update' : 'add')
routeParams.taskId = route.query.taskId as any
await loadDetail()
})
onMounted(async () => {
await preloadProjectList()
if (routeParams.type === 'add') setNowIfAdd()
})
</script>

@ -0,0 +1,319 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width='100px'>
<el-form-item label="项目号" prop="projectCode">
<el-input v-model="queryParams.projectCode" placeholder="请输入项目号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="到货日期" prop="arrivalDate">
<el-date-picker
v-model="arrivalDateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
clearable
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"></el-button>
<el-button icon="Refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['oa/erp:projectReceiving:add']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['oa/erp:projectReceiving:edit']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['oa/erp:projectReceiving:remove']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['oa/erp:projectReceiving:export']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" :columns="columns" :search="true" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="projectReceivingList" @selection-change="handleSelectionChange">
<el-table-column type="index" width="55" align="center" v-if="columns[0].visible" />
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="收货确认编号" align="center" prop="receivingCode" v-if="columns[1].visible"/>
<el-table-column label="项目号" align="center" prop="projectCode" v-if="columns[2].visible"/>
<el-table-column label="项目名称" align="center" prop="projectName" v-if="columns[3].visible"/>
<el-table-column label="项目经理" align="center" prop="projectManagerName" v-if="columns[4].visible">
<template #default="scope">
<span>{{ scope.row.projectManagerName }}</span>
</template>
</el-table-column>
<el-table-column label="到货日期" align="center" prop="arrivalDate" width="180" v-if="columns[5].visible">
<template #default="scope">
<span>{{ parseTime(scope.row.arrivalDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="收货单附件" align="center" v-if="columns[6].visible">
<template #default="scope">
<el-button v-if="scope.row.ossId" link type="primary" icon="Download" @click="downloadOss(scope.row.ossId)"></el-button>
<span v-else style="color:#999">暂无附件</span>
</template>
</el-table-column>
<el-table-column label="部门负责人" align="center" prop="chargeName" v-if="columns[7].visible"/>
<el-table-column label="分管副总" align="center" prop="deputyName" v-if="columns[8].visible"/>
<el-table-column label="备注" align="center" prop="remark" v-if="columns[9].visible"/>
<el-table-column label="流程状态" align="center" v-if="columns[10].visible">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.flowStatus || 'draft'" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="120" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top" v-if="!scope.row.flowStatus || scope.row.flowStatus === 'draft' || scope.row.flowStatus === '草稿' || scope.row.flowStatus === 'back' || scope.row.flowStatus === '驳回'">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['oa/erp:projectReceiving:edit']"></el-button>
</el-tooltip>
<el-tooltip content="查看" placement="top" v-if="scope.row.flowStatus && scope.row.flowStatus !== 'draft' && scope.row.flowStatus !== '草稿' && scope.row.flowStatus !== 'back' && scope.row.flowStatus !== '驳回'">
<el-button link type="info" icon="DocumentChecked" @click="handleView(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['oa/erp:projectReceiving:remove']"></el-button>
</el-tooltip>
<el-tooltip content="审批记录" placement="top" v-if="scope.row.receivingId">
<el-button link type="warning" icon="Histogram" @click="handleApprovalRecord(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<ApprovalRecord ref="approvalRecordRef" />
</div>
</template>
<script setup name="ProjectReceiving" lang="ts">
import { listProjectReceiving, getProjectReceiving, delProjectReceiving, addProjectReceiving, updateProjectReceiving, submitProjectReceivingAndFlowStart, prepareProjectReceivingByProjectId } from '@/api/oa/erp/projectReceiving';
import { ProjectReceivingVO, ProjectReceivingQuery, ProjectReceivingForm } from '@/api/oa/erp/projectReceiving/types';
import { useRouter, useRoute } from 'vue-router';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const router = useRouter();
const route = useRoute();
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
const projectReceivingList = ref<ProjectReceivingVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const selectedRow = ref<ProjectReceivingVO | null>(null);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const projectReceivingFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
//
const columns = ref<FieldOption[]>([
{ key: 0, label: `序号`, visible: true },
{ key: 1, label: `收货确认编号`, visible: true },
{ key: 2, label: `项目号`, visible: true },
{ key: 3, label: `项目名称`, visible: true },
{ key: 4, label: `项目经理`, visible: true },
{ key: 5, label: `到货日期`, visible: true },
{ key: 6, label: `收货单附件`, visible: true },
{ key: 7, label: `部门负责人`, visible: true },
{ key: 8, label: `分管副总`, visible: true },
{ key: 9, label: `备注`, visible: true },
{ key: 10, label: `流程状态`, visible: true },
]);
const initFormData: ProjectReceivingForm = {
receivingId: undefined,
projectCode: undefined,
projectName: undefined,
managerId: undefined,
arrivalDate: undefined,
ossId: undefined,
chargeId: undefined,
deputyId: undefined,
remark: undefined,
}
const data = reactive<PageData<ProjectReceivingForm, ProjectReceivingQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
projectCode: undefined,
projectName: undefined,
managerId: undefined,
arrivalDate: undefined,
ossId: undefined,
chargeId: undefined,
deputyId: undefined,
params: {
}
},
rules: {
projectCode: [{ required: true, message: '项目号不能为空', trigger: 'blur' }],
projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
arrivalDate: [{ required: true, message: '到货日期不能为空', trigger: 'change' }]
}
});
const { queryParams, form, rules } = toRefs(data);
const arrivalDateRange = ref<string[]>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
/** 查询项目收货确认列表 */
const getList = async () => {
loading.value = true;
const res = await listProjectReceiving(queryParams.value);
projectReceivingList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
// reset();
// dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
// form.value = {...initFormData};
// projectReceivingFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
if (arrivalDateRange.value && arrivalDateRange.value.length === 2) {
queryParams.value.params.beginTime = arrivalDateRange.value[0];
queryParams.value.params.endTime = arrivalDateRange.value[1];
} else {
if (queryParams.value.params) {
queryParams.value.params.beginTime = undefined as any;
queryParams.value.params.endTime = undefined as any;
}
}
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: ProjectReceivingVO[]) => {
ids.value = selection.map(item => item.receivingId);
single.value = selection.length != 1;
multiple.value = !selection.length;
selectedRow.value = selection.length === 1 ? selection[0] : null;
}
/** 新增按钮操作 */
const handleAdd = () => {
//
proxy?.$tab.closePage(route as any);
router.push({
path: '/project/projectReceiving/edit',
query: {
type: 'add'
}
});
}
/** 修改按钮操作 */
const handleUpdate = async (row?: ProjectReceivingVO) => {
const target = row || (selectedRow.value as any);
if (!target) return;
// null'draft''稿''back'''
const canEdit = !target.flowStatus || target.flowStatus === 'draft' || target.flowStatus === '草稿' || target.flowStatus === 'back' || target.flowStatus === '驳回';
if (!canEdit) {
proxy?.$modal.msgWarning('当前记录已进入审批或已完成,禁止修改');
return;
}
const pid = target.projectId ?? '';
proxy?.$tab.closePage(route as any);
router.push({
path: '/project/projectReceiving/edit',
query: {
id: target.receivingId,
type: 'update',
projectId: pid
}
});
}
const handleView = (row: ProjectReceivingVO) => {
proxy?.$tab.closePage(route as any);
router.push({
path: '/project/projectReceiving/edit',
query: {
id: row.receivingId,
type: 'view'
}
});
}
/** 提交按钮 */
// const submitForm = () => { /* */ }
// const submitAndStartFlow = async () => { /* */ }
/** 删除按钮操作 */
const handleDelete = async (row?: ProjectReceivingVO) => {
const _receivingIds = row?.receivingId || ids.value;
await proxy?.$modal.confirm('是否确认删除项目收货确认编号为"' + _receivingIds + '"的数据项?').finally(() => loading.value = false);
await delProjectReceiving(_receivingIds);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('oa/erp/projectReceiving/export', {
...queryParams.value
}, `projectReceiving_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
const downloadOss = (ossId: string | number) => {
proxy?.$download.oss(ossId as any);
};
const handleApprovalRecord = (row: ProjectReceivingVO) => {
approvalRecordRef.value?.init(row.receivingId as any);
};
</script>
Loading…
Cancel
Save