[feat] 新增项目信息管理模块业务流程表单

This commit is contained in:
hhyykk 2024-07-07 16:02:33 +08:00
parent 14a8fc6434
commit c8df5ed632
5 changed files with 530 additions and 3 deletions

View File

@ -573,6 +573,40 @@ const remainingRouter: AppRouteRecordRaw[] = [
component: () => import('@/views/crm/product/detail/index.vue')
}
]
},
{
path: '/pms',
component: Layout,
name: 'pms',
meta: {
hidden: true
},
children: [
{
path: 'project/bpm/ProjectBpmCreate',
component: () => import('@/views/pms/project/bpm/ProjectBpmCreate.vue'),
name: 'ProjectBpmCreate',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '发起项目立项',
activeMenu: 'pms/project'
}
},
{
path: 'project/bpm/ProjectBpmDetail',
component: () => import('@/views/pms/project/bpm/ProjectBpmDetail.vue'),
name: 'ProjectBpmDetail',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '查看项目立项详情',
activeMenu: 'pms/project'
}
}
]
}
]

View File

@ -186,7 +186,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="12" v-if="formType !== 'create'">
<el-form-item label="审批状态" prop="processStatus">
<el-select
v-model="formData.processStatus"
@ -194,7 +194,7 @@
disabled
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.PROCESS_STATUS)"
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -237,7 +237,7 @@
</Dialog>
</template>
<script setup lang="ts">
import { getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { getStrDictOptions, getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { ProjectApi, ProjectVO } from '@/api/pms/project'
import * as AreaApi from '@/api/system/area'
import * as UserApi from '@/api/system/user'
@ -312,6 +312,7 @@ const formRules = reactive({
customerCompanyId: [{ required: true, message: '客户公司不能为空', trigger: 'blur' }],
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
customerPhone: [{ required: true, message: '客户电话不能为空', trigger: 'blur' }],
customerUser: [{ required: true, message: '客户联系人不能为空', trigger: 'blur' }],
})
const formRef = ref() // Ref

View File

@ -0,0 +1,357 @@
<template>
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="100px"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="跟踪编号" prop="trackingCode">
<el-input v-model="formData.trackingCode" placeholder="请输入跟踪编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目编号" prop="code">
<el-input v-model="formData.code" placeholder="请输入项目编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="类型" prop="type">
<el-select v-model="formData.type" placeholder="请选择类型">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.PROJECT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="省份" prop="provinceId">
<el-cascader
v-model="formData.provinceId"
:options="areaList"
:props="props"
class="w-1/1"
clearable
filterable
placeholder="请选择省份"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="城市" prop="cityId">
<el-cascader
v-model="formData.cityId"
:options="areaList"
:props="props"
:show-all-levels="false"
class="w-1/1"
clearable
filterable
placeholder="请选择城市"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预计金额(万)" prop="contractAmount" :label-width="102">
<el-input v-model="formData.contractAmount" placeholder="请输入预计合同金额" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户公司" prop="customerCompanyId">
<el-select
v-model="formData.customerCompanyId"
placeholder="请选择客户公司"
>
<el-option
v-for="item in customerCompanyOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户联系人" prop="customerUser">
<el-input v-model="formData.customerUser" placeholder="请输入客户联系人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户电话" prop="customerPhone">
<el-input v-model="formData.customerPhone" placeholder="请输入客户电话" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出图公司" prop="drawingCompany">
<el-input v-model="formData.drawingCompany" placeholder="请输入出图公司" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="跟踪部门" prop="trackingDepId">
<el-tree-select
v-model=formData.trackingDepId
:data="deptOptions"
:props="defaultProps"
check-strictly
filterable
clearable
:render-after-expand="false"
empty-text="加载中,请稍后"
default-expand-all
style="width: 240px"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目经理" prop="projectManagerId">
<el-select
v-model="formData.projectManagerId"
placeholder="请选择项目经理"
class="w-1/1"
>
<el-option
v-for="item in userList"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker
v-model="formData.startTime"
type="date"
value-format="x"
placeholder="选择开始时间"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="跟踪情况" prop="situation">
<el-input v-model="formData.situation" type="textarea" placeholder="请输入跟踪情况" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="落地可能性" prop="possibility">
<el-select v-model="formData.possibility" placeholder="请选择落地可能性">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.POSSIBILITY_OF_LANDING)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="漏斗预期" prop="funnelExpectation">
<el-input v-model="formData.funnelExpectation" placeholder="请输入漏斗预期" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="委托方式" prop="entrustMethod">
<el-select v-model="formData.entrustMethod" placeholder="请选择委托方式">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.ENTRUST_METHOD)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否落地" prop="confirmation">
<el-select v-model="formData.confirmation" placeholder="请选择是否落地">
<el-option
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<!-- <el-col :span="12">-->
<!-- <el-form-item label="审批状态" prop="processStatus">-->
<!-- <el-select-->
<!-- v-model="formData.processStatus"-->
<!-- placeholder="请选择审批状态"-->
<!-- disabled-->
<!-- >-->
<!-- <el-option-->
<!-- v-for="dict in getStrDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"-->
<!-- :key="dict.value"-->
<!-- :label="dict.label"-->
<!-- :value="dict.value"-->
<!-- />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<el-col :span="12" v-if="formData.confirmation === '是' || formData.confirmation === true">
<el-form-item label="落地时间" prop="endTime">
<el-date-picker
v-model="formData.endTime"
type="date"
value-format="x"
placeholder="选择落地时间"
/>
</el-form-item>
</el-col>
<el-col :span="24" v-if="formData.confirmation === '否' || formData.confirmation === false">
<el-form-item label="未落地原因" prop="reason">
<el-input v-model="formData.reason" type="textarea" placeholder="请输入未落地原因" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="评审附件" prop="reviewFileUrl">
<UploadFile v-model="formData.reviewFileUrl" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="中标附件" prop="winFileUrl">
<UploadFile v-model="formData.winFileUrl" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import {DICT_TYPE, getBoolDictOptions, getStrDictOptions} from '@/utils/dict'
import { ProjectApi, ProjectVO } from '@/api/pms/project'
import { useTagsViewStore } from '@/store/modules/tagsView'
import * as DefinitionApi from '@/api/bpm/definition'
import * as UserApi from '@/api/system/user'
import {defaultProps, handleTree} from "@/utils/tree";
import {CustomerCompanyApi, CustomerCompanyVO} from "@/api/cms/customerCompany";
import * as DeptApi from "@/api/system/dept";
import * as AreaApi from "@/api/system/area";
import {useUserStore} from "@/store/modules/user";
defineOptions({ name: 'ProjectBpmCreate' })
const message = useMessage() //
const { delView } = useTagsViewStore() //
const { push, currentRoute } = useRouter() //
const formLoading = ref(false) // 12
const props = {
checkStrictly: true,
children: 'children',
label: 'name',
value: 'id',
isLeaf: 'leaf',
emitPath: false // cascader false
}
const formData = ref({
id: undefined,
name: undefined,
code: undefined,
customerUser: undefined,
drawingCompany: undefined,
trackingDepId: undefined,
endTime: undefined,
possibility: undefined,
funnelExpectation: undefined,
entrustMethod: undefined,
reason: undefined,
processStatus: undefined,
trackingCode: undefined,
type: undefined,
customerCompanyId: undefined,
projectManagerId: undefined,
startTime: undefined,
situation: undefined,
reviewFileUrl: undefined,
confirmation: undefined,
winFileUrl: undefined,
contractAmount: undefined,
customerPhone: undefined,
provinceId: undefined,
cityId: undefined,
})
const formRules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
trackingCode: [{ required: true, message: '跟踪编号不能为空', trigger: 'blur' }],
type: [{ required: true, message: '类型不能为空', trigger: 'change' }],
provinceId: [{ required: true, message: '省份不能为空', trigger: 'change' }],
cityId: [{ required: true, message: '城市不能为空', trigger: 'change' }],
projectManagerId: [{ required: true, message: '项目经理不能为空', trigger: 'change' }],
contractAmount: [{ required: true, message: '预测金额不能为空', trigger: 'blur' }],
drawingCompany: [{ required: true, message: '出图公司不能为空', trigger: 'blur' }],
trackingDepId: [{ required: true, message: '跟踪部门不能为空', trigger: 'change' }],
possibility: [{ required: true, message: '落地可能性不能为空', trigger: 'change' }],
funnelExpectation: [{ required: true, message: '漏斗预期不能为空', trigger: 'blur' }],
entrustMethod: [{ required: true, message: '委托方式不能为空', trigger: 'change' }],
customerCompanyId: [{ required: true, message: '客户公司不能为空', trigger: 'blur' }],
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
customerPhone: [{ required: true, message: '客户电话不能为空', trigger: 'blur' }],
customerUser: [{ required: true, message: '客户联系人不能为空', trigger: 'blur' }],
})
const formRef = ref() // Ref
//
const processDefineKey = 'pms_project_init' // Key
const userList = ref<any[]>([]) //
const areaList = ref([]) //
const customerCompanyOptions = ref<CustomerCompanyVO[]>([]) //
const deptOptions = ref<any[]>([]) //
/** 提交表单 */
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
const data = { ...formData.value } as unknown as ProjectVO
await ProjectApi.createProject(data)
message.success('发起成功')
// Tab
delView(unref(currentRoute))
await push({ name: 'Project' })
} finally {
formLoading.value = false
}
}
/** 初始化 */
onMounted(async () => {
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(
undefined,
processDefineKey
)
if (!processDefinitionDetail) {
message.error('项目立项的流程模型未配置,请检查!')
return
}
//
userList.value = await UserApi.getSimpleUserList()
//
formData.value.projectManagerId = useUserStore().getUser.id
//
deptOptions.value = handleTree(await DeptApi.getSimpleDeptList())
//
areaList.value = await AreaApi.getAreaTree()
//
customerCompanyOptions.value = await CustomerCompanyApi.getCustomerCompanyList()
})
</script>

