Browse Source

版本管理,数据查看

sujunling 2 years ago
parent
commit
32c5e1e3bc

+ 1 - 0
src/mock/role_me_permissions.json

@@ -6,6 +6,7 @@
     "api:yt:dictItem:delete",
     "assembly:server",
     "resource:centre",
+    "vesion:admin",
     "systemAdmin:system:menu",
     "assembly:upload",
     "api:dataresource:mapupload:maploadDelete",

+ 171 - 0
src/views/dataAdmin/dataAdmin/see/DirManagerDrawer.vue

@@ -0,0 +1,171 @@
+<template>
+    <a-drawer :title="title" :visible="true" :width="540" :body-style="{ paddingBottom: '80px' }"
+        :footer-style="{ textAlign: 'right' }" @close="onClose">
+        <a-form ref="formRef" :model="form" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
+            <!-- 上级节点名称 -->
+            <a-form-item label="上级节点" name="pName">
+                <a-input v-model:value="form.pName" disabled />
+            </a-form-item>
+            <!-- 节点名称 -->
+            <a-form-item label="节点名称" name="name">
+                <a-input v-model:value="form.name" />
+            </a-form-item>
+            <!-- 节点别名 -->
+            <a-form-item label="节点别名" name="alaisename">
+                <a-input v-model:value="form.alaisename" />
+            </a-form-item>
+            <!-- 节点编码 -->
+            <!-- <a-form-item label="节点编码" name="code">
+                <a-input v-model:value="form.code" />
+            </a-form-item> -->
+            <!-- 节点分类 -->
+            <a-form-item label="节点分类" name="type">
+                <a-input v-model:value="form.type" disabled />
+            </a-form-item>
+            <!-- 节点描述 -->
+            <a-form-item label="节点描述" name="remark">
+                <a-textarea v-model:value="form.remark" />
+            </a-form-item>
+        </a-form>
+        <div :style="{
+            position: 'absolute',
+            right: 0,
+            bottom: 0,
+            width: '100%',
+            borderTop: '1px solid #e9e9e9',
+            padding: '10px 16px',
+            background: '#fff',
+            textAlign: 'right',
+            zIndex: 1,
+        }">
+            <a-button style="margin-right: 8px" @click="onClose">取消</a-button>
+            <a-button type="primary" @click="onSubmit">确定</a-button>
+        </div>
+    </a-drawer>
+</template>
+<script>
+import { defineComponent, reactive, ref, onMounted, watch } from 'vue';
+import { message } from 'ant-design-vue';
+import { addNode } from '/@/api/sys/dirManager';
+import { session } from '/@/utils/Memory';
+import { v4 as uuidv4 } from 'uuid';
+
+const props = {
+    formData: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    },
+    drawerTitle: {
+        type: String,
+        default: "新增岗位"
+    }
+}
+export default defineComponent({
+    name: 'DirManagerDrawer',
+    components: {},
+    props,
+    setup(props, { emit }) {
+        const form = reactive({
+            id: "",
+            pid: "",
+            pName: "",
+            name: "",
+            alaisename: "",
+            sortindex: 0,
+            // code: "",
+            type: "",
+            remark: ""
+        });
+        const rules = {
+            name: [{
+                required: true,
+                message: '请输入节点名称',
+                trigger: 'blur'
+            }],
+            // code: [{
+            //     required: true,
+            //     message: '请输入节点编码',
+            //     trigger: 'blur'
+            // }],
+            alaisename: [{
+                required: true,
+                message: '请选择节点别名',
+                trigger: 'blur'
+            }],
+            remark: [{
+                required: true,
+                message: '请输入节点描述',
+                trigger: 'blur'
+            }]
+        };
+        const title = ref(props.drawerTitle)
+        const formRef = ref()
+
+        onMounted(() => {
+            if (Object.keys(props.formData).length) {
+                for (let key in form) {
+                    form[key] = props.formData[key]
+                }
+            };
+        })
+
+        watch(
+            () => [props.formData, props.drawerTitle],
+            ([newF, newD]) => {
+                for (let key in form) {
+                    form[key] = props.formData[key]
+                }
+                title.value = newD
+            },
+            {
+                immediate: true,
+                deep: true
+            }
+        )
+        // 关闭弹窗
+        const onClose = () => {
+            emit('closeDialog')
+            resetForm()
+        };
+        // 提交信息
+        const onSubmit = () => {
+            formRef.value.validate().then(() => {
+                form.id = uuidv4();
+                form.pName = undefined;
+                form.updateuser = session.getItem('userInfo').EMPLOYEE.NAME || '';
+                form.userid = session.getItem('userInfo').EMPLOYEE.EMPLOYEE_ID || '';
+                let param = {
+                    1: session.getItem('tokenV2'),
+                    2: JSON.stringify(form)
+                }
+                addNode(param).then(res => {
+                    if(res.status==='0'){
+                        message.success('添加成功')
+                        emit('onSubmit', true, form.name)
+                    }
+                    onClose();
+                })
+            }).catch((error) => {
+                console.log('error', error);
+            });
+        };
+        // 重置表单
+        const resetForm = () => {
+            formRef.value.resetFields();
+        };
+        return {
+            form,
+            rules,
+            title,
+            formRef,
+            labelCol: { span: 6 },
+            wrapperCol: { span: 18 },
+            onClose,
+            onSubmit,
+            resetForm
+        };
+    },
+});
+</script>

+ 599 - 0
src/views/dataAdmin/dataAdmin/see/index.vue

