一个景点的选择组件

直接将一个下拉框做成一个组件

<template>
  <el-select
    v-model="value"
    @change="deptSelectChange"
    placeholder="请输入或点击选择"
    default-first-option
    placement="bottom"
    filterable
    remote
    :multiple="type == 'check'"
    style="width: 100%; max-height: 150px; overflow: auto"
    :disabled="disabled"
    :readonly="readonly"
  >
    <el-option
      v-for="item in deptOptions"
      :key="item.value"
      :label="item.label"
      :value="item.value"
      style="z-index: 999"
    />
  </el-select>
  <el-icon
    v-if="!view"
    size="small"
    @click="selectDeptDialog()"
    style="margin-left: -30px; width: 25px; height: 25px; cursor: pointer"
  >
    <MoreFilled />
  </el-icon>
  <!-- 部门组件 -->
  <DeptDialog
    :type="type"
    :deptIds="deptIds"
    @deptList="selectDeptList"
    :nodeTitle="nodeTitle"
    ref="deptDialogRef"
  ></DeptDialog>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue';
import { getDeptTree } from '@/api/system/dept';

const props = defineProps({
   //  内容
  modelValue: {
    type: [String, Array],
    default: '',
  },
  view: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  readonly: {
    type: Boolean,
    default: false,
  },
  type: {
    type: String,
    default: 'radio',
  },
  nodeTitle: {
    type: String,
    default: '',
  },

});

const emits = defineEmits(['update:modelValue', 'change']);

const value = ref(props.modelValue);

watch(
  () => props.type,
  newType => {
    // 当类型从 'radio' 变为 'check' 时,将 modelValue 转换为数组
    if (newType === 'check' && !Array.isArray(value.value)) {
      value.value = [value.value];
    }
    // 当类型从 'check' 变为 'radio' 时,尝试将 modelValue 转换为字符串
    else if (newType === 'radio' && Array.isArray(value.value)) {
      value.value = value.value[0] || '';
    }
  }
);

// 监听props.modelValue的变化,以便更新内部状态
watch(
  () => props.modelValue,
  newValue => {
    value.value = newValue;
  }
);

// 监听内部value的变化,并通知父组件更新
watch(value, newValue => {
  emits('update:modelValue', newValue);
});

//选择部门组件ref
const deptDialogRef = ref();
//部门id用于回显
const deptIds = ref('');
//部门列表
const deptlist = ref([]);
//部门名称下拉数据
const deptOptions = ref([]);

//获取部门名称模糊搜索数据
const deptNameMessage = async () => {
  try {
    const res = await getDeptTree();
    let result = [];
    if (props.nodeTitle) {
      result = findAllNodesUnderStationSection(res.data.data, props.nodeTitle);
    } else {
      result = res.data.data;
    }
    const flattenedData = flattenTreeData(result);
    deptOptions.value = flattenedData;
    deptlist.value = flattenedData;
  } catch (error) {
    console.error('获取部门树失败:', error);
  }
};

//打开选择部门组件弹框
const selectDeptDialog = () => {
  setTimeout(() => {
    deptDialogRef.value.openDialog();
  }, 200);
};

//弹框组件选择部门回调
const selectDeptList = list => {
  if (props.type == 'check') {
    value.value = list.map(item => item.id);
    deptIds.value = list.map(item => item.id).join(',');
  } else {
    value.value = list[0].id;
    deptIds.value = list[0].id;
  }
  emits('update:modelValue', value.value);
  emits('change', value.value);
};

// 下拉选择部门
const deptSelectChange = item => {
  if (props.type === 'check') {
    value.value = item;
    deptIds.value = item.join(',');
  } else {
    value.value = item;
    deptIds.value = item;
  }
  emits('update:modelValue', value.value);
  emits('change', value.value);
};

