1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336 |
- <template>
- <ContentWrap v-loading="formLoading">
- <el-form
- ref="formRef"
- :model="formData"
- :rules="formRules"
- label-width="100px"
- v-loading="formLoading"
- >
- <el-row>
- <el-col :span="12">
- <el-form-item label="合同名称" prop="contractName">
- <el-select v-model="formData.contractId" placeholder="请选择" @change="getProjectInfo" disabled>
- <el-option
- v-for="item in projectList"
- :key="item.id"
- :label="item.contractName"
- :value="item.id"
- clearable
- />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="合同编号" prop="contractCode">
- <el-input v-model="formData.contractCode" placeholder="请输入合同编号" disabled/>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row>
- <el-col :span="12">
- <el-form-item label="客户名称" prop="manufacturerId" >
- <el-select
- clearable
- @clear="zzClear"
- v-model="formData.manufacturerId"
- :placeholder="t('deviceForm.mfgHolder')"
- disabled
- >
- <el-option :label="formData.manufactureName" :value="formData.manufacturerId" />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <!-- <el-form-item label="结算方式" prop="payment" >
- <el-input v-model="formData.payment" placeholder="请输入结算方式" disabled/>
- </el-form-item> -->
- <el-form-item :label="t('project.payment')" prop="payment">
- <el-select v-model="formData.payment" placeholder="请选择" disabled>
- <el-option
- v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_SETTLEMENT)"
- :key="dict.id"
- :label="dict.label"
- :value="dict.value"
- />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row>
- <el-col :span="12">
- <el-form-item label="总数" prop="workloadTotal">
- <el-input v-model="formData.workloadTotal" placeholder="请输入工作量总数" disabled/>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="已完成" prop="workloadFinish">
- <el-input v-model="formData.workloadFinish" placeholder="已完成工作量" disabled/>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row>
- <el-col :span="12">
- <el-form-item label="开始时间" prop="startTime">
- <el-date-picker
- style="width: 150%"
- v-model="formData.startTime"
- type="date"
- value-format="x"
- placeholder="选择开始时间"
- disabled
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="完成时间" prop="endTime">
- <el-date-picker
- style="width: 150%"
- v-model="formData.endTime"
- type="date"
- value-format="x"
- placeholder="选择完成时间"
- disabled
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-form-item label="备注" prop="remark">
- <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" disabled/>
- </el-form-item>
- </el-form>
- </ContentWrap>
- <ContentWrap>
- <div class="content">
- <div class="toolbar">
- <div class="actions">
- <!--
- <el-button type="primary" @click="addNewRow" icon="plus" >
- 新增行
- </el-button>
- <el-button type="success" @click="saveAll" :disabled="editingRowsCount === 0" icon="check">
- 保存所有更改
- </el-button> -->
- </div>
- </div>
- <div class="table-container">
- <el-table :data="tableData" :row-class-name="rowClassName" empty-text="暂无数据">
- <el-table-column prop="wellName" label="井号">
- <template #default="{ row }">
- <span v-if="!row.editing">{{ row.wellName }}</span>
- <div v-else>
- <el-input v-model="row.editData.wellName" class="edit-input" placeholder="请输入井号" />
- <div v-if="row.errors.wellName" class="error-message">{{ row.errors.wellName }}</div>
- </div>
- </template>
- </el-table-column>
- <!-- 修改井型/井别列 -->
- <el-table-column prop="wellType" label="井型">
- <template #default="{ row }">
- <span v-if="!row.editing">{{ getWellTypeLabel(row.wellType) }}</span>
- <div v-else>
- <el-select v-model="row.editData.wellType" placeholder="请选择井型" clearable>
- <el-option
- v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_TYPE)"
- :key="dict.value"
- :label="dict.label"
- :value="dict.value"
- />
- </el-select>
- <div v-if="row.errors.wellType" class="error-message">{{ row.errors.wellType }}</div>
- </div>
- </template>
- </el-table-column>
- <!-- 新增井别列 -->
- <el-table-column prop="wellCategory" label="井别">
- <template #default="{ row }">
- <span v-if="!row.editing">{{ getWellCategoryLabel(row.wellCategory) }}</span>
- <div v-else>
- <el-select v-model="row.editData.wellCategory" placeholder="请选择井别" clearable>
- <el-option
- v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_CATEGORY)"
- :key="dict.value"
- :label="dict.label"
- :value="dict.value"
- />
- </el-select>
- <div v-if="row.errors.wellCategory" class="error-message">{{ row.errors.wellCategory }}</div>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="location" label="施工地点" >
- <template #default="{ row }">
- <span v-if="!row.editing">{{ row.location }}</span>
- <div v-else>
- <el-input v-model="row.editData.location" class="edit-input" placeholder="请输入施工地点" />
- <div v-if="row.errors.location" class="error-message">{{ row.errors.location }}</div>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="technique" label="施工工艺">
- <template #default="{ row }">
- <span v-if="!row.editing">{{ getTechniqueLabel(row.technique) }}</span>
- <div v-else>
- <el-select v-model="row.editData.technique" placeholder="请选择施工工艺" clearable>
- <el-option
- v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_TECHNOLOGY)"
- :key="dict.value"
- :label="dict.label"
- :value="dict.value"
- />
- </el-select>
- <div v-if="row.errors.technique" class="error-message">{{ row.errors.technique }}</div>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="workloadDesign" label="设计工作量">
- <template #default="{ row }">
- <span v-if="!row.editing">{{ row.workloadDesign }}</span>
- <div v-else>
- <el-input v-model="row.editData.workloadDesign" class="edit-input" placeholder="请输入设计工作量" />
- <div v-if="row.errors.workloadDesign" class="error-message">{{ row.errors.workloadDesign }}</div>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="deptIds" label="施工队伍" >
- <template #default="{ row }">
- <span v-if="!row.editing">
- <el-tooltip
- effect="dark"
- :content="getAllDeptNames(row.deptIds)"
- placement="top"
- :disabled="!row.deptIds || row.deptIds.length <= 1"
- >
- <span class="dept-names">
- {{ getDeptNames(row.deptIds) }}
- </span>
- </el-tooltip>
- </span>
- <div v-else>
- <el-tree-select
- multiple
- v-model="row.editData.deptIds"
- :data="deptList"
- :props="defaultProps"
- check-strictly
- node-key="id"
- filterable
- placeholder="请选择施工队伍"
- clearable
- />
- <div v-if="row.errors.deptIds" class="error-message">{{ row.errors.deptIds }}</div>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="deviceIds" label="施工设备" >
- <template #default="{ row }">
- <el-tooltip
- :content="getAllDeviceNames(row.deviceIds)"
- placement="top"
- :disabled="row.deviceIds && row.deviceIds.length <= 1"
- >
- <span v-if="!row.editing" class="device-names">
- {{ getDeviceNames(row.deviceIds) }}
- </span>
- <div v-else>
- <el-button
- @click="openDeviceDialog(row)"
- type="primary"
- size="small"
- >
- 选择设备
- </el-button>
- <div v-if="row.errors.deviceIds" class="error-message">{{ row.errors.deviceIds }}</div>
- </div>
- </el-tooltip>
- </template>
- </el-table-column>
- <el-table-column prop="responsiblePerson" label="责任人">
- <template #default="{ row }">
- <el-tooltip
- :content="getAllResponsiblePersonNames(row.responsiblePerson)"
- placement="top"
- :disabled="!row.responsiblePerson || row.responsiblePerson.length <= 1"
- >
- <span v-if="!row.editing" class="responsible-names">
- {{ getResponsiblePersonNames(row.responsiblePerson) }}
- </span>
- <div v-else>
- <el-button
- @click="openResponsiblePersonDialog(row)"
- type="primary"
- size="small"
- >
- 选择责任人
- </el-button>
- <div v-if="row.errors.responsiblePerson" class="error-message">{{ row.errors.responsiblePerson }}</div>
- </div>
- </el-tooltip>
- </template>
- </el-table-column>
- <el-table-column prop="remark" label="备注">
- <template #default="{ row }">
- <span v-if="!row.editing">{{ row.remark }}</span>
- <div v-else>
- <el-input v-model="row.editData.remark" class="edit-input" placeholder="请输入备注" />
- <div v-if="row.errors.remark" class="error-message">{{ row.errors.remark }}</div>
- </div>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="200" fixed="right">
- <template #default="{ row, $index }">
- <div class="action-cell">
- <template v-if="!row.editing">
- <el-button size="small" type="primary" @click="editRow(row)" icon="edit">编辑</el-button>
- <el-button size="small" type="danger" @click="deleteRow($index)" icon="delete">删除</el-button>
- </template>
- <template v-else>
- <el-button size="small" type="success" @click="saveRow(row)" icon="check">保存</el-button>
- <el-button size="small" @click="cancelEdit(row)" icon="close">取消</el-button>
- </template>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- </ContentWrap>
- <ContentWrap>
- <el-form style="float: right">
- <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
- <el-button @click="close">取 消</el-button>
- </el-form>
- </ContentWrap>
- <!-- 设备选择对话框 -->
- <el-dialog
- v-model="deviceDialogVisible"
- title="选择施工设备"
- width="1000px"
- :before-close="handleDeviceDialogClose"
- class="device-select-dialog"
- >
- <!-- 父容器加text-center确保穿梭框居中,移除原text-align:left -->
- <!-- 移除text-align:left,保留inline-block让父容器text-center生效 -->
- <div class="transfer-container">
- <el-transfer
- v-model="selectedDeviceIds"
- :data="filteredDeviceList"
- :titles="['可选设备', '已选设备']"
- :props="{ key: 'id', label: 'deviceCode' }"
- filterable
- class="transfer-component"
- @change="handleTransferChange"
- >
- <template #default="{ option }">
- <!-- 无内容时禁用tooltip -->
- <el-tooltip
- effect="dark"
- placement="top"
- :content="`${option.deviceCode || ''} - ${option.deviceName || ''}`"
- :disabled="!option.deviceCode && !option.deviceName"
- transition="fade-in-linear"
- >
- <span class="transfer-option-text">
- {{ option.deviceCode }} - {{ option.deviceName }}
- </span>
- </el-tooltip>
- </template>
- </el-transfer>
- </div>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="handleDeviceDialogClose">取消</el-button>
- <el-button type="primary" @click="confirmDeviceSelection">确定</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 新增责任人选择对话框 -->
- <el-dialog
- v-model="responsiblePersonDialogVisible"
- title="选择责任人"
- width="1000px"
- :before-close="handleResponsiblePersonDialogClose"
- class="responsible-person-select-dialog"
- >
- <div class="transfer-container">
- <el-transfer
- v-model="selectedResponsiblePersonIds"
- :data="responsiblePersonList"
- :titles="['可选人员', '已选人员']"
- :props="{ key: 'id', label: 'nickname' }"
- filterable
- class="transfer-component"
- >
- <template #default="{ option }">
- <el-tooltip
- effect="dark"
- placement="top"
- :content="`${option.nickname} - ${option.deptName || '未分配部门'}`"
- >
- <span class="transfer-option-text">
- {{ option.nickname }} - {{ option.deptName || '未分配部门' }}
- </span>
- </el-tooltip>
- </template>
- </el-transfer>
- </div>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="handleResponsiblePersonDialogClose">取消</el-button>
- <el-button type="primary" @click="confirmResponsiblePersonSelection">确定</el-button>
- </span>
- </template>
- </el-dialog>
- </template>
- <script setup lang="ts">
- import { IotProjectInfoApi, IotProjectInfoVO } from '@/api/pms/iotprojectinfo'
- import {defaultProps,handleTree} from "@/utils/tree";
- import * as DeptApi from "@/api/system/dept";
- import CustomerList from '@/views/pms/device/CustomerList.vue'
- import {ref, reactive, computed, onMounted, watch} from "vue";
- import {useUserStore} from "@/store/modules/user";
- import {IotProjectTaskApi, IotProjectTaskVO} from "@/api/pms/iotprojecttask";
- import {ElMessageBox, ElMessage} from "element-plus";
- import {useTagsViewStore} from "@/store/modules/tagsView";
- import {companyLevelChildrenDepts} from "@/api/system/dept";
- import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
- import * as UserApi from "@/api/system/user";
- import {UserVO} from "@/api/system/user";
- import {DICT_TYPE, getIntDictOptions, getStrDictOptions} from "@/utils/dict";
- const { query, params, name } = useRoute() // 查询参数
- const id = params.id
- // 修改projectId获取逻辑:优先使用params.projectId,其次使用query.projectId
- const projectId = ref<string>('')
- // 获取projectId的逻辑
- if (params.projectId) {
- projectId.value = Array.isArray(params.projectId) ? params.projectId[0] : params.projectId
- } else if (query.projectId) {
- projectId.value = Array.isArray(query.projectId) ? query.projectId[0] : query.projectId as string
- }
- const { delView } = useTagsViewStore() // 视图操作
- const { currentRoute, push } = useRouter()
- const { t } = useI18n() // 国际化
- const message = useMessage() // 消息弹窗
- const deptList = ref<Tree[]>([]) // 树形结构
- const deviceList = ref<IotDeviceVO[]>([]) // 设备列表
- // 设备映射表(ID->设备对象)
- const deviceMap = ref<Record<number, IotDeviceVO>>({});
- const dialogVisible = ref(true) // 弹窗的是否展示
- const dialogTitle = ref('') // 弹窗的标题
- const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
- const taskList = ref<IotProjectTaskVO[]>([]) // 列表的数据
- const projectList = ref<IotProjectInfoVO[]>([])
- const formType = ref('') // 表单的类型:create - 新增;update - 修改
- const loading = ref(true) // 列表的加载中
- // 新增责任人相关变量
- const responsiblePersonDialogVisible = ref(false);
- const responsiblePersonList = ref([]); // 所有责任人列表
- const selectedResponsiblePersonIds = ref([]); // 选中的责任人ID
- const currentEditingRowForResponsible = ref(null); // 当前正在编辑责任人的行
- /** 项目信息 表单 */
- defineOptions({ name: 'IotProjectTaskInfo' })
- const formData = ref({
- id: undefined,
- contractId: undefined,
- contractName: undefined,
- contractCode: undefined,
- workloadTotal: undefined,
- workloadFinish: undefined,
- startTime: undefined,
- endTime: undefined,
- location: undefined,
- technique: undefined,
- payment: undefined,
- remark:undefined,
- manufactureName: undefined,
- manufacturerId: undefined,
- userName: undefined,
- userId: undefined,
- deptId: undefined, // 新增项目部门ID字段
- })
- const close = () => {
- delView(unref(currentRoute))
- push({ name: 'IotProjectTask', params:{}})
- }
- const getProjectInfo = (contractId: number) => {
- const project = projectList.value.find(item => item.id === contractId);
- if (project) {
- formData.value.contractName = project.contractName;
- formData.value.contractCode = project.contractCode;
- formData.value.payment = project.payment;
- formData.value.workloadTotal = project.workloadTotal;
- formData.value.startTime = project.startTime;
- formData.value.endTime = project.endTime;
- formData.value.remark = project.remark;
- formData.value.manufactureName = project.manufactureName;
- formData.value.manufacturerId = project.manufacturerId;
- formData.value.id = project.id;
- formData.value.deptId = project.deptId; // 保存项目部门ID
- }
- }
- const queryParams = reactive({
- projectId:undefined,
- id:undefined
- })
- const formRules = reactive({
- contractId: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
- manufacturerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
- payment: [{ required: true, message: '结算方式不能为空', trigger: 'blur' }],
- workloadTotal: [{ required: true, message: '工作量总数不能为空', trigger: 'blur' }],
- startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
- endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }],
- })
- const formRef = ref() // 表单 Ref
- const zzClear = () =>{
- formData.value.manufacturerId = undefined
- formData.value.manufactureName = undefined
- }
- // 根据部门ID获取部门名称
- const getDeptNames = (deptIds) => {
- if (!deptIds || deptIds.length === 0) return '';
- const names = [];
- const findDept = (list, id) => {
- for (const item of list) {
- if (item.id === id) {
- names.push(item.name);
- return true;
- }
- if (item.children && findDept(item.children, id)) {
- return true;
- }
- }
- return false;
- };
- deptIds.forEach(id => findDept(deptList.value, id));
- if (names.length > 1) {
- return `${names[0]}...`;
- }
- return names.join(', ');
- };
- const getAllDeptNames = (deptIds) => {
- if (!deptIds || deptIds.length === 0) return '无施工队伍';
- const names = [];
- const findDept = (list, id) => {
- for (const item of list) {
- if (item.id === id) {
- names.push(item.name);
- return true;
- }
- if (item.children && findDept(item.children, id)) {
- return true;
- }
- }
- return false;
- };
- deptIds.forEach(id => findDept(deptList.value, id));
- return names.join(', ') || '无有效施工队伍';
- };
- const getDeviceNames = (deviceIds: number[]) => {
- if (!deviceIds || deviceIds.length === 0) return '';
- // 获取所有有效设备名称
- const deviceNames = deviceIds
- .map(id => deviceMap.value[id]?.deviceCode)
- .filter(name => name !== undefined && name !== '');
- if (deviceNames.length === 0) return '';
- // 如果设备数量超过2个,显示前两个加省略号
- if (deviceNames.length > 2) {
- return `${deviceNames[0]}, ${deviceNames[1]}...`;
- }
- // 设备数量不超过2个,正常显示所有
- return deviceNames.join(', ');
- };
- const tableData = ref([
- {
- id: 1,
- deptId:'',
- wellName: '',
- wellType: '',
- wellCategory: '',
- location: '',
- technique: '',
- workloadDesign: '',
- deptIds: [],
- deviceIds: [],
- deviceCodes: '',
- remark:'',
- editData: {},
- editing:true,
- originalData: {},
- errors: {},
- projectId: ''
- },
- ]);
- // 打开责任人选择对话框
- const openResponsiblePersonDialog = async (row) => {
- if (!formData.value.deptId) {
- ElMessage.warning('请先选择合同以确定项目部门');
- return;
- }
- currentEditingRowForResponsible.value = row;
- selectedResponsiblePersonIds.value = [...(row.editData.responsiblePerson || [])];
- try {
- const params = {
- deptIds: [formData.value.deptId] // 使用当前项目所属部门的deptId
- };
- const response = await UserApi.companyDeptsEmployee(params);
- responsiblePersonList.value = response;
- responsiblePersonDialogVisible.value = true;
- } catch (error) {
- ElMessage.error('获取责任人列表失败');
- console.error('获取责任人列表失败:', error);
- }
- };
- // 确认责任人选择
- const confirmResponsiblePersonSelection = () => {
- if (currentEditingRowForResponsible.value) {
- // 更新责任人ID
- currentEditingRowForResponsible.value.editData.responsiblePerson = [...selectedResponsiblePersonIds.value];
- // 更新责任人名称显示
- const selectedPersons = responsiblePersonList.value.filter(person =>
- selectedResponsiblePersonIds.value.includes(person.id)
- );
- currentEditingRowForResponsible.value.editData.responsiblePersonNames = selectedPersons
- .map(person => person.nickname)
- .join(', ');
- }
- responsiblePersonDialogVisible.value = false;
- };
- // 关闭责任人选择对话框
- const handleResponsiblePersonDialogClose = () => {
- responsiblePersonDialogVisible.value = false;
- };
- // 根据责任人ID获取责任人名称
- const getResponsiblePersonNames = (responsiblePersonIds: number[]) => {
- if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '';
- // 获取所有有效责任人名称
- const personNames = responsiblePersonIds
- .map(id => {
- const person = responsiblePersonList.value.find(p => p.id === id);
- return person ? person.nickname : '';
- })
- .filter(name => name !== undefined && name !== '');
- if (personNames.length === 0) return '';
- // 如果责任人数量超过2个,显示前两个加省略号
- if (personNames.length > 2) {
- return `${personNames[0]}, ${personNames[1]}...`;
- }
- // 责任人数量不超过2个,正常显示所有
- return personNames.join(', ');
- };
- // 获取所有责任人名称(用于tooltip提示)
- const getAllResponsiblePersonNames = (responsiblePersonIds: number[]) => {
- if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '无责任人';
- const personNames = responsiblePersonIds
- .map(id => {
- const person = responsiblePersonList.value.find(p => p.id === id);
- return person ? person.nickname : '未知人员';
- })
- .filter(name => name !== '未知人员');
- return personNames.join(', ') || '无有效责任人';
- };
- // 设备选择相关变量
- const deviceDialogVisible = ref(false);
- const currentEditingRow = ref(null);
- const selectedDeviceIds = ref([]);
- const filteredDeviceList = ref([]);
- // 打开设备选择对话框
- const openDeviceDialog = async (row) => {
- if (!row.editData.deptIds || row.editData.deptIds.length === 0) {
- ElMessage.warning('请先选择施工队伍');
- return;
- }
- currentEditingRow.value = row;
- selectedDeviceIds.value = [...(row.editData.deviceIds || [])];
- try {
- const params = {
- deptIds: row.editData.deptIds
- };
- const data = await IotDeviceApi.getDevicesByDepts(params);
- filteredDeviceList.value = data;
- deviceDialogVisible.value = true;
- } catch (error) {
- ElMessage.error('获取设备列表失败');
- console.error('获取设备列表失败:', error);
- }
- };
- // 处理穿梭框变化
- const handleTransferChange = (value, direction, movedKeys) => {
- // 可以添加额外的处理逻辑
- };
- // 确认设备选择
- const confirmDeviceSelection = () => {
- if (currentEditingRow.value) {
- selectedDeviceIds.value.forEach(id => {
- const device = filteredDeviceList.value.find(d => d.id === id);
- if (device && !deviceMap.value[id]) {
- deviceMap.value[id] = device;
- }
- });
- // 更新设备ID
- currentEditingRow.value.editData.deviceIds = [...selectedDeviceIds.value];
- // 更新设备代码显示
- const selectedDevices = filteredDeviceList.value.filter(device =>
- selectedDeviceIds.value.includes(device.id)
- );
- currentEditingRow.value.editData.deviceCodes = selectedDevices
- .map(device => device.deviceCode)
- .join(', ');
- }
- deviceDialogVisible.value = false;
- };
- // 关闭设备选择对话框
- const handleDeviceDialogClose = () => {
- deviceDialogVisible.value = false;
- };
- // 获取井型标签的方法
- const getWellTypeLabel = (value) => {
- const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_TYPE)
- const option = options.find(opt => opt.value === value)
- return option ? option.label : value
- }
- // 获取井别标签的方法
- const getWellCategoryLabel = (value) => {
- const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_CATEGORY)
- const option = options.find(opt => opt.value === value)
- return option ? option.label : value
- }
- // 获取施工工艺标签的方法
- const getTechniqueLabel = (value) => {
- const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_TECHNOLOGY)
- const option = options.find(opt => opt.value === value)
- return option ? option.label : value
- }
- /** 打开弹窗 */
- const open = async () => {
- resetForm()
- // 修改时,设置数据
- if (id) {
- formType.value = 'update';
- formLoading.value = true
- try {
- queryParams.id = id;
- const data = await IotProjectTaskApi.getIotProjectTaskPage(queryParams);
- tableData.value = data.list
- // 1. 收集所有设备ID
- const allDeviceIds = new Set<number>();
- // 收集所有责任人ID
- const allResponsiblePersonIds = new Set<number>();
- data.list.forEach(item => {
- if (item.deviceIds?.length) {
- item.deviceIds.forEach(id => allDeviceIds.add(id));
- }
- if (item.responsiblePerson?.length) {
- item.responsiblePerson.forEach(id => allResponsiblePersonIds.add(id));
- }
- });
- // 2. 批量获取设备信息
- if (allDeviceIds.size > 0) {
- const deviceIdsArray = Array.from(allDeviceIds);
- const devices = await IotDeviceApi.getDevicesByDepts({
- deviceIds: deviceIdsArray
- });
- // 3. 更新设备列表和映射表
- deviceList.value = devices;
- deviceMap.value = {};
- devices.forEach(device => {
- deviceMap.value[device.id] = device;
- });
- }
- // 批量获取责任人信息
- if (allResponsiblePersonIds.size > 0) {
- const personIdsArray = Array.from(allResponsiblePersonIds);
- const params = {
- userIds: personIdsArray
- };
- const persons = await UserApi.companyDeptsEmployee(params);
- // 更新责任人列表
- responsiblePersonList.value = persons;
- }
- tableData.value.forEach(item=>{
- item.editData = {
- ...item,
- deviceIds: item.deviceIds ? [...item.deviceIds] : []
- };
- item.originalData={};
- item.errors={};
- })
- } finally {
- formLoading.value = false
- }
- } else {
- formType.value = 'create';
- }
- // 如果有projectId参数,自动选中对应的合同
- if (projectId.value) {
- autoSelectContract(projectId.value);
- // 自动新增一行空记录
- if (tableData.value.length === 0) {
- addNewRow();
- }
- }
- }
- defineExpose({ open }) // 提供 open 方法,用于打开弹窗
- /** 自动选择合同 */
- const autoSelectContract = async (projectId: string) => {
- // 等待项目列表加载完成
- // await waitForProjectList();
- console.log('项目id:' + projectId);
- // 查找匹配的合同
- const project = projectList.value.find(item => item.id === Number(projectId));
- if (project) {
- // 设置选中值
- formData.value.contractId = project.id;
- // 触发合同信息加载
- getProjectInfo(project.id);
- } else {
- ElMessage.warning('未找到指定的合同信息');
- }
- }
- /** 等待项目列表加载完成 */
- /* const waitForProjectList = () => {
- return new Promise(resolve => {
- const checkInterval = setInterval(() => {
- if (projectList.value.length > 0) {
- clearInterval(checkInterval);
- resolve(true);
- }
- }, 100);
- });
- } */
- /** 校验所有行数据 */
- const validateAllRows = (): boolean => {
- // 新增检查:判断任务列表是否为空
- if (!tableData.value || tableData.value.length === 0) {
- ElMessage.error('没有任务数据,无法保存');
- return false;
- }
- let allValid = true;
- tableData.value.forEach(row => {
- // 对每一行进行校验
- const rowValid = validateRow(row);
- if (!rowValid) {
- allValid = false;
- }
- // 如果是编辑中的行,需要额外检查编辑数据
- if (row.editing) {
- if (!row.editData.wellName || row.editData.wellName.trim() === '') {
- row.errors.wellName = '井号不能为空';
- allValid = false;
- }
- if (!row.editData.wellType || row.editData.wellType.trim() === '') {
- row.errors.wellType = '井型不能为空';
- allValid = false;
- }
- }
- });
- return allValid;
- };
- /** 提交表单 */
- const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
- const submitForm = async () => {
- // 校验表单
- await formRef.value.validate()
- // 校验所有行数据
- if (!validateAllRows()) {
- // ElMessage.error('请检查表格中的数据,部分必填项未填写或格式不正确');
- return;
- }
- // 提交请求
- formLoading.value = true
- try {
- tableData.value.forEach(item=>{
- item.projectId = formData.value.id;
- // item.deptId = useUserStore().getUser.deptId;
- })
- const data = {
- taskList: tableData.value
- }
- if (formType.value === 'create') {
- await IotProjectTaskApi.createIotProjectTask(data)
- message.success(t('common.createSuccess'))
- } else {
- await IotProjectTaskApi.updateIotProjectTask(data)
- message.success(t('common.updateSuccess'))
- }
- // 发送操作成功的事件
- emit('success')
- close()
- } finally {
- formLoading.value = false
- }
- }
- /** 重置表单 */
- const resetForm = () => {
- formData.value = {
- id: undefined,
- contractId: undefined,
- contractName: undefined,
- contractCode: undefined,
- workloadTotal: undefined,
- workloadFinish: undefined,
- startTime: undefined,
- endTime: undefined,
- location: undefined,
- technique: undefined,
- payment: undefined,
- remark: undefined,
- manufactureName: undefined,
- manufacturerId: undefined,
- userName: undefined,
- userId: undefined,
- }
- formRef.value?.resetFields()
- }
- // 监听项目列表变化,确保列表加载完成后才执行自动选择
- watch(projectList, (newVal) => {
- if (newVal && newVal.length > 0) {
- // 如果是编辑模式,使用任务数据中的合同ID
- if (id && tableData.value.length > 0 && tableData.value[0].projectId) {
- autoSelectContract(tableData.value[0].projectId.toString());
- }
- // 如果是新增模式且有传递的projectId,使用传递的projectId
- else if (!id && projectId.value) {
- console.log('watch-新增模式且有传递的projectId' + projectId.value + ' projectId not found');
- autoSelectContract(projectId.value);
- }
- }
- });
- onMounted(async () => {
- // deptList.value = handleTree(await DeptApi.getSimpleDeptList())
- // 查询当前登录人所属公司下的所有部门
- deptList.value = handleTree(await DeptApi.companyLevelChildrenDepts())
- let deptId = useUserStore().getUser.deptId
- projectList.value = await IotProjectInfoApi.getIotProjectInfoUser(deptId);
- // 查询当前任务已经选中的设备信息
- open();
- // 如果有项目 ID 但合同未选中,尝试再次选择
- /* if (projectId && !formData.value.contractId) {
- setTimeout(() => autoSelectContract(projectId), 500);
- } */
- })
- // 计算正在编辑的行数
- const editingRowsCount = computed(() => {
- return tableData.value.filter(row => row.editing).length;
- });
- // 格式化日期
- const formatDate = (timestamp) => {
- const date = new Date(timestamp);
- return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
- };
- // 为编辑中的行添加特殊样式
- const rowClassName = ({ row }) => {
- return row.editing ? 'editable-row' : '';
- };
- // 添加新行
- const addNewRow = () => {
- const newId = tableData.value.length > 0
- ? Math.max(...tableData.value.map(item => item.id)) + 1
- : 1;
- const newRow = {
- id: newId,
- wellName: '',
- wellType: '',
- location: '',
- technique: '',
- workloadDesign: '',
- deptIds: [],
- deviceIds: [],
- deviceCodes: '',
- responsiblePerson: [], // 新增责任人字段
- responsiblePersonNames: '', // 新增责任人名称字段
- remark:'',
- editing: true,
- editData: {
- wellName: '',
- wellType: '',
- wellCategory: '',
- location: '',
- technique: '',
- workloadDesign: '',
- deptIds: [],
- deviceIds: [],
- deviceCodes: '',
- responsiblePerson: [], // 新增责任人字段
- responsiblePersonNames: '', // 新增责任人名称字段
- remark:'',
- },
- originalData: {},
- errors: {}
- };
- tableData.value.unshift(newRow);
- };
- // 编辑行
- const editRow = (row) => {
- // 保存原始数据用于取消编辑时恢复
- row.originalData = {...row};
- // 设置编辑数据
- row.editData = {
- id: row.id,
- wellName: row.wellName,
- wellType: row.wellType,
- wellCategory: row.wellCategory,
- location: row.location,
- technique: row.technique,
- workloadDesign: row.workloadDesign,
- deptIds: row.deptIds,
- deviceIds: row.deviceIds,
- deviceCodes: row.deviceCodes,
- responsiblePerson: row.responsiblePerson ? [...row.responsiblePerson] : [], // 复制责任人数组
- responsiblePersonNames: row.responsiblePersonNames, // 复制责任人名称
- remark: row.remark,
- };
- // 进入编辑状态
- row.editing = true;
- // 清空错误信息
- row.errors = {};
- };
- // 验证行数据
- const validateRow = (row) => {
- row.errors = {};
- let valid = true;
- if (!row.editData.wellName || row.editData.wellName.trim() === '') {
- row.errors.wellName = '井号不能为空';
- valid = false;
- }
- if (!row.editData.wellType || row.editData.wellType.trim() === '') {
- row.errors.wellType = '井型不能为空';
- valid = false;
- }
- // 施工地点
- if (!row.editData.location || row.editData.location.trim() === '') {
- row.errors.location = '施工地点不能为空';
- valid = false;
- }
- if (!row.editData.technique || row.editData.technique.trim() === '') {
- row.errors.technique = '施工工艺不能为空';
- valid = false;
- }
- // 施工队伍 (deptIds 是数组)
- if (!row.editData.deptIds || row.editData.deptIds.length === 0) {
- row.errors.deptIds = '施工队伍不能为空';
- valid = false;
- }
- // 施工设备 (deviceIds 是数组)
- if (!row.editData.deviceIds || row.editData.deviceIds.length === 0) {
- row.errors.deviceIds = '施工设备不能为空';
- valid = false;
- }
- // 责任人 (responsiblePerson 是数组)
- if (!row.editData.responsiblePerson || row.editData.responsiblePerson.length === 0) {
- row.errors.responsiblePerson = '责任人不能为空';
- valid = false;
- }
- return valid;
- };
- // 获取所有设备名称(用于tooltip提示)
- const getAllDeviceNames = (deviceIds: number[]) => {
- if (!deviceIds || deviceIds.length === 0) return '无设备';
- const deviceNames = deviceIds
- .map(id => deviceMap.value[id]?.deviceCode || '未知设备')
- .filter(name => name !== '未知设备');
- return deviceNames.join(', ') || '无有效设备';
- };
- // 保存行
- const saveRow = (row) => {
- if (!validateRow(row)) return;
- // 将编辑数据应用到行
- row.id = row.editData.id;
- row.wellName = row.editData.wellName;
- row.wellType = row.editData.wellType;
- row.wellCategory = row.editData.wellCategory;
- row.location = row.editData.location;
- row.technique = row.editData.technique;
- row.workloadDesign = row.editData.workloadDesign;
- row.deptIds = row.editData.deptIds;
- row.deviceIds = row.editData.deviceIds;
- // row.deviceCodes = row.editData.deviceCodes;
- // 关键修改:根据设备ID列表生成设备代码
- row.responsiblePerson = row.editData.responsiblePerson; // 保存责任人ID
- row.responsiblePersonNames = row.editData.responsiblePersonNames; // 保存责任人名称
- if (currentEditingRow.value) {
- // 更新设备ID
- currentEditingRow.value.editData.deviceIds = [...selectedDeviceIds.value];
- // 更新设备代码显示
- const selectedDevices = filteredDeviceList.value.filter(device =>
- selectedDeviceIds.value.includes(device.id)
- );
- currentEditingRow.value.editData.deviceCodes = selectedDevices
- .map(device => device.deviceCode)
- .join(', ');
- row.deviceCodes = currentEditingRow.value.editData.deviceCodes;
- }
- row.remark = row.editData.remark;
- // 退出编辑状态
- row.editing = false;
- };
- // 保存所有更改
- const saveAll = () => {
- let allValid = true;
- // 验证所有编辑中的行
- tableData.value.forEach(row => {
- if (row.editing && !validateRow(row)) {
- allValid = false;
- }
- });
- if (!allValid) {
- ElMessage.error('部分数据验证失败,请检查输入');
- return;
- }
- // 保存所有编辑中的行
- tableData.value.forEach(row => {
- if (row.editing) {
- row.id = row.editData.id;
- row.wellName = row.editData.wellName;
- row.wellType = row.editData.wellType;
- row.location = row.editData.location;
- row.technique = row.editData.technique;
- row.workloadDesign = row.editData.workloadDesign;
- row.deptIds = row.editData.deptIds;
- row.deviceIds = row.editData.deviceIds;
- row.deviceCodes = row.editData.deviceCodes;
- row.responsiblePerson = row.editData.responsiblePerson; // 保存责任人ID
- row.responsiblePersonNames = row.editData.responsiblePersonNames; // 保存责任人名称
- row.remark = row.editData.remark;
- }
- });
- ElMessage.success('所有更改已保存');
- };
- // 取消编辑
- const cancelEdit = (row) => {
- // 恢复原始数据
- if (row.originalData.id) {
- row.id = row.originalData.id;
- row.wellName = row.originalData.wellName;
- row.wellType = row.originalData.wellType;
- row.wellCategory = row.originalData.wellCategory;
- row.location = row.originalData.location;
- row.technique = row.originalData.technique;
- row.workloadDesign = row.originalData.workloadDesign;
- row.deptIds = row.originalData.deptIds;
- row.deviceIds = row.originalData.deviceIds;
- row.deviceCodes = row.originalData.deviceCodes;
- row.responsiblePerson = row.originalData.responsiblePerson; // 恢复责任人ID
- row.responsiblePersonNames = row.originalData.responsiblePersonNames; // 恢复责任人名称
- row.remark = row.originalData.remark;
- } else {
- // 如果是新增行,则删除
- const index = tableData.value.indexOf(row);
- if (index !== -1) {
- tableData.value.splice(index, 1);
- }
- }
- row.editing = false;
- };
- // 删除行
- const deleteRow = (index) => {
- tableData.value.splice(index, 1);
- ElMessage.success('行已删除');
- };
- </script>
- <style scoped>
- .edit-input {
- margin-right: 10px;
- }
- .error-message {
- color: #f56c6c;
- font-size: 12px;
- margin-top: 5px;
- }
- .action-cell {
- display: flex;
- gap: 8px;
- }
- /* 1. 穿梭框父容器:居中 + 内边距 */
- .transfer-container {
- text-align: center;
- padding: 0px 0; /* 上下内边距,避免紧贴对话框边缘 */
- }
- /* 2. 穿梭框组件:控制宽度,避免过窄或过宽 */
- .transfer-component {
- width: 100%; /* 占对话框90%宽度,兼顾美观和内容显示 */
- min-width: 600px;
- }
- /* 3. 直接调整 el-transfer 左右窗口的宽度 */
- :deep(.el-transfer-panel) {
- width: 40% !important; /* 强制设置左右面板宽度,确保两侧面板对等 */
- }
- /* 3. 深度选择器:修改el-transfer选项样式(解决内容省略问题) */
- :deep(.el-transfer-panel__item) {
- white-space: nowrap; /* 文本不换行,保证一行显示 */
- overflow: hidden; /* 超出部分隐藏 */
- text-overflow: ellipsis; /* 超出显示省略号 */
- max-width: 100%; /* 确保文本不超出选项容器宽度 */
- padding: 6px 6px; /* 适当增加内边距,优化点击体验 */
- }
- /* 4. 选项文本:确保继承父容器宽度,省略号正常生效 */
- .transfer-option-text {
- display: inline-block;
- max-width: 100%;
- }
- /* 设备名称显示区域样式 */
- .device-names {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 200px; /* 根据实际列宽调整 */
- }
- :deep(.el-transfer-panel__list) {
- width: 100% !important; /* 使面板内部内容宽度占满 */
- }
- /* 责任人名称显示区域样式 */
- .responsible-names {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 200px; /* 根据实际列宽调整 */
- }
- /* 添加部门名称的样式 */
- .dept-names {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 200px; /* 根据实际列宽调整 */
- display: inline-block;
- vertical-align: bottom;
- }
- </style>
|