@@ -0,0 +1,599 @@
+<!-- 目录管理 -->
+<template>
+  <div class="dir-manager" style="height: 100%; width: 100%; padding: 0; margin: 0">
+    <!-- 头部 -->
+    <div class="head">
+      <a-card class="box-card" body-style="{height:'100%'}">
+        <div class="head-rows">
+          <div class="head-col">目录:</div>
+          <div class="head-col"><a-select ref="select" style="width: 220px" :value="inputSearchValue">
+              <a-select-option v-for="item in searchCategory" :key="item.id" :value="item.id">{{
+                item.name
+              }}</a-select-option>
+            </a-select>
+          </div>
+        </div>
+      </a-card>
+    </div>
+    <!-- 左边 -->
+    <div class="mains">
+      <div class="left">
+        <a-card class="box-card" body-style="{height:'100%'}">
+          <a-input-search v-model:value="treeSearchValue" style="margin-bottom: 8px" placeholder="搜索关键字" />
+          <a-tree :tree-data="treeData" @select="select" :expandedKeys="expandedKeys"
+            :auto-expand-parent="autoExpandParent" @expand="onExpand">
+            <template #title="{ title }">
+              <span v-if="title.indexOf(treeSearchValue) > -1">
+                {{ title.substr(0, title.indexOf(treeSearchValue)) }}
+                <span style="color: #f50">{{ treeSearchValue }}</span>
+                {{ title.substr(title.indexOf(treeSearchValue) + treeSearchValue.length) }}
+              </span>
+              <span v-else>{{ title }}</span>
+            </template>
+          </a-tree>
+        </a-card>
+      </div>
+      <!-- 右边 -->
+      <div class="right">
+        <div class="right-top">
+          <a-card class="box-card" body-style="{height:'100%'}">
+            <div style="
+                border-bottom-style: solid;
+                border-bottom-width: 1px;
+                border-bottom-color: #ddd;
+                height: 40px;
+                padding: 10px;
+                padding-top: 0px;
+                margin-top: 0px;
+              ">
+              <a-row>
+                <a-col :span="9"><span style="font-weight: 700; font-size: 16px">节点编辑</span>
+                </a-col>
+                <a-col :span="15">
+                  <a-row type="flex" justify="end">
+                    <div class="btn" style="margin-right: 10px;">
+                      <a-button type="primary" @click="getTreeData">刷新</a-button>
+                    </div>
+                    <div class="btn" style="margin-right: 10px;">
+                      <a-button type="primary" @click="addTreeNode">添加根节点</a-button>
+                    </div>
+                    <div class="btn" style="margin-right: 10px;">
+                      <a-button type="primary" @click="addTreeChildNode">添加子节点</a-button>
+                    </div>
+                    <div class="btn">
+                      <a-popconfirm title="确定删除该节点及其子节点?" @confirm="delTreeNode">
+                        <a-button danger :disabled="!editBtuState">删除节点</a-button>
+                      </a-popconfirm>
+                    </div>
+                  </a-row>
+                </a-col>
+              </a-row>
+            </div>
+            <a-form :model="formState" :labelCol="{ span: 5 }" :wrapperCol="{ span: 15 }" style="margin-top: 20px"
+              layout="horizontal">
+              <a-row :gutter="24">
+                <a-col :span="8">
+                  <a-form-item label="名称">
+                    <a-input v-model:value="formState.name" />
+                  </a-form-item>
+                </a-col>
+                <a-col :span="8">
+                  <a-form-item label="别名">
+                    <a-input v-model:value="formState.alaisename" />
+                  </a-form-item>
+                </a-col>
+                <a-col :span="8">
+                  <a-form-item label="排序">
+                    <a-input v-model:value="formState.sortindex" disabled />
+                  </a-form-item>
+                </a-col>
+              </a-row>
+              <a-row :gutter="24">
+                <a-col :span="8">
+                  <a-form-item label="描述">
+                    <a-textarea v-model:value="formState.remark" />
+                  </a-form-item>
+                </a-col>
+              </a-row>
+            </a-form>
+            <div style="margin-top: 60px">
+              <div class="col-item" style="width: 100%;display: flex;justify-content: flex-end;">
+                <a-button type="primary" style="margin-right: 10px;" @click="moveNode(-1)"
+                  :disabled="!editBtuState">上移节点</a-button>
+                <a-button type="primary" style="margin-right: 10px;" @click="moveNode(1)"
+                  :disabled="!editBtuState">下移节点</a-button>
+                <a-button type="primary" @click="editNode" :disabled="!editBtuState">保存节点</a-button>
+              </div>
+            </div>
+          </a-card>
+        </div>
+
+        <div class="right-bottom">
+          <a-card class="box-card" body-style="{height:'100%'}">
+            <div style="
+                border-bottom-style: solid;
+                border-bottom-width: 1px;
+                border-bottom-color: #ddd;
+                height: 40px;
+              ">
+              <a-row>
+                <a-col :span="10">
+                  <span style="font-weight: 700; font-size: 16px">操作记录</span>
+                </a-col>
+              </a-row>
+            </div>
+            <div class="list-box">
+              <a-list size="small" :data-source="recordsData" v-if="recordsData.length">
+                <template #renderItem="{ item }">
+                  <a-list-item>{{ item }}</a-list-item>
+                </template>
+              </a-list>
+              <a-empty :image="simpleImage" v-else />
+            </div>
+          </a-card>
+        </div>
+      </div>
+    </div>
+    <DirManagerDrawer v-if="ifShowDialog" @closeDialog="ifShowDialog = false" :formData="formState2"
+      :drawerTitle="drawerTitle" @onSubmit="onSubmit"></DirManagerDrawer>
+  </div>
+</template>
+<script>
+import { defineComponent, ref } from 'vue';
+import { getTreeRootId, getTreeList, delNode, updateNode, updateNodeList } from '/@/api/sys/dirManager';
+// import Guid from 'guid';
+import { v4 as uuidv4 } from 'uuid';
+import { session } from '/@/utils/Memory';
+import { message, Empty } from 'ant-design-vue';
+import DirManagerDrawer from './DirManagerDrawer.vue';
+
+export default defineComponent({
+
+  components: { DirManagerDrawer },
+
+  data() {
+    return {
+      // 搜索框数据
+      searchCategory: [],
+      // 树的搜索值
+      treeSearchValue: '',
+      // 树形数据
+      treeData: [],
+      // 表单数据
+      formState: {
+        id: '',
+        pid: '',
+        name: '',
+        alaisename: '',
+        remark: '',
+        sortindex: '',
+      },
+      // 头部搜索框的值
+      inputSearchValue: '',
+      // 新增弹窗表单
+      formState2: {
+        pid: '',
+        pName: '',
+        type: "0",
+        sortindex: 0,
+      },
+      // 编辑按钮使用状态
+      editBtuState: true,
+      //用于树状结构搜索的dataList
+      dataList: [],
+      expandedKeys: [],
+      autoExpandParent: true,
+      ifShowDialog: false,
+      drawerTitle: '',
+      delIds: '',
+      simpleImage: Empty.PRESENTED_IMAGE_SIMPLE,
+      recordsData: []
+    };
+  },
+
+  mounted() {
+    this.getTreeData();
+  },
+
+  watch: {
+    treeSearchValue(value) {
+      if (value) {
+        let expanded = this.dataList
+          .map(item => {
+            if (item.title.indexOf(value) > -1) {
+              return this.getParentKey(item.key, this.treeData);
+            }
+            return null;
+          })
+          .filter((item, i, self) => item && self.indexOf(item) === i);
+        this.expandedKeys = expanded;
+      } else {
+        this.expandedKeys = [];
+      }
+      this.treeSearchValue = value;
+      this.autoExpandParent = true;
+    },
+    formState: {
+      handler(val) {
+        this.editBtuState = Boolean(val.id)
+      },
+      immediate: true,
+      deep: true
+    },
+  },
+
+  methods: {
+    //获取树节点列表
+    getTreeData() {
+      this.treeData = []
+      this.dataList = []
+      this.formState = {
+        id: '',
+        pid: '',
+        name: '',
+        alaisename: '',
+        remark: '',
+        sortindex: ''
+      }
+      let param = {
+        1: session.getItem('tokenV2')
+      }
+      getTreeRootId(param).then(res => {
+        this.searchCategory = JSON.parse(res.result)
+        this.inputSearchValue = this.searchCategory[0].id
+        let param2 = {
+          1: session.getItem('tokenV2'),
+          2: this.searchCategory[0].id
+        }
+        getTreeList(param2).then(res2 => {
+          const data = JSON.parse(res2.result);
+          // 让每层带上自己的孩子节点、title、key
+          data.forEach((item) => {
+            item['title'] = item.name;
+            item['key'] = item.id;
+            data.forEach((item2) => {
+              if (item.id == item2.pid) {
+                if (item.children) {
+                  item.children.push(item2);
+                } else {
+                  item['children'] = [];
+                  item.children.push(item2);
+                }
+              }
+            });
+          });
+          data.forEach((item) => {
+            if (this.searchCategory[0].id === item.pid) {
+              this.treeData.push(item);
+            }
+          });
+          this.generateList(this.treeData)
+        })
+      })
+    },
+    //生成用于树搜索的dataList
+    generateList(data) {
+      for (let i = 0; i < data.length; i++) {
+        const node = data[i];
+        const key = node.key;
+        this.dataList.push({
+          key,
+          title: node.title,
+        });
+        if (node.children) {
+          this.generateList(node.children);
+        }
+      }
+    },
+    //获取所有父节点key
+    getParentKey(key, tree) {
+      let parentKey;
+      for (let i = 0; i < tree.length; i++) {
+        const node = tree[i];
+        if (node.children) {
+          if (node.children.some(item => item.key === key)) {
+            parentKey = node.key;
+          } else if (this.getParentKey(key, node.children)) {
+            parentKey = this.getParentKey(key, node.children);
+          }
+        }
+      }
+      return parentKey;
+    },
+    //树结构展开时触发
+    onExpand(keys) {
+      this.expandedKeys = keys;
+      this.autoExpandParent = false;
+    },
+
+    // 点击树节点
+    select(selectkey, { selectedNodes, node }) {
+      console.log(node.dataRef);
+      if (selectkey.length) {
+        this.formState = {
+          ...node.dataRef
+        }
+      } else {
+        this.formState = {
+          id: '',
+          pid: '',
+          name: '',
+          alaisename: '',
+          remark: '',
+          sortindex: ''
+        }
+      }
+    },
+
+    // 添加树节点
+    addTreeNode() {
+      if (this.inputSearchValue) {
+        this.searchCategory.forEach((item) => {
+          if (this.inputSearchValue === item.id) {
+            this.formState2.pName = item.name;
+            this.formState2.pid = item.id;
+            this.formState2.sortindex = this.treeData.length;
+            this.drawerTitle = '添加根节点'
+            this.ifShowDialog = true;
+          }
+        });
+      } else {
+        //没有选择头部分类
+        message.error('没有目录')
+      }
+    },
+
+    // 添加子节点
+    addTreeChildNode() {
+      // message.warning('请选择需要添加子节点的父节点!', 5);
+      if (this.formState.id) {
+        this.formState2.pName = this.formState.name;
+        this.formState2.pid = this.formState.id;
+        this.formState2.sortindex = this.formState.children ? this.formState.children.length : 0;
+        this.drawerTitle = '添加子节点'
+        this.ifShowDialog = true;
+      } else {
+        message.error('请先选择父节点')
+      }
+    },
+
+    //删除节点
+    delTreeNode() {
+      if (this.formState.id) {
+        this.delIds = ''
+        this.getChildrenIds(this.formState)
+        let param = {
+          1: session.getItem('tokenV2'),
+          2: this.delIds
+        }
+        delNode(param).then(res => {
+          if (res.result === "true") {
+            message.success('删除成功');
+            this.recordsData.push(`删除节点:${this.formState.name}`)
+            this.getTreeData();
+          } else {
+            message.error('删除失败');
+          }
+        })
+      } else {
+        message.error('请先选择节点');
+      }
+    },
+
+    //获取节点下所有子节点ids
+    getChildrenIds(node) {
+      this.delIds += node.id + ','
+      if (node.children && node.children.length) {
+        node.children.forEach(item => {
+          this.getChildrenIds(item)
+        })
+      }
+    },
+
+    // 修改节点
+    editNode() {
+      if (this.editBtuState) {
+        let editData = {
+          id: this.formState.id,
+          pid: this.formState.pid,
+          name: this.formState.name,
+          alaisename: this.formState.alaisename,
+          type: this.formState.type,
+          sortindex: this.formState.sortindex,
+          remark: this.formState.remark,
+        };
+        let param = {
+          1: session.getItem('tokenV2'),
+          2: JSON.stringify(editData)
+        }
+        updateNode(param).then(res => {
+          if (res.status === '0') {
+            message.success('修改成功')
+            this.recordsData.push(`修改节点:${this.formState.name}`)
+            this.searchCategory = [];
+            this.getTreeData();
+          }
+        })
+      }
+    },
+
+    //移动节点
+    async moveNode(flag) {
+      //判断是否为根节点, 并获取同层级的数组
+      let data = []
+      let mvNode = null
+      if (this.formState.pid !== this.inputSearchValue) {
+        let param = {
+          1: session.getItem('tokenV2'),
+          2: this.formState.pid
+        }
+        let resData = await getTreeList(param)
+        data = JSON.parse(resData.result);
+        data = data.filter(item => item.id !== this.formState.pid)
+      } else {
+        data = this.treeData
+      }
+      data.sort((a, b) => a['sortindex'] - b['sortindex'])
+      // 判断移动方向
+      if (flag === 1) {
+        // 判断是否是最后一个
+        data.forEach((item, index) => {
+          if (this.formState.id === item.id) {
+            if ((index + 1) >= data.length) {
+              message.info('已经是最后一个节点')
+            } else {
+              mvNode = data[index + 1]
+            }
+          }
+        })
+      } else {
+        // 判断是否是第一个
+        data.forEach((item, index) => {
+          if (this.formState.id === item.id) {
+            if ((index - 1) < 0) {
+              message.info('已经是第一个节点')
+            } else {
+              mvNode = data[index - 1]
+            }
+          }
+        })
+      }
+      //此处使用批量更新
+      if (mvNode) {
+        let editData = {
+          id: this.formState.id,
+          pid: this.formState.pid,
+          name: this.formState.name,
+          alaisename: this.formState.alaisename,
+          type: this.formState.type,
+          sortindex: mvNode.sortindex,
+          remark: this.formState.remark,
+        };
+        let mvData = {
+          id: mvNode.id,
+          pid: mvNode.pid,
+          name: mvNode.name,
+          alaisename: mvNode.alaisename,
+          type: mvNode.type,
+          sortindex: this.formState.sortindex,
+          remark: mvNode.remark,
+        }
+        let param = {
+          1: session.getItem('tokenV2'),
+          2: JSON.stringify([editData, mvData])
+        }
+        updateNodeList(param).then(res => {
+          if (res.status==="0"){
+            message.success('修改成功')
+            this.recordsData.push(`移动节点:${this.formState.name}`)
+            this.searchCategory = [];
+            this.getTreeData();
+          }
+        })
+      }
+    },
+
+    //弹窗确认
+    onSubmit(e, item) {
+      this.ifShowDialog = false
+      if (e) {
+        this.recordsData.push(`添加节点:${item}`)
+        this.getTreeData();
+      }
+    }
+  },
+});
+</script>
+
+<style lang="less" scoped>
+.dir-manager {
+  .head {
+    padding: 20px 20px 0;
+
+    .box-card {
+      height: calc(100% - 0px);
+
+      .head-rows {
+        width: 500px;
+        display: flex;
+        align-items: center;
+      }
+    }
+  }
+
+  .mains {
+    .box-card {
+      height: calc(100% - 0px);
+    }
+
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 20px;
+    height: calc(100% - 102px);
+
+    .left {
+      height: calc(100% - 0px);
+      width: 20%;
+    }
+
+    .right {
+      display: flex;
+      flex-direction: column; //设置主轴为y轴
+      justify-content: flex-start; //子元素排列方式,
+      align-items: center; //设置侧轴上子元素对齐方式
+      padding: 0 0 0 20px;
+      height: calc(100% - 0px);
+      width: 100%;
+      // height: 50%;
+      // width: 78%;
+      // margin-left: 1%;
+      // margin-right: 1%;
+      // border: 1px red solid;
+
+      .right-top {
+        height: 50%;
+        width: 100%;
+        margin-left: 1%;
+        margin-right: 1%;
+        margin-bottom: 2%;
+
+        .right-top-item {
+          border-style: solid;
+          border-width: 1px;
+          border-color: #ddd;
+          box-shadow: 0 0 10px #ddd;
+          height: 250px;
+          margin-top: 10px;
+          padding: 10px;
+        }
+      }
+
+      .right-bottom {
+        height: 50%;
+        width: 100%;
+        margin-left: 1%;
+        margin-right: 1%;
+
+        .list-box {
+          height: 282px;
+          overflow: auto;
+          // display: flex;
+          // align-items: center;
+          // justify-content: center;
+        }
+
+        .right-bottom-item {
+          border-style: solid;
+          border-width: 1px;
+          border-color: #ddd;
+          box-shadow: 0 0 10px #ddd;
+          height: 250px;
+          margin-top: 10px;
+          padding: 10px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 171 - 0
src/views/dataAdmin/dataAdmin/version/DirManagerDrawer.vue

@@ -0,0 +1,171 @@
+<template>
+    <a-drawer :title="title" :visible="true" :width="540" :body-style="{ paddingBottom: '80px' }"
+        :footer-style="{ textAlign: 'right' }" @close="onClose">
+        <a-form ref="formRef" :model="form" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
+            <!-- 上级节点名称 -->
+            <a-form-item label="上级节点" name="pName">
+                <a-input v-model:value="form.pName" disabled />
+            </a-form-item>
+            <!-- 节点名称 -->
+            <a-form-item label="节点名称" name="name">
+                <a-input v-model:value="form.name" />
+            </a-form-item>
+            <!-- 节点别名 -->
+            <a-form-item label="节点别名" name="alaisename">
+                <a-input v-model:value="form.alaisename" />
+            </a-form-item>
+            <!-- 节点编码 -->
+            <!-- <a-form-item label="节点编码" name="code">
+                <a-input v-model:value="form.code" />
+            </a-form-item> -->
+            <!-- 节点分类 -->
+            <a-form-item label="节点分类" name="type">
+                <a-input v-model:value="form.type" disabled />
+            </a-form-item>
+            <!-- 节点描述 -->
+            <a-form-item label="节点描述" name="remark">
+                <a-textarea v-model:value="form.remark" />
+            </a-form-item>
+        </a-form>
+        <div :style="{
+            position: 'absolute',
+            right: 0,
+            bottom: 0,
+            width: '100%',
+            borderTop: '1px solid #e9e9e9',
+            padding: '10px 16px',
+            background: '#fff',
+            textAlign: 'right',
+            zIndex: 1,
+        }">
+            <a-button style="margin-right: 8px" @click="onClose">取消</a-button>
+            <a-button type="primary" @click="onSubmit">确定</a-button>
+        </div>
+    </a-drawer>
+</template>
+<script>
+import { defineComponent, reactive, ref, onMounted, watch } from 'vue';
+import { message } from 'ant-design-vue';
+import { addNode } from '/@/api/sys/dirManager';
+import { session } from '/@/utils/Memory';
+import { v4 as uuidv4 } from 'uuid';
+
+const props = {
+    formData: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    },
+    drawerTitle: {
+        type: String,
+        default: "新增岗位"
+    }
+}
+export default defineComponent({
+    name: 'DirManagerDrawer',
+    components: {},
+    props,
+    setup(props, { emit }) {
+        const form = reactive({
+            id: "",
+            pid: "",
+            pName: "",
+            name: "",
+            alaisename: "",
+            sortindex: 0,
+            // code: "",
+            type: "",
+            remark: ""
+        });
+        const rules = {
+            name: [{
+                required: true,
+                message: '请输入节点名称',
+                trigger: 'blur'
+            }],
+            // code: [{
+            //     required: true,
+            //     message: '请输入节点编码',
+            //     trigger: 'blur'
+            // }],
+            alaisename: [{
+                required: true,
+                message: '请选择节点别名',
+                trigger: 'blur'
+            }],
+            remark: [{
+                required: true,
+                message: '请输入节点描述',
+                trigger: 'blur'
+            }]
+        };
+        const title = ref(props.drawerTitle)
+        const formRef = ref()
+
+        onMounted(() => {
+            if (Object.keys(props.formData).length) {
+                for (let key in form) {
+                    form[key] = props.formData[key]
+                }
+            };
+        })
+
+        watch(
+            () => [props.formData, props.drawerTitle],
+            ([newF, newD]) => {
+                for (let key in form) {
+                    form[key] = props.formData[key]
+                }
+                title.value = newD
+            },
+            {
+                immediate: true,
+                deep: true
+            }
+        )
+        // 关闭弹窗
+        const onClose = () => {
+            emit('closeDialog')
+            resetForm()
+        };
+        // 提交信息
+        const onSubmit = () => {
+            formRef.value.validate().then(() => {
+                form.id = uuidv4();
+                form.pName = undefined;
+                form.updateuser = session.getItem('userInfo').EMPLOYEE.NAME || '';
+                form.userid = session.getItem('userInfo').EMPLOYEE.EMPLOYEE_ID || '';
+                let param = {
+                    1: session.getItem('tokenV2'),
+                    2: JSON.stringify(form)
+                }
+                addNode(param).then(res => {
+                    if(res.status==='0'){
+                        message.success('添加成功')
+                        emit('onSubmit', true, form.name)
+                    }
+                    onClose();
+                })
+            }).catch((error) => {
+                console.log('error', error);
+            });
+        };
+        // 重置表单
+        const resetForm = () => {
+            formRef.value.resetFields();
+        };
+        return {
+            form,
+            rules,
+            title,
+            formRef,
+            labelCol: { span: 6 },
+            wrapperCol: { span: 18 },
+            onClose,
+            onSubmit,
+            resetForm
+        };
+    },
+});
+</script>

+ 599 - 0
src/views/dataAdmin/dataAdmin/version/index.vue

@@ -0,0 +1,599 @@
+<!-- 目录管理 -->
+<template>
+  <div class="dir-manager" style="height: 100%; width: 100%; padding: 0; margin: 0">
+    <!-- 头部 -->
+    <div class="head">
+      <a-card class="box-card" body-style="{height:'100%'}">
+        <div class="head-rows">
+          <div class="head-col">目录:</div>
+          <div class="head-col"><a-select ref="select" style="width: 220px" :value="inputSearchValue">
+              <a-select-option v-for="item in searchCategory" :key="item.id" :value="item.id">{{
+                item.name
+              }}</a-select-option>
+            </a-select>
+          </div>
+        </div>
+      </a-card>
+    </div>
+    <!-- 左边 -->
+    <div class="mains">
+      <div class="left">
+        <a-card class="box-card" body-style="{height:'100%'}">
+          <a-input-search v-model:value="treeSearchValue" style="margin-bottom: 8px" placeholder="搜索关键字" />
+          <a-tree :tree-data="treeData" @select="select" :expandedKeys="expandedKeys"
+            :auto-expand-parent="autoExpandParent" @expand="onExpand">
+            <template #title="{ title }">
+              <span v-if="title.indexOf(treeSearchValue) > -1">
+                {{ title.substr(0, title.indexOf(treeSearchValue)) }}
+                <span style="color: #f50">{{ treeSearchValue }}</span>
+                {{ title.substr(title.indexOf(treeSearchValue) + treeSearchValue.length) }}
+              </span>
+              <span v-else>{{ title }}</span>
+            </template>
+          </a-tree>
+        </a-card>
+      </div>
+      <!-- 右边 -->
+      <div class="right">
+        <div class="right-top">
+          <a-card class="box-card" body-style="{height:'100%'}">
+            <div style="
+                border-bottom-style: solid;
+                border-bottom-width: 1px;
+                border-bottom-color: #ddd;
+                height: 40px;
+                padding: 10px;
+                padding-top: 0px;
+                margin-top: 0px;
+              ">
+              <a-row>
+                <a-col :span="9"><span style="font-weight: 700; font-size: 16px">节点编辑</span>
+                </a-col>
+                <a-col :span="15">
+                  <a-row type="flex" justify="end">
+                    <div class="btn" style="margin-right: 10px;">
+                      <a-button type="primary" @click="getTreeData">刷新</a-button>
+                    </div>
+                    <div class="btn" style="margin-right: 10px;">
+                      <a-button type="primary" @click="addTreeNode">添加根节点</a-button>
+                    </div>
+                    <div class="btn" style="margin-right: 10px;">
+                      <a-button type="primary" @click="addTreeChildNode">添加子节点</a-button>
+                    </div>
+                    <div class="btn">
+                      <a-popconfirm title="确定删除该节点及其子节点?" @confirm="delTreeNode">
+                        <a-button danger :disabled="!editBtuState">删除节点</a-button>
+                      </a-popconfirm>
+                    </div>
+                  </a-row>
+                </a-col>
+              </a-row>
+            </div>
+            <a-form :model="formState" :labelCol="{ span: 5 }" :wrapperCol="{ span: 15 }" style="margin-top: 20px"
+              layout="horizontal">
+              <a-row :gutter="24">
+                <a-col :span="8">
+                  <a-form-item label="名称">
+                    <a-input v-model:value="formState.name" />
+                  </a-form-item>
+                </a-col>
+                <a-col :span="8">
+                  <a-form-item label="别名">
+                    <a-input v-model:value="formState.alaisename" />
+                  </a-form-item>
+                </a-col>
+                <a-col :span="8">
+                  <a-form-item label="排序">
+                    <a-input v-model:value="formState.sortindex" disabled />
+                  </a-form-item>
+                </a-col>
+              </a-row>
+              <a-row :gutter="24">
+                <a-col :span="8">
+                  <a-form-item label="描述">
+                    <a-textarea v-model:value="formState.remark" />
+                  </a-form-item>
+                </a-col>
+              </a-row>
+            </a-form>
+            <div style="margin-top: 60px">
+              <div class="col-item" style="width: 100%;display: flex;justify-content: flex-end;">
+                <a-button type="primary" style="margin-right: 10px;" @click="moveNode(-1)"
+                  :disabled="!editBtuState">上移节点</a-button>
+                <a-button type="primary" style="margin-right: 10px;" @click="moveNode(1)"
+                  :disabled="!editBtuState">下移节点</a-button>
+                <a-button type="primary" @click="editNode" :disabled="!editBtuState">保存节点</a-button>
+              </div>
+            </div>
+          </a-card>
+        </div>
+
+        <div class="right-bottom">
+          <a-card class="box-card" body-style="{height:'100%'}">
+            <div style="
+                border-bottom-style: solid;
+                border-bottom-width: 1px;
+                border-bottom-color: #ddd;
+                height: 40px;
+              ">
+              <a-row>
+                <a-col :span="10">
+                  <span style="font-weight: 700; font-size: 16px">操作记录</span>
+                </a-col>
+              </a-row>
+            </div>
+            <div class="list-box">
+              <a-list size="small" :data-source="recordsData" v-if="recordsData.length">
+                <template #renderItem="{ item }">
+                  <a-list-item>{{ item }}</a-list-item>
+                </template>
+              </a-list>
+              <a-empty :image="simpleImage" v-else />
+            </div>
+          </a-card>
+        </div>
+      </div>
+    </div>
+    <DirManagerDrawer v-if="ifShowDialog" @closeDialog="ifShowDialog = false" :formData="formState2"
+      :drawerTitle="drawerTitle" @onSubmit="onSubmit"></DirManagerDrawer>
+  </div>
+</template>
+<script>
+import { defineComponent, ref } from 'vue';
+import { getTreeRootId, getTreeList, delNode, updateNode, updateNodeList } from '/@/api/sys/dirManager';
+// import Guid from 'guid';
+import { v4 as uuidv4 } from 'uuid';
+import { session } from '/@/utils/Memory';
+import { message, Empty } from 'ant-design-vue';
+import DirManagerDrawer from './DirManagerDrawer.vue';
+
+export default defineComponent({
+
+  components: { DirManagerDrawer },
+
+  data() {
+    return {
+      // 搜索框数据
+      searchCategory: [],
+      // 树的搜索值
+      treeSearchValue: '',
+      // 树形数据
+      treeData: [],
+      // 表单数据
+      formState: {
+        id: '',
+        pid: '',
+        name: '',
+        alaisename: '',
+        remark: '',
+        sortindex: '',
+      },
+      // 头部搜索框的值
+      inputSearchValue: '',
+      // 新增弹窗表单
+      formState2: {
+        pid: '',
+        pName: '',
+        type: "0",
+        sortindex: 0,
+      },
+      // 编辑按钮使用状态
+      editBtuState: true,
+      //用于树状结构搜索的dataList
+      dataList: [],
+      expandedKeys: [],
+      autoExpandParent: true,
+      ifShowDialog: false,
+      drawerTitle: '',
+      delIds: '',
+      simpleImage: Empty.PRESENTED_IMAGE_SIMPLE,
+      recordsData: []
+    };
+  },
+
+  mounted() {
+    this.getTreeData();
+  },
+
+  watch: {
+    treeSearchValue(value) {
+      if (value) {
+        let expanded = this.dataList
+          .map(item => {
+            if (item.title.indexOf(value) > -1) {
+              return this.getParentKey(item.key, this.treeData);
+            }
+            return null;
+          })
+          .filter((item, i, self) => item && self.indexOf(item) === i);
+        this.expandedKeys = expanded;
+      } else {
+        this.expandedKeys = [];
+      }
+      this.treeSearchValue = value;
+      this.autoExpandParent = true;
+    },
+    formState: {
+      handler(val) {
+        this.editBtuState = Boolean(val.id)
+      },
+      immediate: true,
+      deep: true
+    },
+  },
+
+  methods: {
+    //获取树节点列表
+    getTreeData() {
+      this.treeData = []
+      this.dataList = []
+      this.formState = {
+        id: '',
+        pid: '',
+        name: '',
+        alaisename: '',
+        remark: '',
+        sortindex: ''
+      }
+      let param = {
+        1: session.getItem('tokenV2')
+      }
+      getTreeRootId(param).then(res => {
+        this.searchCategory = JSON.parse(res.result)
+        this.inputSearchValue = this.searchCategory[0].id
+        let param2 = {
+          1: session.getItem('tokenV2'),
+          2: this.searchCategory[0].id
+        }
+        getTreeList(param2).then(res2 => {
+          const data = JSON.parse(res2.result);
+          // 让每层带上自己的孩子节点、title、key
+          data.forEach((item) => {
+            item['title'] = item.name;
+            item['key'] = item.id;
+            data.forEach((item2) => {
+              if (item.id == item2.pid) {
+                if (item.children) {
+                  item.children.push(item2);
+                } else {
+                  item['children'] = [];
+                  item.children.push(item2);
+                }
+              }
+            });
+          });
+          data.forEach((item) => {
+            if (this.searchCategory[0].id === item.pid) {
+              this.treeData.push(item);
+            }
+          });
+          this.generateList(this.treeData)
+        })
+      })
+    },
+    //生成用于树搜索的dataList
+    generateList(data) {
+      for (let i = 0; i < data.length; i++) {
+        const node = data[i];
+        const key = node.key;
+        this.dataList.push({
+          key,
+          title: node.title,
+        });
+        if (node.children) {
+          this.generateList(node.children);
+        }
+      }
+    },
+    //获取所有父节点key
+    getParentKey(key, tree) {
+      let parentKey;
+      for (let i = 0; i < tree.length; i++) {
+        const node = tree[i];
+        if (node.children) {
+          if (node.children.some(item => item.key === key)) {
+            parentKey = node.key;
+          } else if (this.getParentKey(key, node.children)) {
+            parentKey = this.getParentKey(key, node.children);
+          }
+        }
+      }
+      return parentKey;
+    },
+    //树结构展开时触发
+    onExpand(keys) {
+      this.expandedKeys = keys;
+      this.autoExpandParent = false;
+    },
+
+    // 点击树节点
+    select(selectkey, { selectedNodes, node }) {
+      console.log(node.dataRef);
+      if (selectkey.length) {
+        this.formState = {
+          ...node.dataRef
+        }
+      } else {
+        this.formState = {
+          id: '',
+          pid: '',
+          name: '',
+          alaisename: '',
+          remark: '',
+          sortindex: ''
+        }
+      }
+    },
+
+    // 添加树节点
+    addTreeNode() {
+      if (this.inputSearchValue) {
+        this.searchCategory.forEach((item) => {
+          if (this.inputSearchValue === item.id) {
+            this.formState2.pName = item.name;
+            this.formState2.pid = item.id;
+            this.formState2.sortindex = this.treeData.length;
+            this.drawerTitle = '添加根节点'
+            this.ifShowDialog = true;
+          }
+        });
+      } else {
+        //没有选择头部分类
+        message.error('没有目录')
+      }
+    },
+
+    // 添加子节点
+    addTreeChildNode() {
+      // message.warning('请选择需要添加子节点的父节点!', 5);
+      if (this.formState.id) {
+        this.formState2.pName = this.formState.name;
+        this.formState2.pid = this.formState.id;
+        this.formState2.sortindex = this.formState.children ? this.formState.children.length : 0;
+        this.drawerTitle = '添加子节点'
+        this.ifShowDialog = true;
+      } else {
+        message.error('请先选择父节点')
+      }
+    },
+
+    //删除节点
+    delTreeNode() {
+      if (this.formState.id) {
+        this.delIds = ''
+        this.getChildrenIds(this.formState)
+        let param = {
+          1: session.getItem('tokenV2'),
+          2: this.delIds
+        }
+        delNode(param).then(res => {
+          if (res.result === "true") {
+            message.success('删除成功');
+            this.recordsData.push(`删除节点:${this.formState.name}`)
+            this.getTreeData();
+          } else {
+            message.error('删除失败');
+          }
+        })
+      } else {
+        message.error('请先选择节点');
+      }
+    },
+
+    //获取节点下所有子节点ids
+    getChildrenIds(node) {
+      this.delIds += node.id + ','
+      if (node.children && node.children.length) {
+        node.children.forEach(item => {
+          this.getChildrenIds(item)
+        })
+      }
+    },
+
+    // 修改节点
+    editNode() {
+      if (this.editBtuState) {
+        let editData = {
+          id: this.formState.id,
+          pid: this.formState.pid,
+          name: this.formState.name,
+          alaisename: this.formState.alaisename,
+          type: this.formState.type,
+          sortindex: this.formState.sortindex,
+          remark: this.formState.remark,
+        };
+        let param = {
+          1: session.getItem('tokenV2'),
+          2: JSON.stringify(editData)
+        }
+        updateNode(param).then(res => {
+          if (res.status === '0') {
+            message.success('修改成功')
+            this.recordsData.push(`修改节点:${this.formState.name}`)
+            this.searchCategory = [];
+            this.getTreeData();
+          }
+        })
+      }
+    },
+
+    //移动节点
+    async moveNode(flag) {
+      //判断是否为根节点, 并获取同层级的数组
+      let data = []
+      let mvNode = null
+      if (this.formState.pid !== this.inputSearchValue) {
+        let param = {
+          1: session.getItem('tokenV2'),
+          2: this.formState.pid
+        }
+        let resData = await getTreeList(param)
+        data = JSON.parse(resData.result);
+        data = data.filter(item => item.id !== this.formState.pid)
+      } else {
+        data = this.treeData
+      }
+      data.sort((a, b) => a['sortindex'] - b['sortindex'])
+      // 判断移动方向
+      if (flag === 1) {
+        // 判断是否是最后一个
+        data.forEach((item, index) => {
+          if (this.formState.id === item.id) {
+            if ((index + 1) >= data.length) {
+              message.info('已经是最后一个节点')
+            } else {
+              mvNode = data[index + 1]
+            }
+          }
+        })
+      } else {
+        // 判断是否是第一个
+        data.forEach((item, index) => {
+          if (this.formState.id === item.id) {
+            if ((index - 1) < 0) {
+              message.info('已经是第一个节点')
+            } else {
+              mvNode = data[index - 1]
+            }
+          }
+        })
+      }
+      //此处使用批量更新
+      if (mvNode) {
+        let editData = {
+          id: this.formState.id,
+          pid: this.formState.pid,
+          name: this.formState.name,
+          alaisename: this.formState.alaisename,
+          type: this.formState.type,
+          sortindex: mvNode.sortindex,
+          remark: this.formState.remark,
+        };
+        let mvData = {
+          id: mvNode.id,
+          pid: mvNode.pid,
+          name: mvNode.name,
+          alaisename: mvNode.alaisename,
+          type: mvNode.type,
+          sortindex: this.formState.sortindex,
+          remark: mvNode.remark,
+        }
+        let param = {
+          1: session.getItem('tokenV2'),
+          2: JSON.stringify([editData, mvData])
+        }
+        updateNodeList(param).then(res => {
+          if (res.status==="0"){
+            message.success('修改成功')
+            this.recordsData.push(`移动节点:${this.formState.name}`)
+            this.searchCategory = [];
+            this.getTreeData();
+          }
+        })
+      }
+    },
+
+    //弹窗确认
+    onSubmit(e, item) {
+      this.ifShowDialog = false
+      if (e) {
+        this.recordsData.push(`添加节点:${item}`)
+        this.getTreeData();
+      }
+    }
+  },
+});
+</script>
+
+<style lang="less" scoped>
+.dir-manager {
+  .head {
+    padding: 20px 20px 0;
+
+    .box-card {
+      height: calc(100% - 0px);
+
+      .head-rows {
+        width: 500px;
+        display: flex;
+        align-items: center;
+      }
+    }
+  }
+
+  .mains {
+    .box-card {
+      height: calc(100% - 0px);
+    }
+
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 20px;
+    height: calc(100% - 102px);
+
+    .left {
+      height: calc(100% - 0px);
+      width: 20%;
+    }
+
+    .right {
+      display: flex;
+      flex-direction: column; //设置主轴为y轴
+      justify-content: flex-start; //子元素排列方式,
+      align-items: center; //设置侧轴上子元素对齐方式
+      padding: 0 0 0 20px;
+      height: calc(100% - 0px);
+      width: 100%;
+      // height: 50%;
+      // width: 78%;
+      // margin-left: 1%;
+      // margin-right: 1%;
+      // border: 1px red solid;
+
+      .right-top {
+        height: 50%;
+        width: 100%;
+        margin-left: 1%;
+        margin-right: 1%;
+        margin-bottom: 2%;
+
+        .right-top-item {
+          border-style: solid;
+          border-width: 1px;
+          border-color: #ddd;
+          box-shadow: 0 0 10px #ddd;
+          height: 250px;
+          margin-top: 10px;
+          padding: 10px;
+        }
+      }
+
+      .right-bottom {
+        height: 50%;
+        width: 100%;
+        margin-left: 1%;
+        margin-right: 1%;
+
+        .list-box {
+          height: 282px;
+          overflow: auto;
+          // display: flex;
+          // align-items: center;
+          // justify-content: center;
+        }
+
+        .right-bottom-item {
+          border-style: solid;
+          border-width: 1px;
+          border-color: #ddd;
+          box-shadow: 0 0 10px #ddd;
+          height: 250px;
+          margin-top: 10px;
+          padding: 10px;
+        }
+      }
+    }
+  }
+}
+</style>