|
@@ -0,0 +1,218 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="person-filter-selection">
|
|
|
|
|
+ <div class="left">
|
|
|
|
|
+ <div class="filter-title">
|
|
|
|
|
+ <el-form-item>
|
|
|
|
|
+ <el-input v-model="personFilterValue" style="width: 253px" placeholder="请输入搜索的内容" clearable>
|
|
|
|
|
+ <template #prepend>
|
|
|
|
|
+ <el-select v-model="personFilterType" style="width: 74px" value-key="type" :validate-event="false">
|
|
|
|
|
+ <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="debouncedHandleFilter" style="width: 65px; height: 32px; margin: 0 11px 0"
|
|
|
|
|
+ >搜 索</el-button
|
|
|
|
|
+ >
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 搜索结果展示 -->
|
|
|
|
|
+ <div v-if="personFilterList?.records?.length" class="filter-result" ref="filterResult">
|
|
|
|
|
+ <div class="filter-result-item" v-for="person in personFilterList.records" :key="person.id">
|
|
|
|
|
+ <el-checkbox
|
|
|
|
|
+ v-model="person.checked"
|
|
|
|
|
+ :label="person.staffNo + '-' + person.realname + ' ' + '(' + person.deptName + ')'"
|
|
|
|
|
+ @change="handleSelect($event, person)"
|
|
|
|
|
+ ></el-checkbox>
|
|
|
|
|
+ <!-- <div style="margin-left: 8px">
|
|
|
|
|
+ {{ person.staffNo + '-' + person.nickname + ' ' + '(' + person.deptName + ')' }}
|
|
|
|
|
+ </div> -->
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ id="next-loading"
|
|
|
|
|
+ style="text-align: center"
|
|
|
|
|
+ v-if="personFilterList.totalRow > personFilterList.records.length"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-icon class="el-input__icon" :size="24">
|
|
|
|
|
+ <Loading />
|
|
|
|
|
+ </el-icon>
|
|
|
|
|
+ </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">
|
|
|
|
|
+ <div class="head">
|
|
|
|
|
+ <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.staffNo + '-' + person.realname }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="footer">
|
|
|
|
|
+ <el-button @click="handleCancle"> 取消 </el-button>
|
|
|
|
|
+ <el-button type="primary" @click="handleSubmit">提交</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+ import { ref, onMounted, onBeforeUnmount } from 'vue';
|
|
|
|
|
+ import { Loading } from '@element-plus/icons-vue';
|
|
|
|
|
+ import { usePersonGroupFilter } from '../hooks/usePersonGroupFilter';
|
|
|
|
|
+ import { PersonGroupItem } from '@/types/person-group/type';
|
|
|
|
|
+ import { debounce } from 'lodash-es';
|
|
|
|
|
+
|
|
|
|
|
+ const {
|
|
|
|
|
+ FILTER_TYPES,
|
|
|
|
|
+ personFilterType,
|
|
|
|
|
+ personFilterValue,
|
|
|
|
|
+ personFilterList,
|
|
|
|
|
+ selectedPersonList,
|
|
|
|
|
+ getPersonFilterList,
|
|
|
|
|
+ getNextPersonFilterList,
|
|
|
|
|
+ handleAddSelectedPerson,
|
|
|
|
|
+ handleRemoveSelectedPerson,
|
|
|
|
|
+ } = usePersonGroupFilter();
|
|
|
|
|
+
|
|
|
|
|
+ const emit = defineEmits(['cancel', 'submit']);
|
|
|
|
|
+
|
|
|
|
|
+ const props = defineProps<{
|
|
|
|
|
+ initSelected?: PersonGroupItem[];
|
|
|
|
|
+ }>();
|
|
|
|
|
+
|
|
|
|
|
+ onMounted(() => {
|
|
|
|
|
+ if (props.initSelected) selectedPersonList.value = [...props.initSelected];
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ let observer: IntersectionObserver;
|
|
|
|
|
+
|
|
|
|
|
+ const filterResult = ref();
|
|
|
|
|
+ const handleFilter = () => {
|
|
|
|
|
+ getPersonFilterList().then(() => {
|
|
|
|
|
+ filterResult.value.scrollTop = 0;
|
|
|
|
|
+ const loading = document.getElementById('next-loading');
|
|
|
|
|
+ if (loading) {
|
|
|
|
|
+ if (observer) observer.unobserve(loading);
|
|
|
|
|
+ observer = new IntersectionObserver(
|
|
|
|
|
+ (entries) => {
|
|
|
|
|
+ if (entries[0].isIntersecting) {
|
|
|
|
|
+ getNextPersonFilterList();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ threshold: 0.9,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ observer.observe(loading);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const debouncedHandleFilter = debounce(handleFilter, 500);
|
|
|
|
|
+
|
|
|
|
|
+ const handleSelect = (v: boolean, person: any) => {
|
|
|
|
|
+ if (v) {
|
|
|
|
|
+ handleAddSelectedPerson(person);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ handleRemoveSelectedPerson(person.id);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ const handleCancle = () => {
|
|
|
|
|
+ emit('cancel');
|
|
|
|
|
+ };
|
|
|
|
|
+ const handleSubmit = () => {
|
|
|
|
|
+ emit('submit', selectedPersonList.value);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ onBeforeUnmount(() => {
|
|
|
|
|
+ if (observer) {
|
|
|
|
|
+ observer.disconnect();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+</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;
|
|
|
|
|
+ height: 392px;
|
|
|
|
|
+ overflow: auto;
|
|
|
|
|
+ // .filter-result-item {
|
|
|
|
|
+ // display: flex;
|
|
|
|
|
+ // align-items: center;
|
|
|
|
|
+ // font-weight: 500;
|
|
|
|
|
+ // font-size: 14px;
|
|
|
|
|
+ // color: #303133;
|
|
|
|
|
+ // line-height: 22px;
|
|
|
|
|
+ // }
|
|
|
|
|
+ }
|
|
|
|
|
+ .filter-result-empty {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ height: 392px;
|
|
|
|
|
+ font-weight: 400;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #999999;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .right {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ margin-left: 16px;
|
|
|
|
|
+ .head {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ font-weight: 400;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ color: rgba(0, 0, 0, 0.88);
|
|
|
|
|
+ line-height: 22px;
|
|
|
|
|
+ margin: 6px 0 6px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .selected {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ max-height: calc(100% - 120px);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .footer {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ padding-right: 16px;
|
|
|
|
|
+ }
|
|
|
|
|
+</style>
|