View File

@ -0,0 +1,115 @@
<template>
<ContentWrap>
<el-descriptions :column="2" border>
<el-descriptions-item label="名称" span="1">
{{ detailData.name }}
</el-descriptions-item>
<el-descriptions-item label="跟踪编号" span="1">
{{ detailData.trackingCode }}
</el-descriptions-item>
<el-descriptions-item label="项目编号" span="1">
{{ detailData.code }}
</el-descriptions-item>
<el-descriptions-item label="类型" span="1">
<dict-tag :type="DICT_TYPE.PROJECT_TYPE" :value="detailData.type" />
</el-descriptions-item>
<el-descriptions-item label="省份" span="1">
{{ detailData.province }}
</el-descriptions-item>
<el-descriptions-item label="城市" span="1">
{{ detailData.city }}
</el-descriptions-item>
<el-descriptions-item label="预计金额(万)" span="1">
{{ detailData.contractAmount }}
</el-descriptions-item>
<el-descriptions-item label="客户公司" :span="1">
{{ detailData.customerCompanyName }}
</el-descriptions-item>
<el-descriptions-item label="客户联系人" :span="1">
{{ detailData.customerUser }}
</el-descriptions-item>
<el-descriptions-item label="客户电话" span="1">
{{ detailData.customerPhone }}
</el-descriptions-item>
<el-descriptions-item label="出图公司" span="1">
{{ detailData.drawingCompany }}
</el-descriptions-item>
<el-descriptions-item label="跟踪部门" span="1">
{{ detailData.trackingDepName }}
</el-descriptions-item>
<el-descriptions-item label="项目经理" span="1">
{{ detailData.projectManagerName }}
</el-descriptions-item>
<el-descriptions-item label="开始时间" span="1">
{{ formatDate(detailData.startTime, 'YYYY-MM-DD') }}
</el-descriptions-item>
<el-descriptions-item label="跟踪情况" span="2">
{{ detailData.situation }}
</el-descriptions-item>
<el-descriptions-item label="落地可能性" span="1">
<dict-tag :type="DICT_TYPE.POSSIBILITY_OF_LANDING" :value="detailData.possibility" />
</el-descriptions-item>
<el-descriptions-item label="漏斗预期" span="1">
{{ detailData.funnelExpectation }}
</el-descriptions-item>
<el-descriptions-item label="委托方式" span="1">
<dict-tag :type="DICT_TYPE.ENTRUST_METHOD" :value="detailData.entrustMethod" />
</el-descriptions-item>
<el-descriptions-item label="是否落地" span="1">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="detailData.confirmation" />
</el-descriptions-item>
<!-- <el-descriptions-item label="审批状态" span="1">-->
<!-- <dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="detailData.processStatus" />-->
<!-- </el-descriptions-item>-->
<el-descriptions-item label="落地时间" v-if="detailData.confirmation === '是' || detailData.confirmation === true" span="1">
{{ formatDate(detailData.endTime, 'YYYY-MM-DD') }}
</el-descriptions-item>
<el-descriptions-item label="未落地原因" v-if="detailData.confirmation === '否' || detailData.confirmation === false" span="2">
{{ detailData.reason }}
</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="1" border>
<el-descriptions-item label="评审附件">
<UploadFile v-model="detailData.reviewFileUrl" :disabled="true"/>
</el-descriptions-item>
<el-descriptions-item label="中标附件">
<UploadFile v-model="detailData.winFileUrl" :disabled="true"/>
</el-descriptions-item>
</el-descriptions>
</ContentWrap>
</template>
<script lang="ts" setup>
import { DICT_TYPE } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime'
import { propTypes } from '@/utils/propTypes'
import {ProjectApi} from "@/api/pms/project";
defineOptions({ name: 'ProjectBpmDetail' })
const { query } = useRoute() //
const props = defineProps({
id: propTypes.number.def(undefined)
})
const detailLoading = ref(false) //
const detailData = ref<any>({}) //
const queryId = query.id as unknown as number // URL id
/** 获得数据 */
const getInfo = async () => {
detailLoading.value = true
try {
detailData.value = await ProjectApi.getProject(props.id || queryId)
} finally {
detailLoading.value = false
}
}
defineExpose({ open: getInfo }) // open
/** 初始化 **/
onMounted(() => {
getInfo()
})
</script>

View File

@ -103,6 +103,15 @@
>
编辑
</el-button>
<el-button
v-if="scope.row.entrustMethod === 'public_bidding' || scope.row.entrustMethod === 'invited_bidding'"
v-hasPermi="['bpm:oa-leave:query']"
link
type="primary"
@click="handleProcessDetail(scope.row)"
>
进度
</el-button>
<el-button
link
type="danger"
@ -144,6 +153,7 @@ const loading = ref(true) // 列表的加载中
const list = ref<ProjectVO[]>([]) //
const total = ref(0) //
const userOptions = ref<UserApi.UserVO[]>([]) //
const router = useRouter() //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
@ -217,6 +227,16 @@ const handleExport = async () => {
}
}
/** 审批进度 */
const handleProcessDetail = (row) => {
router.push({
name: 'BpmProcessInstanceDetail',
query: {
id: row.processInstanceId
}
})
}
/** 初始化 **/
onMounted(() => {
getList()