// 扁平化树形数据
const flattenTreeData = (nodes, flattenedList = []) => {
  nodes.forEach(node => {
    const { title: label, id: value, children } = node;
    const newNode = { label, value }; // 仅保留label和value
    flattenedList.push(newNode); // 添加当前节点到结果数组
    if (children && children.length > 0) {
      flattenTreeData(children, flattenedList); // 递归处理子节点
    }
  });
  return flattenedList;
};

// 查找所有“站段”节点
const findAllNodesUnderStationSection = (nodes, nodeKey, result = []) => {
  nodes.forEach(node => {
    // 检查当前节点是否名称为“站段”
    if (node.title == nodeKey && node.parentId == 0) {
      // 如果是,将当前节点添加到结果中
      result.push(node);
    }
    // 如果当前节点有子节点,递归遍历子节点
    if (node.children && node.children.length > 0) {
      findAllNodesUnderStationSection(node.children, result);
    }
  });
  return result;
};

onMounted(() => {
  deptNameMessage();
});
</script>
<style lang="scss" scoped></style>

弹框组件

<template>
  <el-dialog
    @close="selectCancel"
    append-to-body
    title="选择部门"
    v-model="platformBox"
    width="344"
  >
    <div style="margin-right: 5px">
      <el-input
        placeholder="搜索部门分类"
        style="width: 304px; margin-bottom: 5px"
        v-model="inputValue"
      ></el-input>
    </div>
    <div class="title-style" style="position: sticky">
      <span class="tree-title">部门分类</span>
      <el-button
        @click="dialogChink"
        class="add-button"
        icon="CirclePlus"
        slot="append"
        v-if="userInfo.role_name.includes('admin')"
      >
        <span style="margin-left: 1px">添加</span>
      </el-button>
    </div>
    <div style="height: 20vh; overflow: auto">
      <el-tree
        :current-node-key="checkedNodeKeys"
        :data="dataSource"
        :default-checked-keys="checkedKeys"
        :filter-node-method="filterNode"
        :show-checkbox="showCheckbox"
        @node-click="nodeClick"
        check-strictly
        class="filter-tree"
        default-expand-all
        icon="ArrowRightBold"
        node-key="id"
        ref="treeRef"
        @check-change="handleCheckChange"
      >
        <template v-slot="{ data }">
          <span class="custom-node">{{ data.title }}</span>
        </template>
      </el-tree>
    </div>

    <template #footer>
      <span class="dialog-footer">
        <el-button @click="selectData" type="primary"> 确定 </el-button
        ><el-button @click="selectCancel">取消</el-button>
      </span>
    </template>
    <!--crud-->
    <el-dialog
      destroy-on-close
      :title="title"
      v-model="dialogFormVisible"
      :before-close="resetForm"
      width="400px"
      append-to-body
      draggable
    >
      <el-form
        :model="form"
        :rules="rules"
        ref="ruleFormRef"
        label-position="right"
        status-icon
        label-width="auto"
      >
        <el-form-item label="机构名称" prop="deptName">
          <el-input v-model="form.deptName" placeholder="请输入机构名称" />
        </el-form-item>
        <el-form-item label="机构全称" prop="fullName">
          <el-input v-model="form.fullName" placeholder="请输入机构全称" />
        </el-form-item>

        <el-form-item label="上级机构">
          <el-tree-select
            v-model="form.parentId"
            :data="dataSource"
            :render-after-expand="false"
            style="width: 100%"
            placeholder="请选择上级机构"
          >
          </el-tree-select>
        </el-form-item>

        <el-form-item label="机构类型" prop="deptCategory">
          <el-select v-model="form.deptCategory" placeholder="请选择机构类型" style="width: 100%">
            <el-option
              v-for="item in orgCategoryList"
              :key="item.dictKey"
              :label="item.dictValue"
              :value="item.dictKey"
            >
            </el-option>
          </el-select>
        </el-form-item>

        <el-form-item label="排序" prop="sort" required>
          <el-input-number
            :max="10"
            :min="1"
            controls-position="right"
            placeholder="请输入排序"
            style="text-align: left; width: 100%"
            v-model="form.sort"
          />
        </el-form-item>

        <el-form-item label="备注" prop="remark">
          <el-input
            :row="5"
            placeholder="请输入备注"
            type="textarea"
            v-model="form.remark"
            maxlength="200"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="submitForm(form)" type="primary" v-if="deptAdd"> 确定 </el-button>
          <el-button @click="updateForm(form)" type="primary" v-else> 修改 </el-button>
          <el-button @click="resetForm()">取消</el-button>
        </span>
      </template>
    </el-dialog>
  </el-dialog>
