Hwf 7 bulan lalu
induk
melakukan
3ec21a4bfd

TEMPAT SAMPAH
src/assets/images/close2.png


TEMPAT SAMPAH
src/assets/images/close3.png


TEMPAT SAMPAH
src/assets/images/file.png


TEMPAT SAMPAH
src/assets/images/user.png


+ 1 - 1
src/assets/styles/element-ui.scss

@@ -81,7 +81,7 @@
         font-size: 14px !important;
       }
       .el-dialog__body {
-        padding: 26px 24.5px 8px !important;
+        padding: 24px !important;
       }
       .el-dialog__header {
         padding: 13px 24.5px;

+ 8 - 4
src/assets/styles/sidebar.scss

@@ -78,16 +78,20 @@
       text-overflow: ellipsis !important;
       white-space: nowrap !important;
     }
-    .el-menu-item {
+    .el-menu-item, .el-sub-menu {
       &:hover {
-        color: #ffffff;
-        background-color: #2c81ff;
+        background-color: #2c81ff !important;
+        .el-sub-menu__title {
+          color: #ffffff !important;
+        }
       }
     }
     .el-menu-item.is-active {
-      color: #ffffff;
       background-color: #2c81ff;
       position: relative;
+      .menu-title {
+        color: #ffffff;
+      }
       &::before {
         content: '';
         width: 4px;

+ 204 - 0
src/components/ContactSelect/index.vue

@@ -0,0 +1,204 @@
+<template>
+  <el-dialog v-model="visible" title="选择消息接收人" width="780px" append-to-body @close="closeDialog">
+    <div class="container">
+      <div class="left-content">
+        <div>人员选择列表</div>
+        <el-input v-model="filterText" placeholder="输入关键字进行搜索" class="input" />
+        <el-tree
+          ref="treeRef"
+          class="filter-tree"
+          :data="treeData"
+          show-checkbox
+          :props="defaultProps"
+          :filter-node-method="filterNode"
+          :default-checked-keys="defaultCheckData"
+          node-key="id"
+          @check-change="handleCheckChange"
+        >
+          <template #default="{ node, data }">
+            <div class="custom-tree-node">
+              <img class="icon" :src="getImage(data)" alt="" />
+              <span>{{ node.label }}</span>
+            </div>
+          </template>
+        </el-tree>
+      </div>
+      <div class="right-content">
+        <div style="display: flex">
+          已选择:
+          <div v-show="checkData.length > 0">{{ checkData.length }}人</div>
+        </div>
+        <div class="select-box">
+          <div v-for="(item, index) in checkData" :key="index" class="select-item">
+            <div class="item-left">
+              <img class="icon" :src="getImage(item)" alt="" />
+              {{ item.label }}
+            </div>
+            <i class="icon-close" @click="uncheckNodeById(item.userId, index)" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="closeDialog">取 消</el-button>
+        <el-button type="primary" @click="confirm">确 定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup name="Contact">
+import { deepClone } from '@/utils/index';
+import userImg from '@/assets/images/user.png';
+import fileImg from '@/assets/images/file.png';
+
+const props = defineProps({
+  modelValue: Boolean,
+  treeData: Array,
+  defaultCheckData: Array
+});
+const emits = defineEmits(['update:modelValue', 'confirm']);
+let visible = ref(false);
+const treeRef = ref();
+let filterText = ref('');
+let defaultProps = reactive({
+  children: 'children',
+  label: 'label'
+});
+let checkData = ref([]);
+
+watch(filterText, (val) => {
+  treeRef.value.filter(val);
+});
+const initData = () => {
+  if (!!props.defaultCheckData) {
+    props.defaultCheckData.forEach((item) => {
+      treeRef.value.setChecked(item.id, true, true);
+    });
+    checkData.value = deepClone(props.defaultCheckData);
+  }
+};
+watch(
+  () => props.modelValue,
+  () => {
+    if (!!props.modelValue) {
+      initData();
+    }
+    visible.value = props.modelValue;
+  },
+  {
+    immediate: true
+  }
+);
+const filterNode = (value, data) => {
+  if (!value) return true;
+  return data.label.indexOf(value) !== -1;
+};
+const getImage = (item) => {
+  if (item.img) {
+    return item.img;
+  } else if (item.deptType) {
+    return fileImg;
+  } else {
+    return userImg;
+  }
+};
+const handleCheckChange = () => {
+  const data = [];
+  const treeData = treeRef.value.getCheckedNodes(false, false);
+  treeData.forEach((item) => {
+    if (!item.deptType) {
+      if (!data.some((obj) => obj.userId === item.userId)) {
+        data.push(item);
+      }
+    }
+  });
+  checkData.value = data;
+};
+const uncheckNodeById = (nodeId, index) => {
+  const nodes = treeRef.value.getCheckedNodes();
+  checkData.value.splice(index, 1);
+  nodes.forEach((item) => {
+    if (item.userId === nodeId) {
+      treeRef.value.setChecked(item.id, false, true);
+    }
+  });
+};
+const closeDialog = () => {
+  emits('update:modelValue', false);
+  treeRef.value.setCheckedKeys([], false);
+  checkData.value = [];
+};
+const confirm = () => {
+  const data = deepClone(checkData.value);
+  closeDialog();
+  emits('confirm', data);
+};
+</script>
+
+<style lang="scss" scoped>
+.container {
+  display: flex;
+  .left-content {
+    flex: 1;
+    border: 1px solid #dcdfe6;
+    border-radius: 5px;
+    height: 400px;
+    overflow-y: auto;
+    padding: 10px;
+    .input {
+      margin: 15px 0 5px;
+    }
+    .filter-tree {
+      :deep(.el-tree-node__content) {
+        height: 45px;
+      }
+      .custom-tree-node {
+        display: flex;
+        width: 100%;
+      }
+    }
+  }
+  .right-content {
+    margin-left: 20px;
+    width: 300px;
+    flex-shrink: 0;
+    border: 1px solid #dcdfe6;
+    border-radius: 5px;
+    height: 400px;
+    overflow-y: auto;
+    padding: 10px;
+    .select-box {
+      .select-item {
+        width: 100%;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 10px 0;
+        .item-left {
+          display: flex;
+          align-items: center;
+        }
+        .icon-close {
+          width: 12px;
+          height: 12px;
+          background: url('@/assets/images/close2.png') no-repeat;
+          background-size: 100% 100%;
+          cursor: pointer;
+          &:hover {
+            background: url('@/assets/images/close3.png') no-repeat;
+            background-size: 100% 100%;
+          }
+        }
+      }
+    }
+  }
+}
+.icon {
+  width: 20px;
+  height: 20px;
+  margin-right: 8px;
+  object-fit: cover;
+}
+</style>

+ 2 - 0
src/types/components.d.ts

@@ -14,6 +14,8 @@ declare module 'vue' {
     BuildCode: typeof import('./../components/BuildCode/index.vue')['default']
     ChunkUpload: typeof import('./../components/ChunkUpload/index.vue')['default']
     CompanyMap: typeof import('./../components/Map/company-map.vue')['default']
+    Contact: typeof import('./../components/Contact/index.vue')['default']
+    ContactSelect: typeof import('./../components/ContactSelect/index.vue')['default']
     DictTag: typeof import('./../components/DictTag/index.vue')['default']
     Editor: typeof import('./../components/Editor/index.vue')['default']
     ElAnchor: typeof import('element-plus/es')['ElAnchor']

+ 59 - 1
src/views/index.vue

@@ -1,13 +1,71 @@
 <template>
   <div class="app-container home">
-    <h2>智慧应急工作台</h2>
+    <h2 @click="show = true">智慧应急工作台</h2>
+    <ContactSelect v-model="show" :treeData="treeData" :default-check-data="selectData" @confirm="handleData" />
   </div>
 </template>
 
 <script setup name="Index" lang="ts">
+import ContactSelect from '@/components/ContactSelect/index.vue';
 const goTarget = (url: string) => {
   window.open(url, '__blank');
 };
+let show = ref(true);
+const treeData = reactive([
+  {
+    id: 1,
+    label: '茂南区',
+    deptType: true,
+    children: [
+      {
+        id: 2,
+        userId: 1,
+        label: '李里丽'
+      },
+      {
+        id: 3,
+        userId: 2,
+        label: '何里'
+      },
+      {
+        id: 4,
+        userId: 3,
+        label: '张力'
+      }
+    ]
+  },
+  {
+    id: 5,
+    label: '电白区',
+    deptType: true,
+    children: [
+      {
+        id: 6,
+        userId: 4,
+        label: '王五'
+      },
+      {
+        id: 7,
+        userId: 5,
+        label: '冯可'
+      },
+      {
+        id: 8,
+        userId: 6,
+        label: '刘森'
+      },
+      {
+        id: 9,
+        userId: 3,
+        label: '张力'
+      }
+    ]
+  }
+]);
+const selectData = ref([]);
+const handleData = (data) => {
+  selectData.value = data;
+};
 </script>
 
 <style scoped lang="scss">