|
@@ -0,0 +1,384 @@
|
|
|
+<template>
|
|
|
+ <el-select v-model="selectPersonnelValue" filterable @visible-change="toggleSelectPrefix" remote
|
|
|
+ :remote-method="userListRemotemethod" :multiple="multiple" @focus="userFocus" :size="size" collapse-tags
|
|
|
+ @change="updateValue" clearable :ref="`select${timeRef}`" :style="`width: ${width}`" class="custom-select">
|
|
|
+ <template #prefix>
|
|
|
+ <div style="height: 100%;display: flex;align-items: center;">
|
|
|
+ <!-- 单选 -->
|
|
|
+ <template v-if="!multiple">
|
|
|
+ <div v-if="selectPrefixFlg" class="selectSingleChoice" :style="sizeStyle[size]">
|
|
|
+ <template v-if="getSelectedLabel == '请选择'">
|
|
|
+ <span class="pleaseChoose">请选择</span>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ {{ getSelectedLabel }}
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <!-- 多选 -->
|
|
|
+ <template v-if="multiple">
|
|
|
+ <div class="selectMultiple" :style="sizeStyle[size]">
|
|
|
+ <template v-if="value.length == 0">
|
|
|
+ <span class="textSpan pleaseChoose">请选择</span>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-tag closable type="info" :size="size" class="narrow" style="margin: 0;" @close="userTagClose">{{
|
|
|
+ getSelectedLabel
|
|
|
+ }}</el-tag>
|
|
|
+ <el-tag type="info" :size="size" v-if="value.length > 1" class="narrow" style="margin: 0;">+ {{
|
|
|
+ value.length - 1 }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <!-- 占位符 -->
|
|
|
+ <span></span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 主体显示内容 -->
|
|
|
+ <div :ref="`mySelectUser${timeRef}`" class="select-user-class">
|
|
|
+ <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id">
|
|
|
+ <span class="floatLeft">
|
|
|
+ <TranslationOpenDataText type='userName' :openid='item.name'></TranslationOpenDataText>
|
|
|
+ </span>
|
|
|
+ <span class="jonumberUser">{{ item.jobNumber }}</span>
|
|
|
+ </el-option>
|
|
|
+ <!-- 下滚加载提示 -->
|
|
|
+ <div class="itemsLoading" v-if="loadingInProgress">
|
|
|
+ 加载中...
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-select>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ name: 'HelloWorld',
|
|
|
+ props: {
|
|
|
+ value: { // 双向数据绑定
|
|
|
+ type: [String, Number, Array],
|
|
|
+ required: true
|
|
|
+ },
|
|
|
+ size: { // 尺寸
|
|
|
+ type: String,
|
|
|
+ default: 'small'
|
|
|
+ },
|
|
|
+ multiple: { // 是否多选
|
|
|
+ type: Boolean,
|
|
|
+ default: () => false
|
|
|
+ },
|
|
|
+ width: { // 宽度
|
|
|
+ type: String,
|
|
|
+ default: '200px'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ options: [],
|
|
|
+ selectPersonnelValue: this.value,
|
|
|
+ selectPrefixFlg: true,
|
|
|
+ timeRef: new Date().getTime(),
|
|
|
+ updateTrigger: 0, // 用来强制触发计算属性
|
|
|
+ sizeStyle: {
|
|
|
+ mini: `height: 24px;`,
|
|
|
+ small: `height: 28px`,
|
|
|
+ medium: `height: 32px`,
|
|
|
+ },
|
|
|
+ keyword: '',
|
|
|
+ loadingInProgress: false,
|
|
|
+ userListloading: false,
|
|
|
+ userPageIndex: 1,
|
|
|
+ userPageSize: 50,
|
|
|
+ userTotal: 0,
|
|
|
+ cursor: 1,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ selectPersonnelValue: {
|
|
|
+ handler() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.multiple) {
|
|
|
+ this.getSelectedLabel
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ deep: true,
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ getSelectedLabel() {
|
|
|
+ this.updateTrigger;
|
|
|
+
|
|
|
+ if (!this.multiple) {
|
|
|
+ const selectedOption = this.options.find(item => item.id === this.selectPersonnelValue)
|
|
|
+ return selectedOption ? selectedOption.name : '请选择'
|
|
|
+ } else if (this.multiple) {
|
|
|
+ if (this.selectPersonnelValue.length === 0) {
|
|
|
+ return '请选择'
|
|
|
+ }
|
|
|
+ const selectVal = this.selectPersonnelValue[0]
|
|
|
+ const selectedOption = this.options.find(item => item.id === selectVal)
|
|
|
+ return selectedOption ? selectedOption.name : '请选择'
|
|
|
+ }
|
|
|
+
|
|
|
+ return '请选择'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ userListRemotemethod: _.debounce(function (val) {
|
|
|
+ this.newProjectListPage = 1
|
|
|
+ this.getUserList(val)
|
|
|
+ }, 500),
|
|
|
+ // 随机取名字4-6个字
|
|
|
+ getRandomName() {
|
|
|
+ const firstNames = ['张', '李', '王', '刘', '陈', '杨', '黄', '赵'];
|
|
|
+ const lastNames = ['伟', '芳', '娜', '敏', '军', '洋', '静', '磊'];
|
|
|
+
|
|
|
+ const nameLength = Math.floor(Math.random() * 3) + 4; // 生成4到6之间的随机数
|
|
|
+ let name = '';
|
|
|
+
|
|
|
+ for (let i = 0; i < nameLength; i++) {
|
|
|
+ if (i % 2 === 0) {
|
|
|
+ name += firstNames[Math.floor(Math.random() * firstNames.length)];
|
|
|
+ } else {
|
|
|
+ name += lastNames[Math.floor(Math.random() * lastNames.length)];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return name;
|
|
|
+ },
|
|
|
+ uniqueById(arr) {
|
|
|
+ return arr.reduce((accumulator, current) => {
|
|
|
+ if (!accumulator.some(item => item.id === current.id)) {
|
|
|
+ accumulator.push(current);
|
|
|
+ }
|
|
|
+ return accumulator;
|
|
|
+ }, []);
|
|
|
+ },
|
|
|
+ userTagClose() {
|
|
|
+ this.selectPersonnelValue = this.selectPersonnelValue.splice(0, 1)
|
|
|
+ },
|
|
|
+ userFocus() {
|
|
|
+ console.log('失去焦点了', this.selectPrefixFlg)
|
|
|
+ if (this.selectPrefixFlg) {
|
|
|
+ this.userPageIndex = 1
|
|
|
+ this.getUserList('', false)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addUserList() {
|
|
|
+ this.loadingInProgress = true
|
|
|
+ this.postData(`/user/getSimpleActiveUserListPage`, {
|
|
|
+ pageIndex: this.userPageIndex,
|
|
|
+ pageSize: this.userPageSize,
|
|
|
+ departmentId: '',
|
|
|
+ keyword: this.keyword,
|
|
|
+ cursor: '',
|
|
|
+ userIds: this.multiple ? this.selectPersonnelValue.join(',') : this.selectPersonnelValue
|
|
|
+ }).then(res => {
|
|
|
+ const { data = [], total = 0 } = res.data
|
|
|
+ this.userTotal = total
|
|
|
+ // this.options = [...new Set([...this.options, ...data].map(JSON.stringify))].map(JSON.parse);
|
|
|
+ // this.options = this.uniqueById([...this.options, ...newData])
|
|
|
+ const newData = data.map((item) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ // name: this.getRandomName()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.options = this.uniqueById([...this.options, ...newData])
|
|
|
+ console.log(this.options.length, '<==== this.options')
|
|
|
+ }).finally(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.loadingInProgress = false
|
|
|
+ }, 500)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ getUserList(keyword = '', flag = true) {
|
|
|
+ this.keyword = keyword
|
|
|
+ this.userListloading = flag
|
|
|
+ this.postData(`/user/getSimpleActiveUserListPage`, {
|
|
|
+ pageIndex: this.userPageIndex,
|
|
|
+ pageSize: this.userPageSize,
|
|
|
+ departmentId: '',
|
|
|
+ keyword,
|
|
|
+ cursor: '',
|
|
|
+ userIds: this.multiple ? this.selectPersonnelValue.join(',') : this.selectPersonnelValue
|
|
|
+ }).then(res => {
|
|
|
+ const { data = [], total = 0 } = res.data
|
|
|
+ this.userTotal = total
|
|
|
+ // this.options = [...new Set(data.map(JSON.stringify))].map(JSON.parse);
|
|
|
+ // this.options = this.uniqueById(data);
|
|
|
+ const dataVal = data.map((item) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ // name: this.getRandomName()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.options = this.uniqueById(dataVal);
|
|
|
+ }).finally(() => {
|
|
|
+ this.userListloading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ async postData(urls, param) { // 单独封装 post 请求 方法
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ this.http.post(urls, { ...param },
|
|
|
+ res => {
|
|
|
+ if (res.code == 'ok') {
|
|
|
+ resolve(res)
|
|
|
+ } else {
|
|
|
+ this.$message({
|
|
|
+ message: res.msg,
|
|
|
+ type: 'error'
|
|
|
+ })
|
|
|
+ reject(res)
|
|
|
+ }
|
|
|
+ resolve(res)
|
|
|
+ },
|
|
|
+ error => {
|
|
|
+ this.$message({
|
|
|
+ message: error,
|
|
|
+ type: "error"
|
|
|
+ });
|
|
|
+ reject(error)
|
|
|
+ }
|
|
|
+ )
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleScroll(event) {
|
|
|
+ const container = event.target;
|
|
|
+ const { scrollTop, scrollHeight, clientHeight } = container;
|
|
|
+
|
|
|
+ const totalPage = Math.ceil(this.userTotal / this.userPageSize);
|
|
|
+ if (scrollTop + clientHeight >= scrollHeight - 20 && this.userPageIndex < totalPage && !this.loadingInProgress) {
|
|
|
+ this.loadMoreData()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ loadMoreData() {
|
|
|
+ this.userPageIndex += 1;
|
|
|
+ this.addUserList();
|
|
|
+ },
|
|
|
+ toggleSelectPrefix(value) {
|
|
|
+ this.selectPrefixFlg = !value
|
|
|
+ console.log(this.selectPrefixFlg, '<===== selectPrefixFlg')
|
|
|
+ if (value) {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.addScrollListener();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.removeScrollListener();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addScrollListener() {
|
|
|
+ const container = this.$refs[`mySelectUser${this.timeRef}`];
|
|
|
+ const scrollbar = container.closest('.el-scrollbar__wrap');
|
|
|
+ if (scrollbar) {
|
|
|
+ this.loadingInProgressHight = scrollbar.clientHeight - 40
|
|
|
+ this.removeScrollListener();
|
|
|
+ const debouncedHandleScroll = _.debounce(this.handleScroll, 200);
|
|
|
+ scrollbar.addEventListener('scroll', debouncedHandleScroll);
|
|
|
+ this.scrollListener = debouncedHandleScroll;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ removeScrollListener() {
|
|
|
+ const container = this.$refs[`mySelectUser${this.timeRef}`];
|
|
|
+ const scrollbar = container.closest('.el-scrollbar__wrap');
|
|
|
+ if (scrollbar && this.scrollListener) {
|
|
|
+ scrollbar.removeEventListener('scroll', this.scrollListener);
|
|
|
+ this.scrollListener = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ updateValue(value) { // 更新数据
|
|
|
+ if (this.multiple) {
|
|
|
+ this.updateTrigger += 1 // 强行触发计算属性
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$emit('input', value);
|
|
|
+ this.$emit('change', value);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.getUserList()
|
|
|
+ });
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ this.removeScrollListener();
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+.custom-select input::placeholder {
|
|
|
+ color: transparent !important;
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.select-base {
|
|
|
+ position: absolute;
|
|
|
+ width: auto;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background: #fff;
|
|
|
+ box-sizing: border-box;
|
|
|
+ color: #606266;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.selectSingleChoice {
|
|
|
+ @extend .select-base;
|
|
|
+ left: 26px;
|
|
|
+ transform-origin: left;
|
|
|
+}
|
|
|
+
|
|
|
+.selectMultiple {
|
|
|
+ @extend .select-base;
|
|
|
+ left: 0;
|
|
|
+ z-index: 10;
|
|
|
+ min-width: 40px;
|
|
|
+}
|
|
|
+
|
|
|
+.select-user-class {
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ .itemsLoading {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ bottom: -2px;
|
|
|
+ width: 100%;
|
|
|
+ text-align: center;
|
|
|
+ padding: 10px;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: 0px -4px 20px 0px #999;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #999;
|
|
|
+ z-index: 99;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.narrow {
|
|
|
+ transform: scale(0.9);
|
|
|
+ transform-origin: left;
|
|
|
+}
|
|
|
+
|
|
|
+.selectMultiple .textSpan {
|
|
|
+ position: relative;
|
|
|
+ left: 26px;
|
|
|
+}
|
|
|
+
|
|
|
+.pleaseChoose {
|
|
|
+ color: #C0C4CC;
|
|
|
+}
|
|
|
+
|
|
|
+.floatLeft {
|
|
|
+ float: left;
|
|
|
+}
|
|
|
+
|
|
|
+.jonumberUser {
|
|
|
+ float: right;
|
|
|
+ color: #8492a6;
|
|
|
+}
|
|
|
+</style>
|