add 巡检

pull/853/head
wersd 2025-11-02 17:40:08 +08:00
parent 7e83fc530c
commit a7eb823208
6 changed files with 518 additions and 1 deletions

View File

@ -0,0 +1,50 @@
import request from '@/config/axios'
// 项目巡检内容 VO
export interface ProjectInspectionDataVO {
id: number // 主键ID
configType: number // 巡检配置类型
name: string // 名称
isRequired: boolean // 是否必填0-否1-是
type: number // 类型1-文本,2-数字,3-单选,4-多选,5-日期,6-图片,7-文件
options: string // 选项内容
sortOrder: number // 排序序号
status: boolean // 状态0-停用1-启用
remark: string // 备注
}
// 查询项目巡检内容分页
export const getProjectInspectionDataPage = async (params: any) => {
return await request.get({ url: `/crm/project-inspection-data/page`, params })
}
// 查询项目巡检内容详情
export const getProjectInspectionData = async (id: number) => {
return await request.get({ url: `/crm/project-inspection-data/get?id=` + id })
}
// 新增项目巡检内容
export const createProjectInspectionData = async (data: ProjectInspectionDataVO) => {
return await request.post({ url: `/crm/project-inspection-data/create`, data })
}
// 修改项目巡检内容
export const updateProjectInspectionData = async (data: ProjectInspectionDataVO) => {
return await request.put({ url: `/crm/project-inspection-data/update`, data })
}
// 删除项目巡检内容
export const deleteProjectInspectionData = async (id: number) => {
return await request.delete({ url: `/crm/project-inspection-data/delete?id=` + id })
}
// 导出项目巡检内容 Excel
export const exportProjectInspectionData = async (params) => {
return await request.download({ url: `/crm/project-inspection-data/export-excel`, params })
}
// 获取项目巡检内容列表
export const getProjectInspectionDataList = async (type: number) => {
return await request.get({ url: `/crm/project-inspection-data/list?configType=` + type })
}

View File

@ -0,0 +1,45 @@
import request from '@/config/axios'
// 项目巡检配置 VO
export interface ProjectInspectionConfigVO {
projectId: number // 项目ID
configId: number // 配置ID
configName: string // 配置名称
frequency: string // 巡检频率
managementParty: string // 管理方
contactPerson: string // 联系人
contactPhone: string // 联系电话
responsiblePersonId: string // 负责人
status: boolean // 状态0-停用1-启用
remark: string // 备注
}
// 查询项目巡检配置分页
export const getProjectInspectionConfigPage = async (params: any) => {
return await request.get({ url: `/crm/project-inspection-config/page`, params })
}
// 查询项目巡检配置详情
export const getProjectInspectionConfig = async (id: number) => {
return await request.get({ url: `/crm/project-inspection-config/get?id=` + id })
}
// 新增项目巡检配置
export const createProjectInspectionConfig = async (data: ProjectInspectionConfigVO) => {
return await request.post({ url: `/crm/project-inspection-config/create`, data })
}
// 修改项目巡检配置
export const updateProjectInspectionConfig = async (data: ProjectInspectionConfigVO) => {
return await request.put({ url: `/crm/project-inspection-config/update`, data })
}
// 删除项目巡检配置
export const deleteProjectInspectionConfig = async (id: number) => {
return await request.delete({ url: `/crm/project-inspection-config/delete?id=` + id })
}
// 导出项目巡检配置 Excel
export const exportProjectInspectionConfig = async (params: any) => {
return await request.download({ url: `/crm/project-inspection-config/export-excel`, params })
}

View File

