Procházet zdrojové kódy

feat: 人员分组新版交互实现

wyf před 1 rokem
rodič
revize
eb60571bae

+ 37 - 0
src/api/message/person-group.ts

@@ -1,4 +1,6 @@
 import { http } from '@/utils/http/axios';
+
+// 已弃用
 export function queryUserTree() {
   return http.request({
     url: '/dept/queryUserTree',
@@ -73,3 +75,38 @@ export function queryUserGroupDetail(userGroupList: number[]) {
     method: 'post',
   });
 }
+
+export interface QueryPersonFilterParams {
+  deptName?: string;
+  nickname?: string;
+  staffNo?: string;
+  pageNumber: number;
+  pageSize: number;
+}
+export function queryPersonFilterList(params: QueryPersonFilterParams) {
+  return http.request({
+    url: '/dept/queryAvailableUserList',
+    method: 'post',
+    params,
+  });
+}
+
+export interface QueryPersonFilterListReturn {
+  total: number;
+  availableUserDTOS: FilterPersonInfo[];
+}
+
+export interface SelectedFilterPersonInfo {
+  id: number;
+  nickname: string;
+  staffNo: string;
+}
+
+export interface FilterPersonInfo extends SelectedFilterPersonInfo {
+  id: number;
+  nickname: string;
+  deptId: number;
+  deptName: string;
+  staffNo: string;
+  checked?: boolean;
+}

binární
src/assets/images/person-group-empty.png


+ 172 - 0
src/views/message/components/PersonFilterSelection.vue

@@ -0,0 +1,172 @@
+<template>
+  <div class="person-filter-selection">
+    <div class="left">
+      <div class="filter-title">
+        <el-input v-model="personFilterValue" style="width: 253px" placeholder="请输入搜索的内容">
+          <template #prepend>
+            <el-select v-model="personFilterType" style="width: 74px" value-key="type">
+              <el-option
+                v-for="item in FILTER_TYPES"
+                :key="item.type"
+                :value="item"
+                :label="item.label"
+              />
+            </el-select>
+          </template>
+        </el-input>
+        <div style="float: right">
+          <el-button
+            type="primary"
+            @click="debouncedGetPersonFilterList"
+            style="width: 65px; height: 32px; margin-right: 11px"
+            >搜 索</el-button
+          >
+        </div>
+      </div>
+      <!-- 搜索结果展示 -->
+      <div
+        v-if="personFilterList && personFilterList.availableUserDTOS.length"
+        class="filter-result"
+      >
+        <div v-for="person in personFilterList.availableUserDTOS" :key="person.id">
+          <el-checkbox
+            v-model="person.checked"
+            @change="handleSelect($event, person)"
+          ></el-checkbox>
+          {{ person.nickname }}
+        </div>
+      </div>
+      <div v-else-if="!personFilterList" class="filter-result-empty">
+        <img src="@/assets/images/person-group-empty.png" alt="" />
+        <div style="position: relative; top: -50px">请选择类型并搜索人员</div>
+      </div>
+      <div v-else class="filter-result-empty">
+        <div>未搜索到相关内容</div><div>请重新搜索</div>
+      </div>
+    </div>
+    <div class="right" style="margin-left: 16px">
+      <div class="head" style="margin-bottom: 22px">
+        <span
+          style="font-weight: 400; font-size: 16px; color: rgba(0, 0, 0, 0.88); line-height: 22px"
+          >已选择:{{ selectedPersonList.length }}人</span
+        >
+      </div>
+      <div class="selected">
+        <el-tag
+          v-for="person in selectedPersonList"
+          :key="person.id"
+          closable
+          @close="handleRemoveSelectedPerson(person.id)"
+        >
+          {{ person.nickname }}
+        </el-tag>
+      </div>
+      <div class="footer">
+        <el-button @click="handleCancle"> 取消 </el-button>
+        <el-button type="primary" @click="handleSubmit">提交</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { onMounted } from 'vue';
+  import { usePersonGroupFilterList } from '../persongroup/hook/usePersonGroupFilterList';
+  import { SelectedFilterPersonInfo } from '@/api/message/person-group';
+  const {
+    FILTER_TYPES,
+    personFilterType,
+    personFilterValue,
+    // personFilterParams,
+    personFilterList,
+    selectedPersonList,
+    debouncedGetPersonFilterList,
+    handleAddSelectedPerson,
+    handleRemoveSelectedPerson,
+  } = usePersonGroupFilterList();
+  const emit = defineEmits(['cancel', 'submit']);
+
+  const props = defineProps<{
+    initSelected?: SelectedFilterPersonInfo[];
+  }>();
+
+  onMounted(() => {
+    if (props.initSelected) selectedPersonList.value = [...props.initSelected];
+  });
+
+  const handleSelect = (v: boolean, person: any) => {
+    if (v) {
+      handleAddSelectedPerson(person);
+    } else {
+      handleRemoveSelectedPerson(person.id);
+    }
+  };
+  const handleCancle = () => {
+    emit('cancel');
+  };
+  const handleSubmit = () => {
+    emit('submit', selectedPersonList.value);
+  };
+</script>
+
+<style lang="scss" scoped>
+  .person-filter-selection {
+    display: flex;
+    width: 100%;
+    height: 98%;
+    .left {
+      display: flex;
+      flex-direction: column;
+      width: 50%;
+      height: 100%;
+      border-right: 1px solid rgba(0, 0, 0, 0.06);
+      // .filter-title {
+      // }
+      .filter-result {
+        display: flex;
+        flex-direction: column;
+        gap: 8px;
+        margin-top: 16px;
+      }
+      .filter-result-empty {
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        height: 100%;
+        font-weight: 400;
+        font-size: 14px;
+        color: #999999;
+      }
+    }
+    .right {
+      display: flex;
+      flex-direction: column;
+      flex: 1;
+      height: 100%;
+      position: relative;
+      .head {
+        display: flex;
+        align-items: center;
+        font-weight: 400;
+        font-size: 16px;
+        color: rgba(0, 0, 0, 0.88);
+        line-height: 22px;
+      }
+      .selected {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 8px;
+        overflow-y: auto;
+        max-height: calc(100% - 120px);
+      }
+      .footer {
+        display: flex;
+        gap: 6px;
+        position: absolute;
+        right: 24px;
+        bottom: 20px;
+      }
+    }
+  }
+</style>

+ 1 - 1
src/views/message/persongroup/UserGroup.vue

@@ -67,7 +67,7 @@
         :close-on-click-modal="false"
         :destroy-on-close="true"
       >
-        <GroupBoard @close="drawer = false" :drawer-title="drawerTitle" :form-data="formData" />
+        <GroupBoard @close="drawer = false" :drawer-title="drawerTitle" :form-data="formData!" />
       </el-drawer>
     </div>
   </div>

+ 56 - 26
src/views/message/persongroup/components/GroupBoard.vue

@@ -58,7 +58,12 @@
       </el-form-item>
       <el-form-item label="组内成员:" prop="userList" v-if="props.drawerTitle === '查看人员分组'">
         <el-select v-model="ruleForm.userList" value-key="id" multiple disabled="true">
-          <el-option v-for="user in selectedUser" :key="user.id" :label="user.name" :value="user">
+          <el-option
+            v-for="user in selectedUser"
+            :key="user.id"
+            :label="user.staffNo + '-' + user.nickname"
+            :value="user"
+          >
           </el-option>
         </el-select>
         <p
@@ -78,7 +83,12 @@
           multiple
           @click="dialogVisible = true"
         >
-          <el-option v-for="user in selectedUser" :key="user.id" :label="user.name" :value="user">
+          <el-option
+            v-for="user in selectedUser"
+            :key="user.id"
+            :label="user.staffNo + '-' + user.nickname"
+            :value="user"
+          >
           </el-option>
         </el-select>
         <p
@@ -114,14 +124,22 @@
       :destroy-on-close="true"
       class="workShopDialog"
     >
-      <SelectTree @cancel="handleCancle" @submit="handleSubmit" :selectedUser="selectedUser" />
+      <!-- <SelectTree @cancel="handleCancle" @submit="handleSubmit" :selectedUser="selectedUser" /> -->
+      <PersonFilterSelection
+        @cancel="handleCancle"
+        @submit="handleSubmit"
+        :init-selected="selectedUser"
+      />
     </el-dialog>
   </div>
 </template>
 
 <script lang="ts" setup>
   import { ref, reactive, watch } from 'vue';
-  import SelectTree from './SelectTree.vue';
+  // import SelectTree from './SelectTree.vue';
+  import { SelectedFilterPersonInfo } from '@/api/message/person-group';
+
+  import PersonFilterSelection from '@/views/message/components/PersonFilterSelection.vue';
   import { storeToRefs } from 'pinia';
   import { useUserStore } from '@/store/modules/user';
   import type { FormInstance } from 'element-plus';
@@ -139,29 +157,31 @@
   import userGroupList from '../store/index';
   const userGroup = userGroupList();
   const { getUserGroup } = userGroup;
-  interface UserList {
-    id: string;
-    name: string;
-    userId: number;
-  }
-  const selectedUser = ref<UserList[]>([]);
+
+  // interface UserList {
+  //   id: string;
+  //   name: string;
+  //   userId: number;
+  // }
+  const selectedUser = ref<SelectedFilterPersonInfo[]>([]);
   const dialogVisible = ref<boolean>(false);
   const ruleFormRef = ref<FormInstance>();
   const ruleForm = reactive({
     name: '',
     description: '',
-    userList: [] as UserList[],
+    userList: [] as SelectedFilterPersonInfo[],
     operator: info.value.nickname,
   });
   const handleCancle = () => {
     dialogVisible.value = false;
   };
   const total = ref<number>(0);
-  const handleSubmit = (selectedData: UserList[]) => {
-    selectedUser.value = selectedData;
-    ruleForm.userList = selectedData;
-    total.value = ruleForm.userList.length;
-    dialogVisible.value = false;
+  const handleSubmit = (selectedData: SelectedFilterPersonInfo[]) => {
+    console.log('submit!', selectedData);
+    // selectedUser.value = selectedData;
+    // ruleForm.userList = selectedData;
+    // total.value = ruleForm.userList.length;
+    // dialogVisible.value = false;
   };
   const emit = defineEmits(['close']);
   const debounceEmit = debounce((addUserGroupParams) => {
@@ -196,7 +216,7 @@
       params.value = {
         name: ruleForm.name,
         description: ruleForm.description,
-        userIdList: ruleForm.userList.map((user) => user.userId),
+        userIdList: ruleForm.userList.map((user) => user.id),
         total: total.value,
       };
       debounceEmit(params.value);
@@ -216,7 +236,7 @@
           userGroupId: props.formData.userGroupId,
           name: ruleForm.name,
           description: ruleForm.description,
-          userIdList: ruleForm.userList.map((user) => user.userId),
+          userIdList: ruleForm.userList.map((user) => user.id),
           total: total.value,
         };
         debounceChange(params.value);
@@ -227,15 +247,20 @@
     if (!formEl) return;
     formEl.resetFields();
   };
-  const userList = ref<UserList[]>();
+  const userList = ref<SelectedFilterPersonInfo[]>();
   const recoverForm = (formEl: FormInstance | undefined) => {
     if (!formEl) return;
     ruleForm.name = props.formData.name;
     ruleForm.description = props.formData.description ? props.formData.description : '';
+    // userList.value = props.formData.userList.map((user) => ({
+    //   id: `u${user.userId}`,
+    //   userId: user.userId,
+    //   name: `${user.loginName}-${user.nickname}`,
+    // }));
     userList.value = props.formData.userList.map((user) => ({
-      id: `u${user.userId}`,
-      userId: user.userId,
-      name: `${user.loginName}-${user.nickname}`,
+      id: user.userId,
+      nickname: user.nickname,
+      staffNo: user.loginName,
     }));
     selectedUser.value = userList.value;
     ruleForm.userList = userList.value;
@@ -262,10 +287,15 @@
       if (props.drawerTitle === '查看人员分组') {
         ruleForm.operator = newForm.operator;
       }
-      userList.value = newForm.userList.map((user) => ({
-        id: `u${user.userId}`,
-        userId: user.userId,
-        name: `${user.loginName}-${user.nickname}`,
+      // userList.value = newForm.userList.map((user) => ({
+      //   id: `u${user.userId}`,
+      //   userId: user.userId,
+      //   name: `${user.loginName}-${user.nickname}`,
+      // }));
+      userList.value = props.formData.userList.map((user) => ({
+        id: user.userId,
+        nickname: user.nickname,
+        staffNo: user.loginName,
       }));
       selectedUser.value = userList.value;
       ruleForm.userList = userList.value;

+ 1 - 1
src/views/message/persongroup/components/SelectTree.vue

@@ -63,7 +63,7 @@
   import { Search } from '@element-plus/icons-vue';
   import { ElTree } from 'element-plus';
   import { treeSelected, TreeNode, FormattedNode } from '../type';
-  import { countLeafNodes, formatTree } from '../hook/index';
+  import { countLeafNodes, formatTree } from '@/views/message/persongroup/utils/index';
   import { queryUserTree } from '@/api/message/person-group';
   import { cloneDeep } from 'lodash-es';
   import { TreeKey } from 'element-plus/es/components/tree/src/tree.type';

+ 92 - 0
src/views/message/persongroup/hook/usePersonGroupFilterList.ts

@@ -0,0 +1,92 @@
+import {
+  QueryPersonFilterParams,
+  QueryPersonFilterListReturn,
+  SelectedFilterPersonInfo,
+  FilterPersonInfo,
+  queryPersonFilterList,
+} from '@/api/message/person-group';
+import { ref } from 'vue';
+import { debounce } from 'lodash-es';
+
+export const usePersonGroupFilterList = () => {
+  const FILTER_TYPES = [
+    { type: 'staffNo', label: '工号' },
+    { type: 'nickname', label: '姓名' },
+    { type: 'deptName', label: '部门' },
+  ];
+
+  // 复用输入框类型及标签
+  const personFilterType = ref({ type: 'staffNo', label: '工号' });
+  // 输入框绑定数据
+  const personFilterValue = ref<string>();
+  // 查询参数
+  const personFilterParams = ref<QueryPersonFilterParams>({
+    pageNumber: 1,
+    pageSize: 10,
+  });
+  // 查询事件
+  const getPersonFilterList = async () => {
+    personFilterParams.value = {
+      pageNumber: 1,
+      pageSize: 10,
+    };
+    personFilterParams.value[personFilterType.value.type] = personFilterValue.value;
+    const res = await queryPersonFilterList(personFilterParams.value);
+    personFilterList.value = res;
+    // 如果有返回且搜索到则添加选中标记
+    if (personFilterList.value && personFilterList.value.availableUserDTOS)
+      personFilterList.value.availableUserDTOS = checkPersonList(
+        personFilterList.value.availableUserDTOS,
+      );
+  };
+  // 查询事件加防抖
+  const debouncedGetPersonFilterList = debounce(getPersonFilterList, 500);
+  // 查询结果
+  const personFilterList = ref<QueryPersonFilterListReturn>();
+  // 已选择人员
+  const selectedPersonList = ref<SelectedFilterPersonInfo[]>([]);
+  // 查询结果添加选中标记
+  const checkPersonList = (list: FilterPersonInfo[]) => {
+    return list.map((item) => {
+      return {
+        ...item,
+        checked: selectedPersonList.value.some((person) => person.id === item.id),
+      };
+    });
+  };
+
+  // 查询结果刷新选中标记
+  const refreshPersonFilterCheckedStatus = (personId: number) => {
+    const person = personFilterList.value?.availableUserDTOS.find((item) => item.id === personId);
+    if (person) person.checked = selectedPersonList.value.some((person) => person.id === personId);
+  };
+
+  // 添加已选择人员
+  const handleAddSelectedPerson = (person: FilterPersonInfo) => {
+    selectedPersonList.value.push(person);
+    // refreshPersonFilterCheckedStatus(person.id);
+  };
+
+  // 移除已选择人员
+  const handleRemoveSelectedPerson = (personId: number) => {
+    // selectedPersonList.value = selectedPersonList.value.filter((item) => item.id !== personId);
+    selectedPersonList.value.splice(
+      selectedPersonList.value.findIndex((item) => item.id === personId),
+      1,
+    );
+    refreshPersonFilterCheckedStatus(personId);
+  };
+  return {
+    FILTER_TYPES,
+    personFilterType,
+    personFilterValue,
+    personFilterParams,
+    personFilterList,
+    selectedPersonList,
+    debouncedGetPersonFilterList,
+    handleAddSelectedPerson,
+    handleRemoveSelectedPerson,
+  };
+};
+
+export default usePersonGroupFilterList;

+ 48 - 48
src/views/message/persongroup/type.ts

@@ -1,63 +1,63 @@
 export interface userData {
-    //分组描述
-    description?: string;
-    //ID
-    id?: number;
-    //用户组名称
-    name?: string;
-    //操作时间
-    operationTime?: string;
-    //操作人姓名
-    operatorName?: string;
-    //人数
-    total?: number;
+  //分组描述
+  description?: string;
+  //ID
+  id?: number;
+  //用户组名称
+  name?: string;
+  //操作时间
+  operationTime?: string;
+  //操作人姓名
+  operatorName?: string;
+  //人数
+  total?: number;
 }
 export interface treeSelected {
-    //ID
-    id?: string;
-    //名称
-    name?: string;
-    //userID
-    userId?: number;
+  //ID
+  id?: string;
+  //名称
+  name?: string;
+  //userID
+  userId?: number;
 }
 interface UserVO {
-    userId: number;
-    userNickname: string | null;
-    userNumber: string | null;
-    userLoginName: string | null;
+  userId: number;
+  userNickname: string | null;
+  userNumber: string | null;
+  userLoginName: string | null;
 }
 export interface TreeNode {
-    deptId: number;
-    deptName: string;
-    treePath: string | null;
-    grade: string | null;
-    parent: number;
-    parentName: string | null;
-    orderNum: number | null;
-    children: TreeNode[] | null;
-    userVOList: UserVO[] | null;
+  deptId: number;
+  deptName: string;
+  treePath: string | null;
+  grade: string | null;
+  parent: number;
+  parentName: string | null;
+  orderNum: number | null;
+  children: TreeNode[] | null;
+  userVOList: UserVO[] | null;
 }
 export interface FormattedNode {
-    id: string;
-    name: string | null;
-    userId: number | null;
-    children: FormattedNode[];
-    filter?: boolean;
+  id: string;
+  name: string | null;
+  userId: number | null;
+  children: FormattedNode[];
+  filter?: boolean;
 }
 export interface FromUserList {
-    userId: number;
-    loginName: string;
-    nickname: string;
+  userId: number;
+  loginName: string;
+  nickname: string;
 }
 export interface FormData {
-    description?: string;
-    name: string;
-    operator: string;
-    total: number;
-    userGroupId: number;
-    userList: FromUserList[];
+  description?: string;
+  name: string;
+  operator: string;
+  total: number;
+  userGroupId: number;
+  userList: FromUserList[];
 }
 export interface GroupData extends FormData {
-    isExpand: boolean;
-    isHidden: boolean;
-}
+  isExpand: boolean;
+  isHidden: boolean;
+}

src/views/message/persongroup/hook/index.ts → src/views/message/persongroup/utils/index.ts


+ 1 - 1
src/views/message/systemNotifications/components/WorkShopTree.vue

@@ -62,7 +62,7 @@
   import { onMounted, ref, watch, computed } from 'vue';
   import { Search } from '@element-plus/icons-vue';
   import { ElTree } from 'element-plus';
-  import { countLeafNodes } from '@/views/message/persongroup/hook/index';
+  import { countLeafNodes } from '@/views/message/persongroup/utils/index';
   import { getList } from '@/api/message/system-notifications';
   import useScene from '@/views/system-config/scene-manage/use-scene';
   import { storeToRefs } from 'pinia';