</template>

<script>
import { add, getDeptTree, update } from '@/api/system/dept';
import { mapGetters } from 'vuex';
import { getDictionary } from '@/api/system/dictbiz';
import rulesPublic from '@/utils/biz/rules';

export default {
  props: {
    deptIds: {
      //用于回显’,‘号隔开
      type: String,
    },
    type: {
      //类型 radio.单选,check.多选
      type: String,
    },
    nodeTitle: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      plusBtnType: true, //默认显示 加号
      platformBox: false,
      treeDeptId: '',
      value: '',
      dataSource: [], //数据集合
      deptList: [], //已选择的数据
      checkedKeys: [], // 初始选中项(多选)
      checkedNodeKeys: '', // 初始选中项(单选)
      inputValue: '',
      showCheckbox: false,
      dialogFormVisible: false, //新增
      deptAdd: false, //新增
      orgCategoryList: [], //机构类型
      form: {
        sort: '1',
      },
      // 弹框标题
      title: '',
      // 表单校验
      rules: {
        deptName: rulesPublic.MaxiMumNumber(true, '请输入机构名称', 1, 20),
        fullName: rulesPublic.MaxiMumNumber(true, '请输入机构全称', 1, 20),
        deptCategory: [
          {
            required: true,
            message: '请输入机构类型',
            trigger: 'blur',
          },
        ],
        sort: [
          {
            required: true,
            message: '请输入排序',
            trigger: 'blur',
          },
        ],
      },
    };
  },
  computed: {
    ...mapGetters(['userInfo', 'permission']),
    permissionList() {
      return {
        addBtn: this.validData(this.permission.param_add, false),
        viewBtn: this.validData(this.permission.param_view, false),
        delBtn: this.validData(this.permission.param_delete, false),
        editBtn: this.validData(this.permission.param_edit, false),
      };
    },
  },
  watch: {
    //监听树搜索框
    inputValue(val) {
      this.$refs.treeRef.filter(val);
    },
  },
  methods: {
    handleCheckChange(data, checked, indeterminate) {
      // 单选
      if (checked && this.type === 'radio') {
        this.$refs.treeRef.setCheckedKeys([data.id]);
      }
    },
    //打开弹框
    openDialog() {
      this.showCheckbox = true;
      if (this.deptIds) {
        let videoTrackListIds = this.deptIds.split(',');
        setTimeout(() => {
          this.checkedKeys = videoTrackListIds;
        }, 300);
      }
      this.platformBox = true;
      this.getListTree();
      //查询机构类型
      getDictionary({ code: 'org_category' }).then(res => {
        this.orgCategoryList = res.data.data;
      });
    },
    // 定义一个递归函数来遍历树形结构
    findAllNodesUnderStationSection(nodes, nodeTitle, result = []) {
      nodes.forEach(node => {
        // 检查当前节点是否名称为“站段”
        if (node.title == this.nodeTitle && node.parentId == 0) {
          // 如果是,将当前节点添加到结果中
          result.push(node);
        }
        // 如果当前节点有子节点,递归遍历子节点
        if (node.children && node.children.length > 0) {
          this.findAllNodesUnderStationSection(node.children, result);
        }
      });
      return result;
    },
    //获取树数据
    getListTree() {
      getDeptTree().then(data => {
        let res = [];
        if (this.nodeTitle) {
           res = this.findAllNodesUnderStationSection(data.data.data, this.nodeTitle);
        } else {
           res = data.data.data;
        }
        res.map(item => {
          item.label = item.title;
          if (item.children) {
            this.finMyChildren(item);
          }
        });
        this.dataSource = res;
      });
    },
    //查找树形的子节点
    finMyChildren(data) {
      data.children.forEach(item => {
        item.label = item.title;
        if (item.children && item.children.length > 0) {
          this.finMyChildren(item);
        }
      });
    },
    //组件传参
    nodeClick(data) {
      let key = this.$refs.treeRef.getCheckedKeys();
      if (key) {
        if (key.includes(data.id)) {
          key.map((item, index) => {
            if (item === data.id) {
              key.splice(index, 1);
            }
          });
        } else {
          key.push(data.id);
        }
        this.$refs.treeRef.setCheckedKeys(key);
      }
      this.deptList = [];
      this.deptList.push(data);
    },
    //过滤树节点
    filterNode(value, data) {
      if (!value) return true;
      return data.title.includes(value);
    },
    //选择传递数据
    selectData() {
      if (this.showCheckbox) {
        let list = this.$refs.treeRef.getCheckedNodes();
        this.$emit('deptList', list);
      } else {
        this.$emit('deptList', this.deptList);
      }
      this.checkedKeys = [];
      this.checkedNodeKeys = '';
      this.platformBox = false;
    },
    //关闭弹框
    selectCancel() {
      this.checkedKeys = [];
      this.checkedNodeKeys = '';
      this.platformBox = false;
    },
    dialogChink() {
      //如果是第一次新增
      this.title = '新增';
      this.deptAdd = true;
      this.form.sort = '1';
      this.form.deptCategory = '1';
      //打开新增弹框
      this.dialogFormVisible = true;
    },
    //新增
    submitForm() {
      this.$refs.ruleFormRef.validate(valid => {
        if (valid) {
          add(this.form).then(res => {
            if (res.data.data) {
              this.$message({
                type: 'success',
                message: '新增成功!',
              });
            }
            this.form = {};
            this.dialogFormVisible = false;
            setTimeout(() => {
              this.getListTree();
            }, 300);
          });
        } else {
          return false;
        }
      });
    },
    //修改
    updateForm() {
      this.$refs.ruleFormRef.validate(valid => {
        if (valid) {
          update(this.form)
            .then(res => {
              if (res.data.data) {
                this.$alert('修改失败,修改的分类名称已存在!', '系统提示', {
                  confirmButtonText: '确定',
                  type: 'warning',
                });
              } else {
                this.$message({
                  type: 'success',
                  message: '修改成功!',
                });
              }
            })
            .catch(error => {
              this.ruleForm = this.rowData;
            });
          setTimeout(() => {
            this.getList();
          }, 300);
          this.dialogFormVisible = false;
        } else {
          return false;
        }
      });
    },
    //取消
    resetForm() {
      this.form = {};
      this.dialogFormVisible = false;
    },
    nodeClickTree(row) {
      this.form.parentId = row.id;
      this.form.parentName = row.title;
      this.form.value = row.title;
      setTimeout(() => {
        //当前行子表全选
        this.form.parentName = row.title;
      }, 100);
    },
  },
};
</script>

<style scoped>
.el-dialog-dept {
  width: 340px;
  height: 300px;
}

.el-dialog-dept .el-dialog__body {
  padding: 0 0 0;
}

/*标题行样式*/
.title-style {
  margin-top: 5px;
  display: flex;
  justify-content: space-between;
  padding-left: 7px;
}

/*标题样式*/
.tree-title {
  line-height: 30px;
  font-size: 13px;
  color: #3d3d3d;
  font-weight: bold;
}

/*添加分类按钮样式*/
.add-button {
  background-color: white;
  color: #409eff;
  border: none;
  height: 30px;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值