@ -229,6 +229,8 @@ export enum DICT_TYPE {
CRM_FINANCE_INVOICE_AUXILIARY_TYPE = 'crm_finance_invoice_auxiliary_type', // CRM 票据辅助类型
CRM_FINANCE_INVOICE_WRITE_OFF_STATUS = 'crm_finance_invoice_write_off_status', // CRM 票据核销状态
CRM_PROJECT_SETTLEMENT_STATUS = 'crm_project_settlement_status', // CRM 项目结算状态
CRM_PROJECT_INSPECTION_CHECKPOINT = 'crm_project_inspection_checkpoint', // CRM 项目巡检配置
CRM_PROJECT_INSPECTION_DATA_TYPE = 'crm_project_inspection_data_type', // CRM 项目巡检内容类型
// ========== ERP - 企业资源计划模块 ==========

View File

@ -0,0 +1,333 @@
<template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-tabs v-model="activeTab" type="border-card">
<el-tab-pane v-for="tab in tabs" :key="tab.key" :label="tab.label" :name="tab.key">
<div class="tab-content">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="启用" :prop="`${tab.key}.enabled`">
<el-switch
v-model="formData[tab.key].enabled"
active-text="启用"
inactive-text="禁用"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="管理方" :prop="`${tab.key}.management`">
<el-input
v-model="formData[tab.key].management"
placeholder="请输入管理方"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系人" :prop="`${tab.key}.contact`">
<el-input
v-model="formData[tab.key].contact"
placeholder="请输入联系人"
clearable
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="联系电话" :prop="`${tab.key}.phone`">
<el-input
v-model="formData[tab.key].phone"
placeholder="请输入联系电话"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="责任人" :prop="`${tab.key}.responsiblePersonId`">
<el-select
v-model="formData[tab.key].responsiblePersonId"
placeholder="请选择责任人"
clearable
filterable
class="!w-full"
>
<el-option
v-for="item in userList"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="巡检周期" :prop="`${tab.key}.cycle`">
<Crontab
v-model="formData[tab.key].cycle"
placeholder="请输入或选择Cron表达式"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" :prop="`${tab.key}.remark`">
<el-input
v-model="formData[tab.key].remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
resize="both"
clearable
/>
</el-form-item>
</el-col>
</el-row>
</div>
</el-tab-pane>
</el-tabs>
</el-form>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { Crontab } from '@/components/Crontab'
import * as UserApi from '@/api/system/user'
import * as ProjectInspectionConfigApi from '@/api/crm/project/inspection'
import * as ProjectInspectionCheckpointApi from '@/api/crm/project/inspection/checkpoint'
defineProps<{
disabled?: boolean
}>()
const emit = defineEmits<{
submit: [data: any]
}>()
const formLoading = ref(false)
const activeTab = ref('')
const formRef = ref()
const userList = ref<UserApi.UserVO[]>([]) //
// tabs
const tabs = ref<Array<{ key: string; label: string; id: number }>>([])
//
const formData = ref<Record<string, any>>({})
//
const formRules = ref({})
//
const loadCheckpointList = async () => {
try {
const response = await ProjectInspectionCheckpointApi.getProjectInspectionCheckpointSimpleList()
const checkpointList = response.data || []
// sort
tabs.value = checkpointList
.slice() //
.sort((a, b) => (a.sort || 0) - (b.sort || 0)) // sort
.map((item: ProjectInspectionCheckpointApi.ProjectInspectionCheckpointVO) => ({
key: String(item.id), // 使 id key
label: item.configName, // 使 configName
id: item.id
}))
//
if (tabs.value.length > 0) {
activeTab.value = tabs.value[0].key
}
//
initFormData()
} catch (error) {
console.error('加载检查点列表失败:', error)
ElMessage.error('加载检查点列表失败')
}
}
// -
const initFormData = () => {
tabs.value.forEach(tab => {
formData.value[tab.key] = {
enabled: false,
management: '', //
contact: '', //
phone: '', //
responsiblePersonId: undefined, // ID
cycle: '0 0 0 * * ?', // cron
remark: '' //
}
})
}
//
const resetForm = () => {
formRef.value?.resetFields()
initFormData()
}
/** 打开表单并加载数据 */
const open = async (projectId?: number) => {
resetForm()
formLoading.value = true
try {
//
await loadCheckpointList()
})
}
} catch (error) {
console.error('加载配置数据失败:', error)
ElMessage.error('加载配置数据失败')
} finally {
formLoading.value = false
}
}
//
const submitForm = async () => {
try {
//
const valid = await formRef.value?.validate()
if (!valid) {
return
}
//
const submitData: any = {}
tabs.value.forEach(tab => {
if (formData.value[tab.key]?.enabled) {
submitData[tab.key] = {
enabled: formData.value[tab.key].enabled,
management: formData.value[tab.key].management,
contact: formData.value[tab.key].contact,
phone: formData.value[tab.key].phone,
responsiblePersonId: formData.value[tab.key].responsiblePersonId,
cycle: formData.value[tab.key].cycle,
remark: formData.value[tab.key].remark
}
}
})
formLoading.value = true
emit('submit', submitData)
ElMessage.success('提交成功')
} catch (error) {
console.error('表单验证失败:', error)
} finally {
formLoading.value = false
}
}
/** 初始化,加载用户列表和检查点列表 */
onMounted(async () => {
userList.value = await UserApi.getSimpleUserList()
await loadCheckpointList()
})
//
defineExpose({
open,
submitForm,
resetForm,
formData,
formRules,
formRef
})
</script>
<style scoped lang="scss">
.tab-content {
padding: 20px;
.el-form-item {
margin-bottom: 22px;
}
}
//
:deep(.el-tabs__header) {
margin-bottom: 20px;
}
:deep(.el-tabs__item) {
font-size: 14px;
font-weight: 500;
padding: 12px 20px;
color: #606266;
border-radius: 6px 6px 0 0;
transition: all 0.3s ease;
&:hover {
color: #409eff;
background-color: #f5f7fa;
}
&.is-active {
color: #409eff;
font-weight: 600;
background-color: #fff;
}
}
:deep(.el-tabs__item.is-active::before) {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background-color: #409eff;
}
:deep(.el-tabs__nav-wrap::after) {
background-color: #e4e7ed;
}
:deep(.el-tabs--border-card > .el-tabs__header .el-tabs__item) {
&.is-active {
color: #409eff;
background-color: #fff;
border: 1px solid #e4e7ed;
border-bottom: 2px solid #409eff;
}
&:not(.is-active) {
border: 1px solid transparent;
border-bottom: 1px solid #e4e7ed;
background-color: #f5f7fa;
&:hover {
background-color: #ecf5ff;
border-color: #b3d8ff;
color: #409eff;
}
}
}
:deep(.el-tabs__content) {
min-height: 300px;
padding: 20px;
background-color: #fff;
}
:deep(.el-tabs--border-card) {
border: 1px solid #e4e7ed;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
</style>

View File

@ -100,8 +100,16 @@
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.projectStatus" />
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" align="center" min-width="120px">
<el-table-column label="操作" fixed="right" align="center" min-width="180px">
<template #default="scope">
<!-- <el-button
link
type="warning"
@click="openConfigDialog(scope.row.id)"
v-hasPermi="['crm:project:update']"
>
配置
</el-button> -->
<el-button
link
type="primary"
@ -132,6 +140,21 @@
<!-- 表单弹窗添加/修改 -->
<ProjectForm ref="formRef" @success="getList" />
<!-- 配置表单弹窗 -->
<!-- <Dialog
v-model="configDialogVisible"
title="巡检配置"
width="40%"
>
<ProjectInspectConfigForm ref="configFormRef" />
<template #footer>
<el-button type="primary" @click="handleConfigSubmit" :loading="configSubmitting">
提交
</el-button>
<el-button @click="configDialogVisible = false">取消</el-button>
</template>
</Dialog> -->
</template>
<script setup lang="ts">
@ -140,6 +163,7 @@ import download from '@/utils/download'
import * as ProjectApi from '@/api/crm/project'
import * as DeptApi from '@/api/system/dept'
import ProjectForm from './ProjectForm.vue'
import ProjectInspectConfigForm from './component/ProjectInspectConfigForm.vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
/** CRM 项目信息 列表 */
@ -166,6 +190,12 @@ const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) //
const deptList = ref<DeptApi.DeptVO[]>([])
// //
// const configDialogVisible = ref(false) //
// const configFormRef = ref() //
// const currentProjectId = ref<number>() // ID
// const configSubmitting = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
@ -245,6 +275,62 @@ const calculateCostPerPile = (row) => {
return parseFloat(cost); // 0
};
// /** */
// const openConfigDialog = async (projectId: number) => {
// currentProjectId.value = projectId
// configDialogVisible.value = true
// //
// await nextTick()
// configFormRef.value?.open(projectId)
// }
// /** */
// const handleConfigSubmit = async () => {
// try {
// //
// const formData = configFormRef.value?.formData || {}
// //
// const enabledKeys = Object.keys(formData).filter(key => formData[key]?.enabled)
// for (const key of enabledKeys) {
// if (!formData[key]?.responsiblePersonId) {
// message.error(`${key}`)
// return
// }
// if (!formData[key]?.cycle) {
// message.error(`${key}`)
// return
// }
// }
// //
// const submitData: any = {}
// enabledKeys.forEach(key => {
// submitData[key] = {
// enabled: formData[key].enabled,
// management: formData[key].management,
// contact: formData[key].contact,
// phone: formData[key].phone,
// responsiblePersonId: formData[key].responsiblePersonId,
// cycle: formData[key].cycle,
// remark: formData[key].remark
// }
// })
// configSubmitting.value = true
// // TODO: API
// // await ProjectApi.saveInspectConfig(currentProjectId.value, submitData)
// message.success('')
// configDialogVisible.value = false
// } catch (error) {
// console.error(':', error)
// message.error('')
// } finally {
// configSubmitting.value = false
// }
// }
/** 初始化 **/
onMounted(async () => {
await getList()

View File

@ -17,6 +17,7 @@
class="!w-240px"
placeholder="请选择客户"
@keyup.enter="handleQuery"
filterable
>
<el-option
v-for="item in customerList"