mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-11 16:28:11 +00:00
perf: 优化会员选择器
This commit is contained in:
parent
8157c27529
commit
fedeeb3076
@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<div class="common-user-select" :class="warpClass">
|
||||
<ul v-if="!module">
|
||||
<li v-for="userid in values" v-if="userid" @click="onSelection">
|
||||
<UserAvatar :userid="userid" :size="avatarSize" :show-icon="avatarIcon" :show-name="avatarName"/>
|
||||
</li>
|
||||
<template v-for="userid in values">
|
||||
<li v-if="userid" :key="userid" @click="onSelection">
|
||||
<UserAvatar :userid="userid" :size="avatarSize" :show-icon="avatarIcon" :show-name="avatarName"/>
|
||||
</li>
|
||||
</template>
|
||||
<li v-if="addIcon || values.length === 0" class="add-icon" :style="addStyle" @click="onSelection"></li>
|
||||
</ul>
|
||||
|
||||
@ -19,19 +21,21 @@
|
||||
<!-- 顶部 -->
|
||||
<template #header>
|
||||
<div v-if="isFullscreen" class="user-modal-header">
|
||||
<div class="user-modal-close" @click="showModal=false">{{$L('关闭')}}</div>
|
||||
<div class="user-modal-close" @click="showModal=false">{{ $L('关闭') }}</div>
|
||||
<div class="user-modal-title">
|
||||
<span ref="headerTitle" @click="onClickTitle">{{localTitle}}</span>
|
||||
<span ref="headerTitle" @click="onClickTitle">{{ localTitle }}</span>
|
||||
</div>
|
||||
<div ref="headerSubmit" class="user-modal-submit" @click="onSubmit">
|
||||
<div v-if="submittIng > 0" class="submit-loading"><Loading /></div>
|
||||
{{$L('确定')}}
|
||||
<div v-if="submittIng > 0" class="submit-loading">
|
||||
<Loading/>
|
||||
</div>
|
||||
{{ $L('确定') }}
|
||||
<template v-if="selects.length > 0">
|
||||
({{selects.length}}<span v-if="multipleMax">/{{multipleMax}}</span>)
|
||||
({{ selects.length }}<span v-if="multipleMax">/{{ multipleMax }}</span>)
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ivu-modal-header-inner">{{localTitle}}</div>
|
||||
<div v-else class="ivu-modal-header-inner">{{ localTitle }}</div>
|
||||
</template>
|
||||
<template #close>
|
||||
<i class="ivu-icon ivu-icon-ios-close"></i>
|
||||
@ -41,14 +45,14 @@
|
||||
<div class="user-modal-search">
|
||||
<Scrollbar ref="selected" class="search-selected" v-if="selects.length > 0" enable-x :enable-y="false">
|
||||
<ul>
|
||||
<li v-for="item in formatSelect(selects)" :data-id="item.userid" @click.stop="onRemoveItem(item.userid)">
|
||||
<li v-for="item in formatSelect(selects)" :key="item.userid" :data-id="item.userid" @click.stop="onRemoveItem(item.userid)">
|
||||
<template v-if="item.type=='group'">
|
||||
<EAvatar v-if="item.avatar" class="img-avatar" :src="item.avatar" :size="32"></EAvatar>
|
||||
<i v-else-if="item.group_type=='department'" class="taskfont icon-avatar department"></i>
|
||||
<i v-else-if="item.group_type=='project'" class="taskfont icon-avatar project"></i>
|
||||
<i v-else-if="item.group_type=='task'" class="taskfont icon-avatar task"></i>
|
||||
<i v-else-if="item.group_type=='okr'" class="taskfont icon-avatar task"></i>
|
||||
<Icon v-else class="icon-avatar" type="ios-people" />
|
||||
<Icon v-else class="icon-avatar" type="ios-people"/>
|
||||
</template>
|
||||
<UserAvatar v-else :userid="item.userid"/>
|
||||
</li>
|
||||
@ -57,7 +61,7 @@
|
||||
<div class="search-input">
|
||||
<div class="search-pre">
|
||||
<Loading v-if="loadIng > 0"/>
|
||||
<Icon v-else type="ios-search" />
|
||||
<Icon v-else type="ios-search"/>
|
||||
</div>
|
||||
<Form class="search-form" action="javascript:void(0)" @submit.native.prevent="$A.eeuiAppKeyboardHide">
|
||||
<Input
|
||||
@ -76,7 +80,8 @@
|
||||
<li
|
||||
v-for="item in switchItems" :key="item.key"
|
||||
:class="{active:switchActive===item.key}"
|
||||
@click="switchActive=item.key">{{ $L(item.label) }}</li>
|
||||
@click="switchActive=item.key">{{ $L(item.label) }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 列表 -->
|
||||
@ -85,71 +90,95 @@
|
||||
<ul v-if="switchActive == 'project'" class="user-modal-project">
|
||||
<li
|
||||
v-for="item in lists"
|
||||
:key="item.id"
|
||||
:class="selectClass(item.userid_list)"
|
||||
@click="onSelectProject(item.userid_list)">
|
||||
<Icon class="user-modal-icon" :type="selectIcon(item.userid_list)" />
|
||||
@click="onSelectMultiple(item.userid_list)">
|
||||
<Icon class="user-modal-icon" :type="selectIcon(item.userid_list)"/>
|
||||
<div class="user-modal-avatar">
|
||||
<i class="taskfont icon-avatar"></i>
|
||||
<div class="project-name">
|
||||
<div class="label">{{item.name}}</div>
|
||||
<div class="label">{{ item.name }}</div>
|
||||
<div class="subtitle">
|
||||
{{item.userid_list.length}} {{$L('项目成员')}}
|
||||
<em class="all">{{$L('已全选')}}</em>
|
||||
<em class="some">{{$L('已选部分')}}</em>
|
||||
{{ item.userid_list.length }} {{ $L('项目成员') }}
|
||||
<em class="all">{{ $L('已全选') }}</em>
|
||||
<em class="some">{{ $L('已选部分') }}</em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- 会员、会话 -->
|
||||
<ul v-else>
|
||||
<li
|
||||
v-if="showSelectAll"
|
||||
:class="selectClass('all')"
|
||||
@click="onSelectAll">
|
||||
<Icon class="user-modal-icon" :type="selectIcon('all')" />
|
||||
<div class="user-modal-all">{{$L('全选')}}</div>
|
||||
</li>
|
||||
<li
|
||||
v-for="item in lists"
|
||||
:class="{
|
||||
selected: selects.includes(item.userid),
|
||||
disabled: isNoChoice(item.userid)
|
||||
}"
|
||||
@click="onSelectItem(item)">
|
||||
<Icon v-if="selects.includes(item.userid)" class="user-modal-icon" type="ios-checkmark-circle" />
|
||||
<Icon v-else-if="isNoChoice(item.userid)" class="user-modal-icon" type="ios-remove-circle-outline" />
|
||||
<Icon v-else class="user-modal-icon" type="ios-radio-button-off" />
|
||||
<div v-if="item.type=='group'" class="user-modal-avatar">
|
||||
<EAvatar v-if="item.avatar" class="img-avatar" :src="item.avatar" :size="40"></EAvatar>
|
||||
<i v-else-if="item.group_type=='department'" class="taskfont icon-avatar department"></i>
|
||||
<i v-else-if="item.group_type=='project'" class="taskfont icon-avatar project"></i>
|
||||
<i v-else-if="item.group_type=='task'" class="taskfont icon-avatar task"></i>
|
||||
<i v-else-if="item.group_type=='okr'" class="taskfont icon-avatar task"></i>
|
||||
<Icon v-else class="icon-avatar" type="ios-people" />
|
||||
<div class="avatar-name">
|
||||
<span>{{item.name}}</span>
|
||||
<template v-else>
|
||||
<ul v-if="showSelectAll || switchActive=='contact'" class="sticky-top">
|
||||
<li :class="selectClass('all')">
|
||||
<div v-if="showSelectAll" @click="onSelectAll" class="user-modal-label">
|
||||
<Icon class="user-modal-icon" :type="selectIcon('all')"/>
|
||||
<span>{{ $L('全选') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<UserAvatar v-else class="user-modal-avatar" :userid="item.userid" :size="40" show-name/>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="switchActive=='contact'" class="user-modal-view">
|
||||
<RadioGroup v-model="contactViewMode" type="button" button-style="solid">
|
||||
<Radio label="list">{{ $L('列表视图') }}</Radio>
|
||||
<Radio label="department">{{ $L('部门视图') }}</Radio>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<template v-for="items in convertTwoList(lists)">
|
||||
<ul v-if="items.name !== null" :key="`${items.id}-sticky`" class="sticky-top">
|
||||
<li :class="selectClass(items.userid_list)">
|
||||
<div @click="onSelectMultiple(items.userid_list)" class="user-modal-label">
|
||||
<Icon class="user-modal-icon" :type="selectIcon(items.userid_list)"/>
|
||||
<span>{{ items.name }}</span>
|
||||
</div>
|
||||
<div class="user-modal-view">{{ items.list.length }} {{ $L('部门成员') }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul :key="`${items.id}-list`">
|
||||
<li
|
||||
v-for="item in items.list"
|
||||
:key="item.userid"
|
||||
:class="{
|
||||
selected: selects.includes(item.userid),
|
||||
disabled: isNoChoice(item.userid),
|
||||
}"
|
||||
@click="onSelectItem(item)">
|
||||
<Icon v-if="selects.includes(item.userid)" class="user-modal-icon" type="ios-checkmark-circle"/>
|
||||
<Icon v-else-if="isNoChoice(item.userid)" class="user-modal-icon" type="ios-remove-circle-outline"/>
|
||||
<Icon v-else class="user-modal-icon" type="ios-radio-button-off"/>
|
||||
<div v-if="item.type=='group'" class="user-modal-avatar">
|
||||
<EAvatar v-if="item.avatar" class="img-avatar" :src="item.avatar" :size="40"></EAvatar>
|
||||
<i v-else-if="item.group_type=='department'" class="taskfont icon-avatar department"></i>
|
||||
<i v-else-if="item.group_type=='project'" class="taskfont icon-avatar project"></i>
|
||||
<i v-else-if="item.group_type=='task'" class="taskfont icon-avatar task"></i>
|
||||
<i v-else-if="item.group_type=='okr'" class="taskfont icon-avatar task"></i>
|
||||
<Icon v-else class="icon-avatar" type="ios-people"/>
|
||||
<div class="avatar-name">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<UserAvatar v-else class="user-modal-avatar" :userid="item.userid" :size="40" show-name/>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</template>
|
||||
</Scrollbar>
|
||||
<!-- 空 -->
|
||||
<div v-else class="user-modal-empty">
|
||||
<Loading v-if="waitIng > 0"/>
|
||||
<template v-else>
|
||||
<div class="empty-icon"><Icon type="ios-cafe-outline" /></div>
|
||||
<div class="empty-text">{{$L('暂无结果')}}</div>
|
||||
<div class="empty-icon">
|
||||
<Icon type="ios-cafe-outline"/>
|
||||
</div>
|
||||
<div class="empty-text">{{ $L('暂无结果') }}</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 底部 -->
|
||||
<template #footer>
|
||||
<Button type="primary" :loading="submittIng > 0" @click="onSubmit">
|
||||
{{$L('确定')}}
|
||||
{{ $L('确定') }}
|
||||
<template v-if="selects.length > 0">
|
||||
({{selects.length}}<span v-if="multipleMax">/{{multipleMax}}</span>)
|
||||
({{ selects.length }}<span v-if="multipleMax">/{{ multipleMax }}</span>)
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
@ -289,6 +318,7 @@ export default {
|
||||
{key: 'project', label: '项目成员'},
|
||||
],
|
||||
switchActive: 'recent',
|
||||
contactViewMode: 'list',
|
||||
|
||||
loadIng: 0, // 搜索框等待效果
|
||||
waitIng: 0, // 页面等待效果
|
||||
@ -309,6 +339,9 @@ export default {
|
||||
searchCache: [],
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.contactViewMode = await $A.IDBString("userSelectContactViewMode", this.contactViewMode)
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(value) {
|
||||
@ -354,6 +387,10 @@ export default {
|
||||
this.searchBefore()
|
||||
},
|
||||
|
||||
contactViewMode(value) {
|
||||
$A.IDBSet("userSelectContactViewMode", value)
|
||||
},
|
||||
|
||||
isFullscreen(value) {
|
||||
if (value) {
|
||||
this.upTitleWidth()
|
||||
@ -473,6 +510,48 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
convertTwoList(lists) {
|
||||
if (this.switchActive === 'contact' && this.contactViewMode === 'department') {
|
||||
const departmentMap = new Map()
|
||||
const noDepartmentMembers = []
|
||||
// 部门视图
|
||||
lists.forEach(item => {
|
||||
if (item.department_info && item.department_info.length > 0) {
|
||||
// 用户可能属于多个部门
|
||||
item.department_info.forEach(dept => {
|
||||
if (!departmentMap.has(dept.id)) {
|
||||
departmentMap.set(dept.id, {
|
||||
id: dept.id,
|
||||
name: dept.name,
|
||||
list: [],
|
||||
})
|
||||
}
|
||||
departmentMap.get(dept.id).list.push(item)
|
||||
})
|
||||
} else {
|
||||
// 没有部门信息的成员
|
||||
noDepartmentMembers.push(item)
|
||||
}
|
||||
})
|
||||
// 处理默认部门(未分组)
|
||||
if (noDepartmentMembers.length > 0) {
|
||||
departmentMap.set(0, {
|
||||
id: 0,
|
||||
name: this.$L('默认部门'),
|
||||
list: noDepartmentMembers,
|
||||
})
|
||||
}
|
||||
return Array.from(departmentMap.values()).map(item => ({...item, userid_list: item.list.map(item => item.userid)}))
|
||||
}
|
||||
return [
|
||||
{
|
||||
id: 0,
|
||||
name: null,
|
||||
list: lists,
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
selectIcon(value) {
|
||||
if (value === 'all') {
|
||||
return this.isSelectAll ? 'ios-checkmark-circle' : 'ios-radio-button-off';
|
||||
@ -584,7 +663,8 @@ export default {
|
||||
disable: this.showDisable && key ? 2 : 0,
|
||||
},
|
||||
page,
|
||||
pagesize: 50
|
||||
pagesize: 100,
|
||||
with_department: 1,
|
||||
},
|
||||
}).then(({data}) => {
|
||||
if (this.searchKey != key) {
|
||||
@ -695,25 +775,6 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
onSelectAll() {
|
||||
if (this.isSelectAll) {
|
||||
this.selects = $A.cloneJSON(this.uncancelable)
|
||||
return
|
||||
}
|
||||
this.lists.some(item => {
|
||||
if (this.isDisabled(item.userid)) {
|
||||
return false
|
||||
}
|
||||
if (this.multipleMax && this.selects.length >= this.multipleMax) {
|
||||
$A.messageWarning("已超过最大选择数量")
|
||||
return true
|
||||
}
|
||||
if (!this.selects.includes(item.userid)) {
|
||||
this.selects.push(item.userid)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onSelectItem({userid}) {
|
||||
if (this.selects.includes(userid)) {
|
||||
if (this.isUncancelable(userid)) {
|
||||
@ -739,7 +800,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
onSelectProject(userid_list) {
|
||||
onSelectMultiple(userid_list) {
|
||||
switch (this.selectIcon(userid_list)) {
|
||||
case 'ios-checkmark-circle':
|
||||
// 去除
|
||||
@ -765,6 +826,25 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
onSelectAll() {
|
||||
if (this.isSelectAll) {
|
||||
this.selects = $A.cloneJSON(this.uncancelable)
|
||||
return
|
||||
}
|
||||
this.lists.some(item => {
|
||||
if (this.isDisabled(item.userid)) {
|
||||
return false
|
||||
}
|
||||
if (this.multipleMax && this.selects.length >= this.multipleMax) {
|
||||
$A.messageWarning("已超过最大选择数量")
|
||||
return true
|
||||
}
|
||||
if (!this.selects.includes(item.userid)) {
|
||||
this.selects.push(item.userid)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onRemoveItem(userid) {
|
||||
if (this.isUncancelable(userid)) {
|
||||
return
|
||||
|
||||
@ -70,6 +70,7 @@
|
||||
bottom: 0;
|
||||
left: 100px;
|
||||
right: 100px;
|
||||
|
||||
> span {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
@ -234,10 +235,26 @@
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 400px;
|
||||
min-height: 300px;
|
||||
max-height: calc(100vh - 280px);
|
||||
|
||||
ul {
|
||||
padding: 16px 24px;
|
||||
padding: 8px 24px;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
&.sticky-top {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: #ffffff;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&.user-modal-project {
|
||||
> li {
|
||||
@ -303,10 +320,12 @@
|
||||
&.disabled {
|
||||
color: #c5c8ce;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
color: #c5c8ce;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.user-modal-icon {
|
||||
color: #c5c8ce;
|
||||
}
|
||||
@ -319,9 +338,24 @@
|
||||
color: rgba($primary-desc-color, 0.7);
|
||||
}
|
||||
|
||||
.user-modal-all {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
.user-modal-label {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
> span {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.user-modal-view {
|
||||
flex-shrink: 0;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.user-modal-avatar {
|
||||
|
||||
16
resources/assets/sass/dark.scss
vendored
16
resources/assets/sass/dark.scss
vendored
@ -4,17 +4,20 @@ body.dark-mode-reverse {
|
||||
.ivu-btn-grey,
|
||||
.ivu-btn-error,
|
||||
.ivu-btn-warning,
|
||||
.common-right-bottom .common-right-bottom-link {
|
||||
.common-right-bottom .common-right-bottom-link,
|
||||
.ivu-radio-group-button-solid .ivu-radio-wrapper-checked:not(.ivu-radio-wrapper-disabled) {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.ivu-btn-grey {
|
||||
background-color: #c7c7c7;
|
||||
border-color: #c7c7c7;
|
||||
|
||||
&:hover {
|
||||
background-color: #d5d5d5;
|
||||
border-color: #d5d5d5;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
@ -23,10 +26,12 @@ body.dark-mode-reverse {
|
||||
.ivu-btn-error {
|
||||
background-color: #ff9d84;
|
||||
border-color: #ff9d84;
|
||||
|
||||
&:hover {
|
||||
background-color: #ffbdab;
|
||||
border-color: #ffbdab;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
@ -260,9 +265,11 @@ body.dark-mode-reverse {
|
||||
background-color: #d34521;
|
||||
border-color: #d34521;
|
||||
}
|
||||
|
||||
.ivu-btn-grey {
|
||||
background-color: #9e9e9e;
|
||||
border-color: #969696;
|
||||
|
||||
&:hover {
|
||||
background-color: #959595;
|
||||
border-color: #959595;
|
||||
@ -585,12 +592,14 @@ body.dark-mode-reverse {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
&.cancel {
|
||||
.record-remove {
|
||||
background-color: #ff6565;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
.record-convert,
|
||||
.record-remove {
|
||||
color: #000000;
|
||||
@ -609,6 +618,7 @@ body.dark-mode-reverse {
|
||||
|
||||
.chat-input-convert-transfer {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
|
||||
.convert-box {
|
||||
.convert-body {
|
||||
.convert-content {
|
||||
@ -621,14 +631,17 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.convert-footer {
|
||||
color: #000000;
|
||||
|
||||
> li {
|
||||
> i {
|
||||
&.send,
|
||||
&.error {
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
&.send {
|
||||
color: #0a7600;
|
||||
}
|
||||
@ -711,6 +724,7 @@ body.dark-mode-reverse {
|
||||
.icon-avatar {
|
||||
color: #1c1917;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
.item-desc {
|
||||
.desc-tag {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user