|
@@ -0,0 +1,500 @@
|
|
|
+<template>
|
|
|
+ <hc-drawer
|
|
|
+ v-model="isShow"
|
|
|
+ to-id="hc-main-box"
|
|
|
+ is-close
|
|
|
+ @close="drawerClose"
|
|
|
+ >
|
|
|
+ <hc-body split>
|
|
|
+ <template #left>
|
|
|
+ <hc-card scrollbar>
|
|
|
+ <template #header>
|
|
|
+ <hc-search-input
|
|
|
+ v-model="searchTreeName"
|
|
|
+ @search="searchTreeNameClick"
|
|
|
+ >
|
|
|
+ <template #prepend>
|
|
|
+ <el-select
|
|
|
+ v-model="queryType"
|
|
|
+ placeholder="类型"
|
|
|
+ style="width: 75px"
|
|
|
+ >
|
|
|
+ <el-option label="节点" value="1" />
|
|
|
+ <el-option label="表名" value="2" />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </hc-search-input>
|
|
|
+ </template>
|
|
|
+ <hc-lazy-tree
|
|
|
+ v-if="isTreeMode"
|
|
|
+ :root-menu="treeMenuData"
|
|
|
+ :menus="treeMenuDatas"
|
|
|
+ :h-props="treeProps"
|
|
|
+ tree-key="id"
|
|
|
+ @load="treeLoadNode"
|
|
|
+ @node-tap="treeNodeTap"
|
|
|
+ @menu-tap="treeMenuClick"
|
|
|
+ >
|
|
|
+ <template #nodeName="{ data }">
|
|
|
+ <span class="text-16px font-400">{{
|
|
|
+ data.nodeName
|
|
|
+ }}</span>
|
|
|
+ </template>
|
|
|
+ </hc-lazy-tree>
|
|
|
+ <template v-if="isTreeMode === true">
|
|
|
+ <hc-data-tree
|
|
|
+ ref="treeRef"
|
|
|
+ :h-props="treeProps"
|
|
|
+ tree-key="id"
|
|
|
+ :datas="treeData"
|
|
|
+ @node-tap="treeNodeTap"
|
|
|
+ >
|
|
|
+ <template #name="{ data }">
|
|
|
+ <span class="text-16px font-400">{{
|
|
|
+ data.name
|
|
|
+ }}</span>
|
|
|
+ </template>
|
|
|
+ </hc-data-tree>
|
|
|
+ </template>
|
|
|
+ </hc-card>
|
|
|
+ </template>
|
|
|
+ <hc-card class="hc-desk-system-unit-temp">
|
|
|
+ <template v-if="!isNullES(nodeInfo.id)">
|
|
|
+ <hc-card-item title="节点信息">
|
|
|
+ <el-descriptions :column="3" border>
|
|
|
+ <el-descriptions-item label="节点编号:">{{
|
|
|
+ nodeInfo.nodeCode ?? "-"
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="节点名称:">{{
|
|
|
+ nodeInfo.nodeName ?? "-"
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="节点类型:">{{
|
|
|
+ nodeInfo.nodeTypeName ?? "-"
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="工程类型:">{{
|
|
|
+ nodeInfo.engineeringTypeName ?? "-"
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="备注:">{{
|
|
|
+ nodeInfo.remarks ?? "-"
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </hc-card-item>
|
|
|
+ <hc-card-item title="下节节点列表">
|
|
|
+ <hc-table
|
|
|
+ :column="tableColumn"
|
|
|
+ :datas="tableData"
|
|
|
+ :loading="tableLoading"
|
|
|
+ :is-index="false"
|
|
|
+ :is-current-row="false"
|
|
|
+ />
|
|
|
+ </hc-card-item>
|
|
|
+ </template>
|
|
|
+ <hc-empty v-else title="请先点击左侧树节点" />
|
|
|
+ </hc-card>
|
|
|
+ </hc-body>
|
|
|
+
|
|
|
+ <!-- 新增/修改 -->
|
|
|
+ <hc-dialog
|
|
|
+ v-model="isAddEditShow"
|
|
|
+ widths="24rem"
|
|
|
+ is-footer-center
|
|
|
+ :title="formModel.id ? '修改' : '新增'"
|
|
|
+ @close="addEditClose"
|
|
|
+ >
|
|
|
+ <el-form
|
|
|
+ ref="formRef"
|
|
|
+ :model="formModel"
|
|
|
+ :rules="formRules"
|
|
|
+ label-position="top"
|
|
|
+ label-width="auto"
|
|
|
+ >
|
|
|
+ <el-form-item label="节点编号:">
|
|
|
+ <el-input
|
|
|
+ v-model="formModel.nodeCode"
|
|
|
+ clearable
|
|
|
+ placeholder="请输入节点编号"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="节点名称:" prop="nodeName">
|
|
|
+ <el-input
|
|
|
+ v-model="formModel.nodeName"
|
|
|
+ clearable
|
|
|
+ placeholder="请输入节点名称"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="节点类型:" prop="nodeType">
|
|
|
+ <el-select
|
|
|
+ v-model="formModel.nodeType"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ block
|
|
|
+ placeholder="请选择节点类型"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in meterUnitType"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="工程类型:">
|
|
|
+ <el-input
|
|
|
+ v-model="formModel.engineeringTypeName"
|
|
|
+ clearable
|
|
|
+ placeholder="请输入工程类型"
|
|
|
+ disabled
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="备注:">
|
|
|
+ <el-input
|
|
|
+ v-model="formModel.remarks"
|
|
|
+ clearable
|
|
|
+ placeholder="请输入备注"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button hc-btn @click="addEditClose">取消</el-button>
|
|
|
+ <el-button
|
|
|
+ hc-btn
|
|
|
+ type="primary"
|
|
|
+ :loading="addEditLoading"
|
|
|
+ @click="addEditSubmit"
|
|
|
+ >提交</el-button
|
|
|
+ >
|
|
|
+ </template>
|
|
|
+ </hc-dialog>
|
|
|
+
|
|
|
+ <!-- 调整排序 -->
|
|
|
+ <hc-dialog
|
|
|
+ v-model="isSortingShow"
|
|
|
+ title="调整排序"
|
|
|
+ widths="600px"
|
|
|
+ is-table
|
|
|
+ @close="sortingClose"
|
|
|
+ >
|
|
|
+ <hc-table
|
|
|
+ ref="tableSortingRef"
|
|
|
+ :column="tableSortingColumn"
|
|
|
+ :datas="tableSortingData"
|
|
|
+ is-row-drop
|
|
|
+ is-sort
|
|
|
+ quick-sort
|
|
|
+ @row-drop="sortingRowDropTap"
|
|
|
+ @row-sort="sortingRowSortTap"
|
|
|
+ />
|
|
|
+ <template #footer>
|
|
|
+ <el-button hc-btn @click="sortingClose">取消</el-button>
|
|
|
+ <el-button
|
|
|
+ hc-btn
|
|
|
+ type="primary"
|
|
|
+ :disabled="tableSortingData.length <= 0"
|
|
|
+ :loading="sortingLoading"
|
|
|
+ @click="sortingSubmit"
|
|
|
+ >提交</el-button
|
|
|
+ >
|
|
|
+ </template>
|
|
|
+ </hc-dialog>
|
|
|
+ </hc-drawer>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { nextTick, ref, watch } from "vue";
|
|
|
+import { HcDelMsg } from "hc-vue3-ui";
|
|
|
+import { getDictionaryData, getDictionaryName } from "~src/utils/tools";
|
|
|
+import {
|
|
|
+ arrToId,
|
|
|
+ deepClone,
|
|
|
+ formValidate,
|
|
|
+ getArrValue,
|
|
|
+ getObjValue,
|
|
|
+ isNullES,
|
|
|
+} from "js-fast-way";
|
|
|
+import mainApi from "~api/wbs/tree";
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ data: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({}),
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+const emit = defineEmits(["close"]);
|
|
|
+
|
|
|
+//双向绑定
|
|
|
+const isShow = defineModel("modelValue", {
|
|
|
+ default: false,
|
|
|
+});
|
|
|
+
|
|
|
+//监听数据
|
|
|
+const dataInfo = ref(props.data);
|
|
|
+watch(
|
|
|
+ () => props.data,
|
|
|
+ (data) => {
|
|
|
+ dataInfo.value = data;
|
|
|
+ },
|
|
|
+ { immediate: true, deep: true }
|
|
|
+);
|
|
|
+
|
|
|
+//监听显示
|
|
|
+watch(isShow, (val) => {
|
|
|
+ if (val) getDataApi();
|
|
|
+});
|
|
|
+
|
|
|
+//处理相关数据
|
|
|
+const meterUnitType = ref([]);
|
|
|
+const getDataApi = async () => {
|
|
|
+ isTreeMode.value = true;
|
|
|
+ meterUnitType.value = await getDictionaryData("meter_unit_type", true);
|
|
|
+};
|
|
|
+//树搜索
|
|
|
+const queryType = ref("1");
|
|
|
+const searchTreeName = ref("");
|
|
|
+const searchTreeNameClick = async () => {
|
|
|
+ if (isNullES(searchTreeName.value)) {
|
|
|
+ isTreeMode.value = true;
|
|
|
+ } else {
|
|
|
+ isTreeMode.value = false;
|
|
|
+ await getSearchData();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const getSearchData = async () => {
|
|
|
+ const { id } = getObjValue(dataInfo.value);
|
|
|
+ const { data } = await mainApi.getQueryValueByType({
|
|
|
+ wbsId: id,
|
|
|
+ type: queryType.value,
|
|
|
+ queryValue: searchTreeName.value,
|
|
|
+ });
|
|
|
+ treeData.value = data;
|
|
|
+};
|
|
|
+
|
|
|
+//树配置
|
|
|
+const isTreeMode = ref(false);
|
|
|
+const treeProps = {
|
|
|
+ label: "title",
|
|
|
+ children: "children",
|
|
|
+ isLeaf: function (data) {
|
|
|
+ if (data.hasChildren && data.isExistForm != 1) {
|
|
|
+ return false;
|
|
|
+ } else if (data.hasChildren && data.isExistForm == 1) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+};
|
|
|
+const treeData = ref([]);
|
|
|
+//懒加载树
|
|
|
+const treeLoadNode = async ({ item, level }, resolve) => {
|
|
|
+ const parentId = level === 0 ? 0 : item.id;
|
|
|
+ const { id } = getObjValue(dataInfo.value);
|
|
|
+ const { data } = await mainApi.getLazytree({
|
|
|
+ parentId: parentId,
|
|
|
+ wbsId: id,
|
|
|
+ tenantId: "0000",
|
|
|
+ });
|
|
|
+ resolve(getArrValue(data));
|
|
|
+};
|
|
|
+
|
|
|
+//树菜单根节点
|
|
|
+const treeMenuData = [
|
|
|
+ { icon: "add-circle", label: "新增节点", key: "add" },
|
|
|
+ { icon: "draft", label: "编辑节点", key: "edit" },
|
|
|
+ { icon: "delete-bin", label: "删除节点", key: "del" },
|
|
|
+];
|
|
|
+const treeMenuDatas = [
|
|
|
+ { icon: "add-circle", label: "新增节点", key: "add" },
|
|
|
+ { icon: "draft", label: "编辑节点", key: "edit" },
|
|
|
+ { icon: "arrow-up-down", label: "排序节点", key: "rank" },
|
|
|
+ { icon: "delete-bin", label: "删除节点", key: "del" },
|
|
|
+];
|
|
|
+
|
|
|
+//菜单被点击
|
|
|
+const treeMenuClick = async ({ key, data, node }) => {
|
|
|
+ nodeInfo.value = data;
|
|
|
+ if (key === "add") {
|
|
|
+ formModel.value = {};
|
|
|
+ await nextTick();
|
|
|
+ isAddEditShow.value = true;
|
|
|
+ } else if (key === "edit") {
|
|
|
+ formModel.value = deepClone(data);
|
|
|
+ await nextTick();
|
|
|
+ isAddEditShow.value = true;
|
|
|
+ } else if (key === "rank") {
|
|
|
+ const pid = node.parent.data.id;
|
|
|
+ const { data } = await mainApi.getSameList({ parentId: pid });
|
|
|
+ tableSortingData.value = getArrValue(data);
|
|
|
+ isSortingShow.value = true;
|
|
|
+ } else if (key === "del") {
|
|
|
+ HcDelMsg(async (resolve) => {
|
|
|
+ const { code } = await mainApi.remove(data.id);
|
|
|
+ resolve(); //关闭弹窗的回调
|
|
|
+ if (code !== 200) return;
|
|
|
+ window.$message.success("删除成功");
|
|
|
+ setTreeMode();
|
|
|
+ }).then();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+//树节点被点击
|
|
|
+const nodeInfo = ref({});
|
|
|
+const treeNodeTap = ({ data }) => {
|
|
|
+ const label = getDictionaryName(meterUnitType.value, data.nodeType);
|
|
|
+ data.nodeTypeName = label ?? "-";
|
|
|
+ nodeInfo.value = data;
|
|
|
+ getTableData(data);
|
|
|
+};
|
|
|
+
|
|
|
+//表格数据
|
|
|
+const tableColumn = ref([
|
|
|
+ { key: "nodeCode", name: "节点编号" },
|
|
|
+ { key: "nodeName", name: "节点名称" },
|
|
|
+ { key: "nodeTypeName", name: "节点类型" },
|
|
|
+ { key: "engineeringTypeName", name: "工程类型" },
|
|
|
+ { key: "remarks", name: "备注" },
|
|
|
+]);
|
|
|
+const tableData = ref([{}]);
|
|
|
+
|
|
|
+//获取表格数据
|
|
|
+const tableLoading = ref(false);
|
|
|
+const getTableData = async () => {
|
|
|
+ tableData.value = [];
|
|
|
+ const { id } = nodeInfo.value;
|
|
|
+ tableLoading.value = true;
|
|
|
+ const { data } = await mainApi.getChildList({ id });
|
|
|
+ tableData.value = getArrValue(data);
|
|
|
+ tableLoading.value = false;
|
|
|
+};
|
|
|
+
|
|
|
+//新增、编辑节点
|
|
|
+const isAddEditShow = ref(false);
|
|
|
+const formRef = ref(null);
|
|
|
+const formModel = ref({});
|
|
|
+const formRules = {
|
|
|
+ nodeName: { required: true, trigger: "blur", message: "请输入节点名称" },
|
|
|
+ nodeType: { required: true, trigger: "blur", message: "请选择节点类型" },
|
|
|
+};
|
|
|
+
|
|
|
+//新增、编辑节点提交
|
|
|
+const addEditLoading = ref(false);
|
|
|
+const addEditSubmit = async () => {
|
|
|
+ //验证表单
|
|
|
+ const isForm = await formValidate(formRef.value);
|
|
|
+ if (!isForm) return false;
|
|
|
+ addEditLoading.value = true;
|
|
|
+ //处理表单
|
|
|
+ const form = formModel.value;
|
|
|
+ form.nodeTypeName = getDictionaryName(meterUnitType.value, form.nodeType);
|
|
|
+ //发起请求
|
|
|
+ let res;
|
|
|
+ if (isNullES(form.id)) {
|
|
|
+ const { id, ancestor } = nodeInfo.value;
|
|
|
+ const { id: templateId } = dataInfo.value;
|
|
|
+ form.parentId = id;
|
|
|
+ form.ancestor = ancestor;
|
|
|
+ form.templateId = templateId;
|
|
|
+ res = await mainApi.add(form);
|
|
|
+ } else {
|
|
|
+ res = await mainApi.update(form);
|
|
|
+ }
|
|
|
+ //处理结果
|
|
|
+ const { error, code } = getObjValue(res);
|
|
|
+ addEditLoading.value = false;
|
|
|
+ if (!error && code === 200) {
|
|
|
+ window?.$message?.success("操作成功");
|
|
|
+ addEditClose();
|
|
|
+ setTreeMode();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+//关闭 新增、编辑节点
|
|
|
+const addEditClose = () => {
|
|
|
+ isAddEditShow.value = false;
|
|
|
+ formModel.value = {};
|
|
|
+};
|
|
|
+
|
|
|
+//树重加载
|
|
|
+const setTreeMode = () => {
|
|
|
+ isTreeMode.value = false;
|
|
|
+ nodeInfo.value = {};
|
|
|
+ tableData.value = [];
|
|
|
+ setTimeout(() => {
|
|
|
+ isTreeMode.value = true;
|
|
|
+ }, 500);
|
|
|
+};
|
|
|
+
|
|
|
+//排序
|
|
|
+const isSortingShow = ref(false);
|
|
|
+const tableSortingRef = ref(null);
|
|
|
+
|
|
|
+//排序表
|
|
|
+const tableSortingColumn = [{ key: "nodeName", name: "节点名称" }];
|
|
|
+const tableSortingData = ref([]);
|
|
|
+
|
|
|
+// 行拖拽
|
|
|
+const sortingRowDropTap = async (rows) => {
|
|
|
+ // 先清空,否则排序会异常
|
|
|
+ tableData.value = [];
|
|
|
+ await nextTick();
|
|
|
+ tableData.value = rows;
|
|
|
+};
|
|
|
+
|
|
|
+// 点击排序
|
|
|
+const sortingRowSortTap = async (rows) => {
|
|
|
+ // 先清空,否则排序会异常
|
|
|
+ tableData.value = [];
|
|
|
+ await nextTick();
|
|
|
+ tableData.value = rows;
|
|
|
+};
|
|
|
+
|
|
|
+//排序提交
|
|
|
+const sortingLoading = ref(false);
|
|
|
+const sortingSubmit = async () => {
|
|
|
+ const arr = tableSortingData.value;
|
|
|
+ if (arr.length <= 0) {
|
|
|
+ window?.$message?.warning("暂无数据");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sortingLoading.value = true;
|
|
|
+ const ids = arrToId(arr);
|
|
|
+ const { error, code } = await mainApi.sortNode({ ids });
|
|
|
+ sortingLoading.value = false;
|
|
|
+ if (!error && code === 200) {
|
|
|
+ window?.$message?.success("操作成功");
|
|
|
+ sortingClose();
|
|
|
+ setTreeMode();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+//关闭排序
|
|
|
+const sortingClose = () => {
|
|
|
+ isSortingShow.value = false;
|
|
|
+ tableData.value = [];
|
|
|
+};
|
|
|
+
|
|
|
+//关闭抽屉
|
|
|
+const drawerClose = () => {
|
|
|
+ isShow.value = false;
|
|
|
+ isTreeMode.value = false;
|
|
|
+ nodeInfo.value = {};
|
|
|
+ emit("close");
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+.hc-desk-system-unit-temp .el-card {
|
|
|
+ .hc-card-item-box {
|
|
|
+ height: auto;
|
|
|
+ padding: 10px 14px;
|
|
|
+ .hc-card-item-header {
|
|
|
+ color: #4a4a4a;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .hc-card-item-box + .hc-card-item-box {
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|