| New file |
| | |
| | | <template> |
| | | <el-dialog |
| | | class="gd-dialog" |
| | | v-model="visible" |
| | | :title="titleEnum[dialogMode]" |
| | | @closed="visible = false" |
| | | destroy-on-close |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-row class="detail-row-view" v-if="dialogReadonly"> |
| | | <el-col :span="12"> |
| | | <div class="label">材料名称</div> |
| | | <div class="val">{{ formData.materialName }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">材料说明</div> |
| | | <div class="val">{{ formData.materialDesc }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">必要性</div> |
| | | <div class="val">{{ getDictLabel(formData.necessity, dictObj.necessity) }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">是否适用全部情形</div> |
| | | <div class="val">{{ getDictLabel(String(formData.isAllScenario), dictObj.isAllScenario) }}</div> |
| | | </el-col> |
| | | <!-- <el-col :span="12"> |
| | | <div class="label">所属区域</div> |
| | | <div class="val">{{ formData.areaName }}</div> |
| | | </el-col> --> |
| | | </el-row> |
| | | |
| | | <el-form |
| | | class="gd-dialog-form" |
| | | v-else |
| | | ref="formRef" |
| | | :model="formData" |
| | | :rules="rules" |
| | | :disabled="dialogReadonly" |
| | | label-width="140px" |
| | | > |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="材料名称" prop="materialName"> |
| | | <el-input class="gd-input" v-model="formData.materialName" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="材料说明" prop="materialDesc"> |
| | | <el-input class="gd-input" v-model="formData.materialDesc" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="必要性" prop="necessity"> |
| | | <el-select |
| | | class="gd-select" |
| | | popper-class="gd-select-popper" |
| | | v-model="formData.necessity" |
| | | placeholder="请选择" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in dictObj.necessity" |
| | | :key="item.dictKey" |
| | | :label="item.dictValue" |
| | | :value="item.dictKey" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="是否适用全部情形" prop="isAllScenario"> |
| | | <el-select |
| | | class="gd-select" |
| | | popper-class="gd-select-popper" |
| | | v-model="formData.isAllScenario" |
| | | placeholder="请选择" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in dictObj.isAllScenario" |
| | | :key="item.dictKey" |
| | | :label="item.dictValue" |
| | | :value="Number(item.dictKey)" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <!-- <el-col :span="12"> |
| | | <el-form-item label="所属区域" prop="areaCode"> |
| | | <el-tree-select |
| | | class="gd-select" |
| | | popper-class="gd-tree-select-popper" |
| | | v-model="formData.areaCode" |
| | | node-key="id" |
| | | :default-expanded-keys="expandedKeys" |
| | | :props="treeSelectProps" |
| | | placeholder="请选择" |
| | | clearable |
| | | filterable |
| | | :check-strictly="true" |
| | | lazy |
| | | :load="loadRegionNode" |
| | | :render-after-expand="false" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> --> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer v-if="!dialogReadonly"> |
| | | <el-button color="#F2F3F5" @click="visible = false">{{ dialogReadonly ? '关闭' : '取消' }}</el-button> |
| | | <el-button class="save-btn" color="#4C34FF" :loading="submitting" :disabled="submitting" @click="handleSubmit"> |
| | | 保存 |
| | | </el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getDictLabel } from '@ztzf/utils' |
| | | import { computed, ref, inject } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { fieldRules } from '@ztzf/utils' |
| | | import { materialsSubmitApi, materialsDetailApi, regionLazyTreeApi } from './materialsApi' |
| | | |
| | | // 初始化表单数据 |
| | | const initForm = () => ({ |
| | | materialName: '', |
| | | materialDesc: '', |
| | | necessity: '', |
| | | isAllScenario: '', |
| | | areaCode: '', |
| | | id: null, |
| | | }) |
| | | |
| | | const dictObj = inject('dictObj') |
| | | |
| | | const emit = defineEmits(['success']) |
| | | const formRef = ref(null) // 表单实例 |
| | | const formData = ref(initForm()) // 表单数据 |
| | | const visible = defineModel() // 弹框显隐 |
| | | const dialogMode = ref('add') // 弹框模式 |
| | | const submitting = ref(false) // 提交中 |
| | | const dialogReadonly = computed(() => dialogMode.value === 'view') |
| | | const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' }) |
| | | const expandedKeys = ref([]) // 树形选择器展开的节点 |
| | | const regionNodeCache = ref(new Map()) // 缓存区域节点及其父节点关系 |
| | | |
| | | // 树形选择器配置 |
| | | const treeSelectProps = ref({ |
| | | value: 'id', |
| | | label: 'name', |
| | | children: 'children', |
| | | // 判断叶子节点 |
| | | isLeaf: (data, node) => { |
| | | return node.level >= 2 |
| | | }, |
| | | }) |
| | | |
| | | // 表单校验规则 |
| | | const rules = { |
| | | materialName: fieldRules(true), |
| | | materialDesc: fieldRules(true), |
| | | necessity: fieldRules(true), |
| | | isAllScenario: fieldRules(true), |
| | | areaCode: fieldRules(true), |
| | | } |
| | | |
| | | // 提交新增/编辑 |
| | | async function handleSubmit() { |
| | | const isValid = await formRef.value?.validate().catch(() => false) |
| | | if (!isValid) return |
| | | submitting.value = true |
| | | try { |
| | | await materialsSubmitApi(formData.value) |
| | | ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功') |
| | | visible.value = false |
| | | emit('success') |
| | | } finally { |
| | | submitting.value = false |
| | | } |
| | | } |
| | | |
| | | // 加载详情 |
| | | async function loadDetail() { |
| | | if (!formData.value.id) return |
| | | const res = await materialsDetailApi({ id: formData.value.id }) |
| | | const data = res?.data?.data ?? {} |
| | | // 将 areaCode 转换为字符串类型 |
| | | if (data.areaCode !== undefined) { |
| | | data.areaCode = String(data.areaCode) |
| | | } |
| | | formData.value = data |
| | | |
| | | // 如果是编辑模式,获取区域节点的路径以展开树形选择器 |
| | | if (dialogMode.value === 'edit' && data.areaCode) { |
| | | await loadRegionPath(data.areaCode) |
| | | } |
| | | } |
| | | |
| | | // 加载区域节点路径 |
| | | async function loadRegionPath(areaCode) { |
| | | try { |
| | | const path = [] |
| | | const queue = [{ code: '360800000000', currentPath: ['360800000000'] }] |
| | | let found = false |
| | | |
| | | // 广度优先搜索,逐层加载节点直到找到目标节点 |
| | | while (queue.length > 0 && !found) { |
| | | const { code, currentPath } = queue.shift() |
| | | |
| | | // 获取当前节点的子节点 |
| | | const res = await regionLazyTreeApi({ parentCode: code }) |
| | | const nodes = res?.data?.data || [] |
| | | |
| | | for (const node of nodes) { |
| | | const nodeId = node.id || node.value |
| | | |
| | | // 缓存节点及其父节点关系 |
| | | regionNodeCache.value.set(nodeId, { |
| | | id: nodeId, |
| | | name: node.name || node.title || node.label, |
| | | parentId: code, |
| | | hasChildren: node.hasChildren || false, |
| | | }) |
| | | |
| | | // 检查是否找到目标节点 |
| | | if (nodeId === areaCode) { |
| | | path.push(...currentPath, nodeId) |
| | | found = true |
| | | break |
| | | } |
| | | |
| | | // 如果有子节点,加入队列继续搜索 |
| | | if (node.hasChildren) { |
| | | queue.push({ |
| | | code: nodeId, |
| | | currentPath: [...currentPath, nodeId], |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (path.length > 0) { |
| | | expandedKeys.value = path |
| | | } |
| | | } catch (error) { |
| | | console.error('加载区域路径失败:', error) |
| | | } |
| | | } |
| | | |
| | | // 打开弹框 |
| | | async function open({ mode = 'add', row } = {}) { |
| | | dialogMode.value = mode |
| | | formData.value = dialogMode.value === 'add' ? initForm() : row |
| | | if (dialogMode.value !== 'add') { |
| | | await loadDetail() |
| | | } |
| | | } |
| | | |
| | | // 懒加载获取区域节点数据 |
| | | async function loadRegionNode(node, resolve) { |
| | | try { |
| | | // 限制只加载两级,当前节点 level >= 1 时不加载子节点 |
| | | if (node.level >= 2) { |
| | | resolve([]) |
| | | return |
| | | } |
| | | // 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数 |
| | | const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' } |
| | | const parentCode = node.data?.id || '360800000000' |
| | | const res = await regionLazyTreeApi(params) |
| | | const nodes = res?.data?.data || [] |
| | | |
| | | // 处理返回的节点数据 |
| | | const processedNodes = nodes.map(item => { |
| | | const nodeId = item.id || item.value |
| | | // 缓存节点及其父节点关系 |
| | | regionNodeCache.value.set(nodeId, { |
| | | id: nodeId, |
| | | name: item.name || item.title || item.label, |
| | | parentId: parentCode, |
| | | hasChildren: item.hasChildren || false, |
| | | }) |
| | | |
| | | return { |
| | | id: nodeId, |
| | | name: item.name || item.title || item.label, |
| | | hasChildren: item.hasChildren || false, |
| | | } |
| | | }) |
| | | |
| | | resolve(processedNodes) |
| | | } catch (error) { |
| | | console.error('获取区域数据失败:', error) |
| | | resolve([]) |
| | | } |
| | | } |
| | | |
| | | defineExpose({ open }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |