mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-09 06:28:11 +00:00
perf: 优化会员选择器
This commit is contained in:
parent
29bbbd804a
commit
04632182b4
@ -2,14 +2,18 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.27.31]
|
||||
## [0.27.36]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- 打开会话面板报错
|
||||
- 子任务通知无法打开
|
||||
|
||||
### Performance
|
||||
|
||||
- 优化会员选择器
|
||||
- 优化会员选择器
|
||||
- 优化图片压缩
|
||||
- 回复图片显示图片搜略图
|
||||
- 优化会员选择器
|
||||
- 会员选择下拉框提示
|
||||
|
||||
@ -56,9 +56,15 @@ class ProjectController extends AbstractController
|
||||
* - all:全部
|
||||
* - no:未归档(默认)
|
||||
* - yes:已归档
|
||||
* @apiParam {String} [getcolumn] 同时取项目列表
|
||||
* @apiParam {String} [getcolumn] 同时取列表
|
||||
* - no:不取(默认)
|
||||
* - yes:取列表
|
||||
* @apiParam {String} [getuserid] 同时取成员ID
|
||||
* - no:不取(默认)
|
||||
* - yes:取列表
|
||||
* @apiParam {String} [getstatistics] 同时取任务统计
|
||||
* - no:不取
|
||||
* - yes:取统计(默认)
|
||||
* @apiParam {Object} [keys] 搜索条件
|
||||
* - keys.name: 项目名称
|
||||
* @apiParam {String} [timerange] 时间范围(如:1678248944,1678248944)
|
||||
@ -110,6 +116,8 @@ class ProjectController extends AbstractController
|
||||
$type = Request::input('type', 'all');
|
||||
$archived = Request::input('archived', 'no');
|
||||
$getcolumn = Request::input('getcolumn', 'no');
|
||||
$getuserid = Request::input('getuserid', 'no');
|
||||
$getstatistics = Request::input('getstatistics', 'yes');
|
||||
$keys = Request::input('keys');
|
||||
$timerange = TimeRange::parse(Request::input('timerange'));
|
||||
//
|
||||
@ -151,8 +159,15 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
//
|
||||
$list = $builder->orderByDesc('projects.id')->paginate(Base::getPaginate(100, 50));
|
||||
$list->transform(function (Project $project) use ($user) {
|
||||
return array_merge($project->toArray(), $project->getTaskStatistics($user->userid));
|
||||
$list->transform(function (Project $project) use ($getstatistics, $getuserid, $user) {
|
||||
$array = $project->toArray();
|
||||
if ($getuserid == 'yes') {
|
||||
$array['userid_list'] = ProjectUser::whereProjectId($project->id)->pluck('userid')->toArray();
|
||||
}
|
||||
if ($getstatistics == 'yes') {
|
||||
$array = array_merge($array, $project->getTaskStatistics($user->userid));
|
||||
}
|
||||
return $array;
|
||||
});
|
||||
//
|
||||
$data = $list->toArray();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "DooTask",
|
||||
"version": "0.27.31",
|
||||
"version": "0.27.36",
|
||||
"description": "DooTask is task management system.",
|
||||
"scripts": {
|
||||
"start": "./cmd dev",
|
||||
|
||||
@ -1,57 +1,151 @@
|
||||
<template>
|
||||
<div class="common-user-select" :class="{'select-border': border}">
|
||||
<ul @click="onSelect(true)" :style="warpStyle">
|
||||
<div class="common-user-select" :class="warpClass">
|
||||
<ul v-if="!module" @click="onSelection">
|
||||
<li v-for="userid in values">
|
||||
<UserAvatar :userid="userid" :size="avatarSize" :show-icon="avatarIcon" :show-name="avatarName" tooltip-disabled/>
|
||||
</li>
|
||||
<li v-if="addIcon || values.length === 0" class="add-icon" :style="addStyle" @click.stop="onSelect"></li>
|
||||
<li v-if="addIcon || values.length === 0" class="add-icon" :style="addStyle" @click.stop="onSelection"></li>
|
||||
</ul>
|
||||
|
||||
<Modal
|
||||
v-model="showModal"
|
||||
:mask-closable="false"
|
||||
class-name="common-user-select-modal"
|
||||
:title="localTitle"
|
||||
:fullscreen="windowWidth < 576">
|
||||
:mask-closable="false"
|
||||
:closable="!isFullscreen"
|
||||
:fullscreen="isFullscreen"
|
||||
:footer-hide="isFullscreen"
|
||||
width="640">
|
||||
|
||||
<!-- 顶部 -->
|
||||
<template #header>
|
||||
<div v-if="isFullscreen" class="user-modal-header">
|
||||
<div class="user-modal-close" @click="showModal=false">{{$L('关闭')}}</div>
|
||||
<div class="user-modal-title">{{localTitle}}</div>
|
||||
<div class="user-modal-submit" @click="onSubmit">
|
||||
<div v-if="loadIng > 0" class="submit-loading"><Loading /></div>
|
||||
{{$L('确定')}}
|
||||
<template v-if="selects.length > 0">
|
||||
({{selects.length}}<span v-if="multipleMax">/{{multipleMax}}</span>)
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ivu-modal-header-inner">{{localTitle}}</div>
|
||||
</template>
|
||||
<template #close>
|
||||
<i class="ivu-icon ivu-icon-ios-close"></i>
|
||||
</template>
|
||||
|
||||
<!-- 搜索 -->
|
||||
<div class="user-modal-search">
|
||||
<Input v-model="searchKey" :placeholder="localPlaceholder" clearable>
|
||||
<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)">
|
||||
<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>
|
||||
<Icon v-else class="icon-avatar" type="ios-people" />
|
||||
</template>
|
||||
<UserAvatar v-else :userid="item.userid" tooltip-disabled/>
|
||||
</li>
|
||||
</ul>
|
||||
</Scrollbar>
|
||||
<Input class="search-input" v-model="searchKey" :placeholder="localPlaceholder" clearable>
|
||||
<div class="search-pre" slot="prefix">
|
||||
<Loading v-if="loadIng > 0"/>
|
||||
<Icon v-else type="ios-search" />
|
||||
</div>
|
||||
</Input>
|
||||
</div>
|
||||
<Scrollbar class="user-modal-list">
|
||||
<ul>
|
||||
|
||||
<!-- 切换 -->
|
||||
<ul v-if="isWhole" class="user-modal-switch">
|
||||
<li
|
||||
v-for="item in switchItems" :key="item.key"
|
||||
:class="{active:switchActive===item.key}"
|
||||
@click="switchActive=item.key">{{ $L(item.label) }}</li>
|
||||
</ul>
|
||||
|
||||
<!-- 列表 -->
|
||||
<Scrollbar v-if="lists.length > 0" class="user-modal-list">
|
||||
<!-- 项目 -->
|
||||
<ul v-if="switchActive == 'project'" class="user-modal-project">
|
||||
<li
|
||||
v-for="item in lists"
|
||||
:class="selectClass(item.userid_list)"
|
||||
@click="onSelectProject(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="subtitle">
|
||||
{{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: inUncancelable(item.userid) || isDisabled(item.userid)
|
||||
disabled: isUncancelable(item.userid) || isDisabled(item.userid)
|
||||
}"
|
||||
@click="selectUser(item)">
|
||||
@click="onSelectItem(item)">
|
||||
<Icon v-if="selects.includes(item.userid)" class="user-modal-icon" type="ios-checkmark-circle" />
|
||||
<Icon v-else class="user-modal-icon" type="ios-radio-button-off" />
|
||||
<UserAvatar class="user-modal-avatar" :userid="item.userid" show-name tooltip-disabled/>
|
||||
<div class="user-modal-userid">ID: {{item.userid}}</div>
|
||||
<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>
|
||||
<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 tooltip-disabled/>
|
||||
</li>
|
||||
</ul>
|
||||
</Scrollbar>
|
||||
<div v-if="multipleMax" class="user-modal-multiple">
|
||||
<Checkbox class="multiple-check" v-model="multipleCheck" @on-change="onMultipleChange" :disabled="lists.length === 0">{{$L(multipleCheck ? '取消全选' : '全选')}}</Checkbox>
|
||||
<div class="multiple-text">
|
||||
<span>{{$L('最多只能选择' + multipleMax + '个')}}</span>
|
||||
<em v-if="selects.length">({{$L(`已选${selects.length}个`)}})</em>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="adaption">
|
||||
<Button type="default" :loading="submittIng > 0" @click="showModal=false">{{$L('取消')}}</Button>
|
||||
<Button type="primary" :loading="submittIng > 0" @click="onSubmit">{{$L('确定')}}</Button>
|
||||
<!-- 空 -->
|
||||
<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>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 底部 -->
|
||||
<template #footer>
|
||||
<Button type="primary" :loading="submittIng > 0" @click="onSubmit">
|
||||
{{$L('确定')}}
|
||||
<template v-if="selects.length > 0">
|
||||
({{selects.length}}<span v-if="multipleMax">/{{multipleMax}}</span>)
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'UserSelect',
|
||||
props: {
|
||||
@ -75,6 +169,7 @@ export default {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
// 指定项目ID
|
||||
projectId: {
|
||||
type: Number,
|
||||
@ -90,6 +185,7 @@ export default {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
|
||||
// 是否显示机器人
|
||||
showBot: {
|
||||
type: Boolean,
|
||||
@ -120,16 +216,11 @@ export default {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示添加按钮(已选择为空时一定是true)
|
||||
// 是否显示添加按钮(已选择为空时强制true)
|
||||
addIcon: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否只有点击添加按钮才显示弹窗
|
||||
onlyAddIconClick: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 显示边框
|
||||
border: {
|
||||
type: Boolean,
|
||||
@ -145,25 +236,49 @@ export default {
|
||||
type: String,
|
||||
},
|
||||
|
||||
// 显示全选项
|
||||
showSelectAll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 显示所有会话(会话返回格式:d:{会话ID},建议配合 module=true 一起使用)
|
||||
showDialog: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 模块化(通过 api 方法调用)
|
||||
module: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 提交前的回调
|
||||
beforeSubmit: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadIng: 0,
|
||||
submittIng: 0,
|
||||
switchItems: [
|
||||
{key: 'recent', label: '最近'},
|
||||
{key: 'contact', label: '通讯录'},
|
||||
{key: 'project', label: '项目成员'},
|
||||
],
|
||||
switchActive: 'recent',
|
||||
|
||||
loadIng: 0, // 搜索框等待效果
|
||||
waitIng: 0, // 页面等待效果
|
||||
submittIng: 0, // 提交按钮等待效果
|
||||
|
||||
values: [],
|
||||
lists: [],
|
||||
selects: [],
|
||||
|
||||
recents: [],
|
||||
contacts: [],
|
||||
projects: [],
|
||||
|
||||
showModal: false,
|
||||
|
||||
multipleCheck: false,
|
||||
|
||||
searchTimer: null,
|
||||
searchKey: null,
|
||||
searchHistory: [],
|
||||
searchCache: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -179,73 +294,210 @@ export default {
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
|
||||
isWhole: {
|
||||
handler(value) {
|
||||
if (value) {
|
||||
this.switchActive = 'recent'
|
||||
} else {
|
||||
this.switchActive = 'contact'
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
|
||||
showModal(value) {
|
||||
if (value) {
|
||||
this.searchUser()
|
||||
this.searchBefore()
|
||||
} else {
|
||||
this.searchKey = ""
|
||||
}
|
||||
},
|
||||
|
||||
searchKey() {
|
||||
this.searchUser()
|
||||
this.searchBefore()
|
||||
},
|
||||
'lists.length'() {
|
||||
this.calcMultiple()
|
||||
},
|
||||
'selects.length'() {
|
||||
this.calcMultiple()
|
||||
|
||||
switchActive() {
|
||||
this.searchBefore()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
warpStyle() {
|
||||
if (!this.onlyAddIconClick) {
|
||||
return {
|
||||
cursor: 'pointer'
|
||||
}
|
||||
}
|
||||
...mapState([
|
||||
'cacheDialogs',
|
||||
]),
|
||||
|
||||
isFullscreen({windowWidth}) {
|
||||
return windowWidth < 576
|
||||
},
|
||||
addStyle() {
|
||||
|
||||
isWhole({projectId, noProjectId, dialogId}) {
|
||||
return projectId === 0 && noProjectId === 0 && dialogId === 0
|
||||
},
|
||||
|
||||
lists({switchActive, searchKey, recents, contacts, projects}) {
|
||||
switch (switchActive) {
|
||||
case 'recent':
|
||||
if (searchKey) {
|
||||
return recents.filter(item => {
|
||||
return `${item.name}`.indexOf(searchKey) > -1
|
||||
})
|
||||
}
|
||||
return recents
|
||||
|
||||
case 'contact':
|
||||
return contacts
|
||||
|
||||
case 'project':
|
||||
return projects
|
||||
}
|
||||
return []
|
||||
},
|
||||
|
||||
isSelectAll({lists, selects}) {
|
||||
return lists.length > 0 && lists.filter(item => selects.includes(item.userid)).length === lists.length;
|
||||
},
|
||||
|
||||
warpClass() {
|
||||
return {
|
||||
width: this.avatarSize + 'px',
|
||||
height: this.avatarSize + 'px',
|
||||
'select-module': this.module,
|
||||
'select-border': this.border,
|
||||
'select-whole': this.isWhole,
|
||||
}
|
||||
},
|
||||
localTitle() {
|
||||
if (this.title === undefined) {
|
||||
|
||||
addStyle({avatarSize}) {
|
||||
return {
|
||||
width: avatarSize + 'px',
|
||||
height: avatarSize + 'px',
|
||||
}
|
||||
},
|
||||
|
||||
localTitle({title}) {
|
||||
if (title === undefined) {
|
||||
return this.$L('选择会员')
|
||||
} else {
|
||||
return this.title;
|
||||
return title;
|
||||
}
|
||||
},
|
||||
localPlaceholder() {
|
||||
if (this.placeholder === undefined) {
|
||||
return this.$L('搜索会员')
|
||||
|
||||
localPlaceholder({placeholder}) {
|
||||
if (placeholder === undefined) {
|
||||
return this.$L('搜索')
|
||||
} else {
|
||||
return this.placeholder;
|
||||
return placeholder;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
searchUser() {
|
||||
isUncancelable(value) {
|
||||
if (this.uncancelable.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return this.uncancelable.includes(value);
|
||||
},
|
||||
|
||||
isDisabled(userid) {
|
||||
if (this.disabledChoice.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return this.disabledChoice.includes(userid)
|
||||
},
|
||||
|
||||
formatSelect(list) {
|
||||
return list.map(userid => {
|
||||
if ($A.leftExists(userid, 'd:')) {
|
||||
return this.recents.find(item => item.userid === userid)
|
||||
}
|
||||
return {
|
||||
type: 'user',
|
||||
userid,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
selectIcon(value) {
|
||||
if (value === 'all') {
|
||||
return this.isSelectAll ? 'ios-checkmark-circle' : 'ios-radio-button-off';
|
||||
}
|
||||
if ($A.isArray(value) && value.length > 0) {
|
||||
const len = value.filter(value => this.selects.includes(value)).length
|
||||
if (len === value.length) {
|
||||
return 'ios-checkmark-circle';
|
||||
}
|
||||
if (len > 0) {
|
||||
return 'ios-radio-button-on';
|
||||
}
|
||||
}
|
||||
return 'ios-radio-button-off';
|
||||
},
|
||||
|
||||
selectClass(value) {
|
||||
switch (this.selectIcon(value)) {
|
||||
case 'ios-checkmark-circle':
|
||||
return 'selected';
|
||||
case 'ios-radio-button-on':
|
||||
return 'somed';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
searchBefore() {
|
||||
if (!this.showModal) {
|
||||
return
|
||||
}
|
||||
//
|
||||
if (this.switchActive === 'recent') {
|
||||
this.searchRecent()
|
||||
} else if (this.switchActive === 'contact') {
|
||||
this.searchContact()
|
||||
} else if (this.switchActive === 'project') {
|
||||
this.searchProject()
|
||||
}
|
||||
},
|
||||
|
||||
searchRecent() {
|
||||
this.recents = this.cacheDialogs.filter(dialog => {
|
||||
if (dialog.name === undefined || dialog.dialog_delete === 1) {
|
||||
return false
|
||||
}
|
||||
if (!this.showBot && dialog.bot) {
|
||||
return false
|
||||
}
|
||||
return this.showDialog || dialog.type === 'user'
|
||||
}).sort((a, b) => {
|
||||
if (a.top_at || b.top_at) {
|
||||
return $A.Date(b.top_at) - $A.Date(a.top_at);
|
||||
}
|
||||
if (a.todo_num > 0 || b.todo_num > 0) {
|
||||
return b.todo_num - a.todo_num;
|
||||
}
|
||||
return $A.Date(b.last_at) - $A.Date(a.last_at);
|
||||
}).map(({id, name, type, group_type, avatar, dialog_user}) => {
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
group_type,
|
||||
avatar,
|
||||
userid: type === 'user' ? dialog_user.userid : `d:${id}`,
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
searchContact() {
|
||||
let key = this.searchKey;
|
||||
const history = this.searchHistory.find(item => item.key == key);
|
||||
if (history) {
|
||||
this.lists = history.data
|
||||
const cache = this.searchCache.find(item => item.type === 'contact' && item.key == key);
|
||||
if (cache) {
|
||||
this.contacts = cache.data
|
||||
}
|
||||
//
|
||||
if (this.searchTimer) {
|
||||
clearTimeout(this.searchTimer);
|
||||
}
|
||||
this.searchTimer = setTimeout(() => {
|
||||
this.waitIng++
|
||||
setTimeout(() => {
|
||||
if (this.searchKey != key) {
|
||||
this.waitIng--
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.loadIng++;
|
||||
this.loadIng++
|
||||
}, 300)
|
||||
this.$store.dispatch("call", {
|
||||
url: 'users/search',
|
||||
@ -261,35 +513,154 @@ export default {
|
||||
take: 50
|
||||
},
|
||||
}).then(({data}) => {
|
||||
this.lists = data
|
||||
data = data.map(item => Object.assign(item, {type: 'user'}))
|
||||
this.contacts = data
|
||||
//
|
||||
const index = this.searchHistory.findIndex(item => item.key == key);
|
||||
const tmpData = {key, data, time: $A.Time()};
|
||||
const index = this.searchCache.findIndex(item => item.key == key);
|
||||
const tmpData = {type: 'contact', key, data, time: $A.Time()};
|
||||
if (index > -1) {
|
||||
this.searchHistory.splice(index, 1, tmpData)
|
||||
this.searchCache.splice(index, 1, tmpData)
|
||||
} else {
|
||||
this.searchHistory.push(tmpData)
|
||||
this.searchCache.push(tmpData)
|
||||
}
|
||||
}).catch(({msg}) => {
|
||||
this.lists = []
|
||||
this.contacts = []
|
||||
$A.messageWarning(msg)
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
this.waitIng--;
|
||||
});
|
||||
}, this.searchHistory.length > 0 ? 300 : 0)
|
||||
}, this.searchCache.length > 0 ? 300 : 0)
|
||||
},
|
||||
|
||||
onSelect(warp = false) {
|
||||
if (warp === true) {
|
||||
if (this.onlyAddIconClick) {
|
||||
searchProject() {
|
||||
let key = this.searchKey;
|
||||
const cache = this.searchCache.find(item => item.type === 'project' && item.key == key);
|
||||
if (cache) {
|
||||
this.projects = cache.data
|
||||
}
|
||||
//
|
||||
this.waitIng++
|
||||
setTimeout(() => {
|
||||
if (this.searchKey != key) {
|
||||
this.waitIng--
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.loadIng++
|
||||
}, 300)
|
||||
this.$store.dispatch("call", {
|
||||
url: 'project/lists',
|
||||
data: {
|
||||
type: 'team',
|
||||
keys: {
|
||||
name: key,
|
||||
},
|
||||
getuserid: 'yes',
|
||||
getstatistics: 'no'
|
||||
},
|
||||
}).then(({data}) => {
|
||||
data = data.data.map(item => Object.assign(item, {type: 'project'}))
|
||||
this.projects = data
|
||||
//
|
||||
const index = this.searchCache.findIndex(item => item.key == key);
|
||||
const tmpData = {type: 'project', key, data, time: $A.Time()};
|
||||
if (index > -1) {
|
||||
this.searchCache.splice(index, 1, tmpData)
|
||||
} else {
|
||||
this.searchCache.push(tmpData)
|
||||
}
|
||||
}).catch(({msg}) => {
|
||||
this.projects = []
|
||||
$A.messageWarning(msg)
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
this.waitIng--;
|
||||
});
|
||||
}, this.searchCache.length > 0 ? 300 : 0)
|
||||
},
|
||||
|
||||
onSelection() {
|
||||
this.$nextTick(_ => {
|
||||
this.selects = $A.cloneJSON(this.values)
|
||||
this.showModal = true
|
||||
})
|
||||
},
|
||||
|
||||
onSelectAll() {
|
||||
if (this.isSelectAll) {
|
||||
this.selects = $A.cloneJSON(this.uncancelable)
|
||||
return
|
||||
}
|
||||
let optional = this.multipleMax - this.selects.length
|
||||
this.lists.some(item => {
|
||||
if (this.isUncancelable(item.userid)) {
|
||||
return false
|
||||
}
|
||||
if (this.isDisabled(item.userid)) {
|
||||
return false
|
||||
}
|
||||
if (optional <= 0) {
|
||||
$A.messageWarning("已超过最大选择数量")
|
||||
return true
|
||||
}
|
||||
if (!this.selects.includes(item.userid)) {
|
||||
this.selects.push(item.userid)
|
||||
optional--
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onSelectItem({userid}) {
|
||||
if (this.selects.includes(userid)) {
|
||||
if (this.isUncancelable(userid)) {
|
||||
return
|
||||
}
|
||||
this.selects = this.selects.filter(value => value != userid)
|
||||
} else {
|
||||
if (this.isDisabled(userid)) {
|
||||
return
|
||||
}
|
||||
if (this.multipleMax && this.selects.length >= this.multipleMax) {
|
||||
$A.messageWarning("已超过最大选择数量")
|
||||
return
|
||||
}
|
||||
this.selects.push(userid)
|
||||
this.$nextTick(() => {
|
||||
$A.scrollIntoViewIfNeeded(this.$refs.selected.querySelector(`li[data-id="${userid}"]`))
|
||||
})
|
||||
}
|
||||
this.selects = $A.cloneJSON(this.values)
|
||||
this.showModal = true
|
||||
},
|
||||
|
||||
onSelectProject(userid_list) {
|
||||
switch (this.selectIcon(userid_list)) {
|
||||
case 'ios-checkmark-circle':
|
||||
// 去除
|
||||
const removeList = userid_list.filter(userid => !this.isUncancelable(userid))
|
||||
if (removeList.length != userid_list.length) {
|
||||
$A.messageWarning("部分成员禁止取消")
|
||||
}
|
||||
this.selects = this.selects.filter(userid => !removeList.includes(userid))
|
||||
break;
|
||||
default:
|
||||
// 添加
|
||||
const addList = userid_list.filter(userid => !this.isDisabled(userid))
|
||||
if (addList.length != userid_list.length) {
|
||||
$A.messageWarning("部分成员禁止选择")
|
||||
}
|
||||
this.selects = this.selects.concat(addList.filter(userid => !this.selects.includes(userid)))
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onRemoveItem(userid) {
|
||||
this.selects = this.selects.filter(value => value != userid)
|
||||
},
|
||||
|
||||
onSubmit() {
|
||||
if (this.submittIng > 0) {
|
||||
return
|
||||
}
|
||||
const clone = $A.cloneJSON(this.values)
|
||||
this.values = $A.cloneJSON(this.selects)
|
||||
this.$emit('input', this.values)
|
||||
@ -313,69 +684,6 @@ export default {
|
||||
this.showModal = false
|
||||
}
|
||||
},
|
||||
|
||||
onMultipleChange(check) {
|
||||
if (check) {
|
||||
let optional = this.multipleMax - this.selects.length
|
||||
this.lists.some(item => {
|
||||
if (this.inUncancelable(item.userid)) {
|
||||
return false
|
||||
}
|
||||
if (this.isDisabled(item.userid)) {
|
||||
return false
|
||||
}
|
||||
if (optional <= 0) {
|
||||
$A.messageWarning("已超过最大选择数量")
|
||||
return true
|
||||
}
|
||||
if (!this.selects.includes(item.userid)) {
|
||||
this.selects.push(item.userid)
|
||||
optional--
|
||||
}
|
||||
})
|
||||
this.calcMultiple()
|
||||
} else {
|
||||
this.selects = $A.cloneJSON(this.uncancelable)
|
||||
}
|
||||
},
|
||||
|
||||
calcMultiple() {
|
||||
this.$nextTick(() => {
|
||||
this.multipleCheck = this.lists.length > 0 && this.lists.filter(item => this.selects.includes(item.userid)).length === this.lists.length;
|
||||
})
|
||||
},
|
||||
|
||||
selectUser(item) {
|
||||
if (this.selects.includes(item.userid)) {
|
||||
if (this.inUncancelable(item.userid)) {
|
||||
return
|
||||
}
|
||||
this.selects = this.selects.filter(userid => userid != item.userid)
|
||||
} else {
|
||||
if (this.isDisabled(item.userid)) {
|
||||
return
|
||||
}
|
||||
if (this.multipleMax && this.selects.length >= this.multipleMax) {
|
||||
$A.messageWarning("已超过最大选择数量")
|
||||
return
|
||||
}
|
||||
this.selects.push(item.userid)
|
||||
}
|
||||
},
|
||||
|
||||
inUncancelable(value) {
|
||||
if (this.uncancelable.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return this.uncancelable.includes(value);
|
||||
},
|
||||
|
||||
isDisabled(userid) {
|
||||
if (this.disabledChoice.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return this.disabledChoice.includes(userid)
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,78 +0,0 @@
|
||||
<template>
|
||||
<Form ref="forwardForm" :model="value" label-width="auto" @submit.native.prevent>
|
||||
<FormItem prop="dialogids" :label="$L('最近聊天')">
|
||||
<Select
|
||||
v-model="value.dialogids"
|
||||
:placeholder="$L('选择转发最近聊天')"
|
||||
:multiple-max="20"
|
||||
multiple
|
||||
filterable
|
||||
class="dialog-wrapper-dialogids"
|
||||
transfer-class-name="dialog-wrapper-forward">
|
||||
<div slot="drop-prepend" class="forward-drop-prepend">{{$L('最多只能选择20个')}}</div>
|
||||
<Option
|
||||
v-for="(dialog, key) in dialogList"
|
||||
:value="dialog.id"
|
||||
:key="key"
|
||||
:key-value="dialog.name"
|
||||
:label="dialog.name">
|
||||
<div class="forward-option">
|
||||
<div class="forward-avatar">
|
||||
<template v-if="dialog.type=='group'">
|
||||
<i v-if="dialog.group_type=='department'" class="taskfont icon-avatar department"></i>
|
||||
<i v-else-if="dialog.group_type=='project'" class="taskfont icon-avatar project"></i>
|
||||
<i v-else-if="dialog.group_type=='task'" class="taskfont icon-avatar task"></i>
|
||||
<Icon v-else class="icon-avatar" type="ios-people" />
|
||||
</template>
|
||||
<div v-else-if="dialog.dialog_user" class="user-avatar"><UserAvatar :userid="dialog.dialog_user.userid" :size="26"/></div>
|
||||
<Icon v-else class="icon-avatar" type="md-person" />
|
||||
</div>
|
||||
<div class="forward-name">{{ dialog.name }}</div>
|
||||
</div>
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem prop="userids" :label="`(${$L('或')}) ${$L('指定成员')}`">
|
||||
<UserSelect v-model="value.userids" :multiple-max="20" :avatar-size="24" :title="$L('选择转发指定成员')" border/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {mapState} from "vuex";
|
||||
import UserSelect from "../../../components/UserSelect.vue";
|
||||
|
||||
export default {
|
||||
name: "DialogSelect",
|
||||
components: {UserSelect},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState([
|
||||
'cacheDialogs',
|
||||
]),
|
||||
|
||||
dialogList() {
|
||||
return this.cacheDialogs.filter(dialog => {
|
||||
return !(dialog.name === undefined || dialog.dialog_delete === 1);
|
||||
}).sort((a, b) => {
|
||||
if (a.top_at || b.top_at) {
|
||||
return $A.Date(b.top_at) - $A.Date(a.top_at);
|
||||
}
|
||||
if (a.todo_num > 0 || b.todo_num > 0) {
|
||||
return b.todo_num - a.todo_num;
|
||||
}
|
||||
return $A.Date(b.last_at) - $A.Date(a.last_at);
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -376,16 +376,15 @@
|
||||
</Modal>
|
||||
|
||||
<!-- 转发 -->
|
||||
<Modal
|
||||
v-model="forwardShow"
|
||||
<UserSelect
|
||||
ref="forwardSelect"
|
||||
v-model="forwardData"
|
||||
:multiple-max="50"
|
||||
:title="$L('转发')"
|
||||
:mask-closable="false">
|
||||
<DialogSelect v-model="forwardData"/>
|
||||
<div slot="footer" class="adaption">
|
||||
<Button type="default" @click="forwardShow=false">{{$L('取消')}}</Button>
|
||||
<Button type="primary" :loading="forwardLoad" @click="onForward('submit')">{{$L('转发')}}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
:before-submit="onForward"
|
||||
:show-select-all="false"
|
||||
show-dialog
|
||||
module/>
|
||||
|
||||
<!-- 设置待办 -->
|
||||
<Modal
|
||||
@ -517,7 +516,6 @@ import ChatInput from "./ChatInput";
|
||||
|
||||
import VirtualList from 'vue-virtual-scroll-list-hi'
|
||||
import {Store} from "le5le-store";
|
||||
import DialogSelect from "./DialogSelect";
|
||||
import ImgUpload from "../../../components/ImgUpload.vue";
|
||||
import {choiceEmojiOne} from "./ChatInput/one";
|
||||
|
||||
@ -529,7 +527,6 @@ export default {
|
||||
components: {
|
||||
UserSelect,
|
||||
ImgUpload,
|
||||
DialogSelect,
|
||||
DialogRespond,
|
||||
DialogItem,
|
||||
VirtualList,
|
||||
@ -592,12 +589,7 @@ export default {
|
||||
modifyData: {},
|
||||
modifyLoad: 0,
|
||||
|
||||
forwardShow: false,
|
||||
forwardLoad: false,
|
||||
forwardData: {
|
||||
dialogids: [],
|
||||
userids: [],
|
||||
},
|
||||
forwardData: [],
|
||||
|
||||
openId: 0,
|
||||
dialogDrag: false,
|
||||
@ -2045,34 +2037,32 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
onForward(type) {
|
||||
if (type === 'open') {
|
||||
this.forwardData = {
|
||||
dialogids: [],
|
||||
userids: [],
|
||||
msg_id: this.operateItem.id
|
||||
};
|
||||
this.forwardShow = true;
|
||||
} else if (type === 'submit') {
|
||||
if ($A.arrayLength(this.forwardData.dialogids) === 0 && $A.arrayLength(this.forwardData.userids) === 0) {
|
||||
$A.messageWarning("请选择转发对话或成员");
|
||||
onForward() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.forwardData.length === 0) {
|
||||
$A.messageError("请选择转发对话或成员");
|
||||
reject();
|
||||
return
|
||||
}
|
||||
this.forwardLoad = true;
|
||||
const dialogids = this.forwardData.filter(value => $A.leftExists(value, 'd:')).map(value => value.replace('d:', ''));
|
||||
const userids = this.forwardData.filter(value => !$A.leftExists(value, 'd:'));
|
||||
this.$store.dispatch("call", {
|
||||
url: 'dialog/msg/forward',
|
||||
data: this.forwardData
|
||||
data: {
|
||||
dialogids,
|
||||
userids,
|
||||
msg_id: this.operateItem.id
|
||||
}
|
||||
}).then(({data, msg}) => {
|
||||
this.forwardShow = false;
|
||||
this.$store.dispatch("saveDialogMsg", data.msgs);
|
||||
this.$store.dispatch("updateDialogLastMsg", data.msgs);
|
||||
$A.messageSuccess(msg);
|
||||
resolve();
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg);
|
||||
}).finally(_ => {
|
||||
this.forwardLoad = false;
|
||||
reject();
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onScroll(event) {
|
||||
@ -2237,7 +2227,8 @@ export default {
|
||||
break;
|
||||
|
||||
case "forward":
|
||||
this.onForward('open')
|
||||
this.forwardData = [];
|
||||
this.$refs.forwardSelect.onSelection()
|
||||
break;
|
||||
|
||||
case "withdraw":
|
||||
|
||||
@ -322,16 +322,15 @@
|
||||
</Modal>
|
||||
|
||||
<!-- 文件发送 -->
|
||||
<Modal
|
||||
v-model="sendShow"
|
||||
<UserSelect
|
||||
ref="sendFile"
|
||||
v-model="sendData"
|
||||
:multiple-max="50"
|
||||
:title="$L('发送文件')"
|
||||
:mask-closable="false">
|
||||
<DialogSelect v-model="sendData"/>
|
||||
<div slot="footer" class="adaption">
|
||||
<Button type="default" @click="sendShow=false">{{$L('取消')}}</Button>
|
||||
<Button type="primary" :loading="sendLoad" @click="onSendFile">{{$L('发送文件')}}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
:before-submit="onSendFile"
|
||||
:show-select-all="false"
|
||||
show-dialog
|
||||
module/>
|
||||
|
||||
<!--文件链接-->
|
||||
<Modal
|
||||
@ -401,7 +400,6 @@ import {sortBy} from "lodash";
|
||||
import DrawerOverlay from "../../components/DrawerOverlay";
|
||||
import PreviewImage from "../../components/PreviewImage";
|
||||
import longpress from "../../directives/longpress";
|
||||
import DialogSelect from "./components/DialogSelect";
|
||||
import UserSelect from "../../components/UserSelect.vue";
|
||||
|
||||
const FilePreview = () => import('./components/FilePreview');
|
||||
@ -409,7 +407,7 @@ const FileContent = () => import('./components/FileContent');
|
||||
const FileObject = {sort: null, mode: null, shared: null};
|
||||
|
||||
export default {
|
||||
components: {UserSelect, DialogSelect, PreviewImage, FilePreview, DrawerOverlay, FileContent},
|
||||
components: {UserSelect, PreviewImage, FilePreview, DrawerOverlay, FileContent},
|
||||
directives: {longpress},
|
||||
data() {
|
||||
return {
|
||||
@ -477,12 +475,8 @@ export default {
|
||||
shareList: [],
|
||||
shareLoad: 0,
|
||||
|
||||
sendShow: false,
|
||||
sendLoad: false,
|
||||
sendData: {
|
||||
dialogids: [],
|
||||
userids: [],
|
||||
},
|
||||
sendFileId: 0,
|
||||
sendData: [],
|
||||
|
||||
linkShow: false,
|
||||
linkData: {},
|
||||
@ -1197,12 +1191,9 @@ export default {
|
||||
break;
|
||||
|
||||
case 'send':
|
||||
this.sendData = {
|
||||
dialogids: [],
|
||||
userids: [],
|
||||
file_id: item.id
|
||||
};
|
||||
this.sendShow = true;
|
||||
this.sendFileId = item.id;
|
||||
this.sendData = [];
|
||||
this.$refs.sendFile.onSelection()
|
||||
break;
|
||||
|
||||
case 'share':
|
||||
@ -1269,24 +1260,31 @@ export default {
|
||||
},
|
||||
|
||||
onSendFile() {
|
||||
if ($A.arrayLength(this.sendData.dialogids) === 0 && $A.arrayLength(this.sendData.userids) === 0) {
|
||||
$A.messageWarning("请选择转发对话或成员");
|
||||
return
|
||||
}
|
||||
this.sendLoad = true;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'dialog/msg/sendfileid',
|
||||
data: this.sendData
|
||||
}).then(({data, msg}) => {
|
||||
this.sendShow = false;
|
||||
this.$store.dispatch("saveDialogMsg", data.msgs);
|
||||
this.$store.dispatch("updateDialogLastMsg", data.msgs);
|
||||
$A.messageSuccess(msg);
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg);
|
||||
}).finally(_ => {
|
||||
this.sendLoad = false;
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.sendData.length === 0) {
|
||||
$A.messageError("请选择转发对话或成员");
|
||||
reject();
|
||||
return
|
||||
}
|
||||
const dialogids = this.sendData.filter(value => $A.leftExists(value, 'd:')).map(value => value.replace('d:', ''));
|
||||
const userids = this.sendData.filter(value => !$A.leftExists(value, 'd:'));
|
||||
this.$store.dispatch("call", {
|
||||
url: 'dialog/msg/sendfileid',
|
||||
data: {
|
||||
dialogids,
|
||||
userids,
|
||||
file_id: this.sendFileId
|
||||
}
|
||||
}).then(({data, msg}) => {
|
||||
this.$store.dispatch("saveDialogMsg", data.msgs);
|
||||
this.$store.dispatch("updateDialogLastMsg", data.msgs);
|
||||
$A.messageSuccess(msg);
|
||||
resolve();
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg);
|
||||
reject();
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
linkGet(refresh) {
|
||||
|
||||
531
resources/assets/sass/components/user-select.scss
vendored
531
resources/assets/sass/components/user-select.scss
vendored
@ -1,152 +1,447 @@
|
||||
.common-user-select {
|
||||
&.select-module {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.select-border {
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 4px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
> ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
cursor: pointer;
|
||||
|
||||
> li {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 3px 6px 3px 0;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.add-icon {
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
background: #F2F3F5 url("data:image/svg+xml;base64,PHN2ZyB0PSIxNjg2NjIxNjA3NDE0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik04NzQuNjY2NjY3IDQ3My42aC0zMjQuMjY2NjY3VjE0OS4zMzMzMzNjMC0yMS4zMzMzMzMtMTcuMDY2NjY3LTM4LjQtMzguNC0zOC40cy0zOC40IDE3LjA2NjY2Ny0zOC40IDM4LjR2MzI0LjI2NjY2N0gxNDkuMzMzMzMzYy0yMS4zMzMzMzMgMC0zOC40IDE3LjA2NjY2Ny0zOC40IDM4LjRzMTcuMDY2NjY3IDM4LjQgMzguNCAzOC40aDMyNC4yNjY2Njd2MzI0LjI2NjY2N2MwIDIxLjMzMzMzMyAxNy4wNjY2NjcgMzguNCAzOC40IDM4LjRzMzguNC0xNy4wNjY2NjcgMzguNC0zOC40di0zMjQuMjY2NjY3aDMyNC4yNjY2NjdjMjEuMzMzMzMzIDAgMzguNC0xNy4wNjY2NjcgMzguNC0zOC40cy0xNy4wNjY2NjctMzguNC0zOC40LTM4LjR6IiAgZmlsbD0iIzYwNjI2NiI+PC9wYXRoPjwvc3ZnPg==") no-repeat center;
|
||||
background-size: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.common-user-select-modal {
|
||||
.ivu-modal-body {
|
||||
padding: 0 16px !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.user-modal-search {
|
||||
flex-shrink: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 9;
|
||||
padding: 8px 16px;
|
||||
background: #ffffff;
|
||||
.search-pre {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.common-loading {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.user-modal-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 400px;
|
||||
ul {
|
||||
padding: 8px 0;
|
||||
> li {
|
||||
list-style: none;
|
||||
padding: 8px 16px;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #f3f3f3;
|
||||
}
|
||||
&.selected {
|
||||
.user-modal-icon {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
&.disabled,
|
||||
&.disabled:hover {
|
||||
color: #c5c8ce;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.user-modal-icon {
|
||||
flex-shrink: 0;
|
||||
font-size: 24px;
|
||||
margin-right: 10px;
|
||||
color: rgba($primary-desc-color, 0.7);
|
||||
}
|
||||
.user-modal-avatar {
|
||||
flex: 1;
|
||||
.avatar-name {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.user-modal-userid {
|
||||
flex-shrink: 0;
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
color: #cccccc;
|
||||
transition: margin 0.1s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.user-modal-multiple {
|
||||
flex-shrink: 0;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: 9;
|
||||
padding: 6px 16px 0;
|
||||
background: #ffffff;
|
||||
color: $primary-desc-color;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.multiple-check {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.multiple-text {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
padding-left: 6px;
|
||||
> em {
|
||||
padding-left: 2px;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ivu-modal-fullscreen {
|
||||
.ivu-modal-content {
|
||||
margin-top: 46px;
|
||||
border-top-left-radius: 18px !important;
|
||||
border-top-right-radius: 18px !important;
|
||||
.ivu-modal-body {
|
||||
bottom: 80px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.common-user-select-modal {
|
||||
.common-user-select-modal {
|
||||
.ivu-modal {
|
||||
max-width: 90%;
|
||||
|
||||
.user-modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 500;
|
||||
|
||||
.user-modal-close {
|
||||
flex-shrink: 0;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
|
||||
.user-modal-title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
padding: 0 16px;
|
||||
color: $primary-title-color;
|
||||
}
|
||||
|
||||
.user-modal-submit {
|
||||
flex-shrink: 0;
|
||||
color: $primary-color;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.submit-loading {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ivu-modal-body {
|
||||
padding: 0 24px !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.user-modal-search {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.search-selected {
|
||||
flex: none;
|
||||
height: auto;
|
||||
flex-shrink: 0;
|
||||
max-width: 60%;
|
||||
margin-right: 12px;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
> li {
|
||||
list-style: none;
|
||||
margin-right: 6px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.img-avatar,
|
||||
.icon-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
font-size: 20px;
|
||||
background-color: #61B2F9;
|
||||
color: #ffffff;
|
||||
|
||||
&.department {
|
||||
background-color: #5BC7B0;
|
||||
}
|
||||
|
||||
&.project {
|
||||
background-color: #6E99EB;
|
||||
}
|
||||
|
||||
&.task {
|
||||
background-color: #9B96DF;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
> img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-input {
|
||||
.ivu-input {
|
||||
height: 36px;
|
||||
border-color: #f4f5f7;
|
||||
background-color: #f4f5f7;
|
||||
}
|
||||
|
||||
.search-pre {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.common-loading {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-modal-switch {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
border-radius: 4px;
|
||||
background: #F4F5F7;
|
||||
|
||||
> li {
|
||||
flex: 1;
|
||||
list-style: none;
|
||||
color: $primary-text-color;
|
||||
border-radius: 2px;
|
||||
margin: 2px;
|
||||
padding: 4px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
font-weight: 500;
|
||||
color: $primary-title-color;
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-modal-list {
|
||||
max-height: none;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 400px;
|
||||
|
||||
ul {
|
||||
padding: 16px 0;
|
||||
|
||||
&.user-modal-project {
|
||||
> li {
|
||||
|
||||
&:last-child {
|
||||
.user-modal-avatar {
|
||||
.project-name {
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 16px 0 0;
|
||||
height: 46px;
|
||||
box-sizing: content-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
.user-modal-icon {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.user-modal-avatar {
|
||||
.project-name {
|
||||
.subtitle {
|
||||
> em.all {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.somed {
|
||||
.user-modal-icon {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.user-modal-avatar {
|
||||
.project-name {
|
||||
.subtitle {
|
||||
> em.some {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled,
|
||||
&.disabled:hover {
|
||||
color: #c5c8ce;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.user-modal-icon {
|
||||
flex-shrink: 0;
|
||||
font-size: 22px;
|
||||
margin-right: 10px;
|
||||
color: rgba($primary-desc-color, 0.7);
|
||||
}
|
||||
|
||||
.user-modal-all {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.user-modal-avatar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.img-avatar,
|
||||
.icon-avatar {
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
font-size: 26px;
|
||||
background-color: #61B2F9;
|
||||
color: #ffffff;
|
||||
|
||||
&.department {
|
||||
background-color: #5BC7B0;
|
||||
}
|
||||
|
||||
&.project {
|
||||
background-color: #6E99EB;
|
||||
}
|
||||
|
||||
&.task {
|
||||
background-color: #9B96DF;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
> img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-name,
|
||||
.project-name {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
margin-left: 12px;
|
||||
|
||||
> span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: -9px;
|
||||
height: 1px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
padding-top: 2px;
|
||||
font-size: 12px;
|
||||
color: $primary-desc-color;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> em {
|
||||
display: none;
|
||||
font-style: normal;
|
||||
padding-left: 4px;
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-modal-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 36px 36px 20px;
|
||||
min-height: 200px;
|
||||
|
||||
.empty-icon {
|
||||
background-color: #f4f5f7;
|
||||
padding: 20px;
|
||||
border-radius: 50%;
|
||||
|
||||
.ivu-icon {
|
||||
color: #d1d8dd;
|
||||
font-size: 46px;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
margin-top: 16px;
|
||||
color: #bec6cc;
|
||||
background-color: #f4f5f7;
|
||||
padding: 4px 15px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ivu-modal-footer {
|
||||
border-top: 1px solid #f2f2f2 !important;
|
||||
padding: 12px 0 !important;
|
||||
margin: 0 24px !important;
|
||||
}
|
||||
|
||||
&.ivu-modal-fullscreen {
|
||||
max-width: none;
|
||||
|
||||
.ivu-modal-content {
|
||||
margin-top: 46px;
|
||||
border-top-left-radius: 18px !important;
|
||||
border-top-right-radius: 18px !important;
|
||||
}
|
||||
|
||||
.ivu-modal-header {
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
}
|
||||
|
||||
.ivu-modal-body {
|
||||
top: 60px;
|
||||
|
||||
.user-modal-search {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.user-modal-list {
|
||||
max-height: none;
|
||||
|
||||
ul {
|
||||
> li {
|
||||
&:last-child {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
87
resources/assets/sass/dark.scss
vendored
87
resources/assets/sass/dark.scss
vendored
@ -53,6 +53,7 @@ body.dark-mode-reverse {
|
||||
.overlay-close {
|
||||
> a {
|
||||
color: #323232;
|
||||
|
||||
&:hover {
|
||||
color: #000;
|
||||
}
|
||||
@ -69,6 +70,7 @@ body.dark-mode-reverse {
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.teditor-loadedstyle {
|
||||
.tox-sidebar-wrap {
|
||||
box-shadow: none
|
||||
@ -96,6 +98,7 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-column {
|
||||
> ul {
|
||||
> li {
|
||||
@ -107,6 +110,7 @@ body.dark-mode-reverse {
|
||||
color: #ed4014;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&.today {
|
||||
color: #ff9900;
|
||||
background-color: transparent;
|
||||
@ -118,6 +122,7 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-table {
|
||||
.project-table-body {
|
||||
.task-row {
|
||||
@ -128,6 +133,7 @@ body.dark-mode-reverse {
|
||||
color: #ed4014;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&.today {
|
||||
color: #ff9900;
|
||||
background-color: transparent;
|
||||
@ -149,6 +155,7 @@ body.dark-mode-reverse {
|
||||
color: #1c1917;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
.main-title {
|
||||
> h2 {
|
||||
@ -159,22 +166,26 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-scroller {
|
||||
.dialog-item {
|
||||
.dialog-view {
|
||||
.dialog-head {
|
||||
background-color: #e1e1e1;
|
||||
|
||||
.dialog-reply {
|
||||
.reply-desc {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
.content-text,
|
||||
.content-record,
|
||||
.content-meeting {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.open-approve-details {
|
||||
.cause {
|
||||
border-bottom: 1px solid #7f7f7f;
|
||||
@ -182,9 +193,11 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-emoji {
|
||||
> li {
|
||||
background-color: rgba(#f3f3f3, 0.5);
|
||||
|
||||
&.hasme {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
@ -192,18 +205,22 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.self {
|
||||
.dialog-view {
|
||||
.dialog-head {
|
||||
background-color: $primary-color;
|
||||
|
||||
.dialog-content {
|
||||
.content-text {
|
||||
> pre {
|
||||
a {
|
||||
color: #0027a1;
|
||||
}
|
||||
|
||||
.mention {
|
||||
color: #000000;
|
||||
|
||||
&.file,
|
||||
&[data-denotation-char="~"] {
|
||||
color: #0027a1 !important;
|
||||
@ -212,16 +229,20 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-emoji {
|
||||
> li {
|
||||
background-color: rgba(#b2ff93, 0.5);
|
||||
|
||||
&.hasme {
|
||||
background-color: #b2ff93;
|
||||
}
|
||||
|
||||
.emoji-users {
|
||||
&:before {
|
||||
background-color: rgba(#000000, 0.7);
|
||||
}
|
||||
|
||||
> ul {
|
||||
> li {
|
||||
color: #000000;
|
||||
@ -253,73 +274,95 @@ body.dark-mode-reverse {
|
||||
&:before {
|
||||
background-image: url("../images/file/dark/other.svg");
|
||||
}
|
||||
|
||||
&.archive:before {
|
||||
background-image: url("../images/file/dark/archive.svg");
|
||||
}
|
||||
|
||||
&.cad:before {
|
||||
background-image: url("../images/file/dark/cad.svg");
|
||||
}
|
||||
|
||||
&.code:before {
|
||||
background-image: url("../images/file/dark/code.svg");
|
||||
}
|
||||
|
||||
&.document:before {
|
||||
background-image: url("../images/file/dark/document.svg");
|
||||
}
|
||||
|
||||
&.drawio:before {
|
||||
background-image: url("../images/file/dark/drawio.svg");
|
||||
}
|
||||
|
||||
&.excel:before {
|
||||
background-image: url("../images/file/dark/excel.svg");
|
||||
}
|
||||
|
||||
&.flow:before {
|
||||
background-image: url("../images/file/dark/flow.svg");
|
||||
}
|
||||
|
||||
&.folder {
|
||||
&:before {
|
||||
background-image: url("../images/file/dark/folder.svg");
|
||||
}
|
||||
|
||||
&.share {
|
||||
&:before {
|
||||
background-image: url("../images/file/dark/folder-share.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.media:before {
|
||||
background-image: url("../images/file/dark/media.svg");
|
||||
}
|
||||
|
||||
&.mind:before {
|
||||
background-image: url("../images/file/dark/mind.svg");
|
||||
}
|
||||
|
||||
&.ofd:before {
|
||||
background-image: url("../images/file/dark/ofd.svg");
|
||||
}
|
||||
|
||||
&.pdf:before {
|
||||
background-image: url("../images/file/dark/pdf.svg");
|
||||
}
|
||||
|
||||
&.picture:before {
|
||||
background-image: url("../images/file/dark/picture.svg");
|
||||
}
|
||||
|
||||
&.ppt:before {
|
||||
background-image: url("../images/file/dark/ppt.svg");
|
||||
}
|
||||
|
||||
&.sheet:before {
|
||||
background-image: url("../images/file/dark/sheet.svg");
|
||||
}
|
||||
|
||||
&.tif:before {
|
||||
background-image: url("../images/file/dark/tif.svg");
|
||||
}
|
||||
|
||||
&.txt:before {
|
||||
background-image: url("../images/file/dark/txt.svg");
|
||||
}
|
||||
|
||||
&.updir:before {
|
||||
background-image: url("../images/file/dark/updir.svg");
|
||||
}
|
||||
|
||||
&.upload:before {
|
||||
background-image: url("../images/file/dark/upload.svg");
|
||||
}
|
||||
|
||||
&.word:before {
|
||||
background-image: url("../images/file/dark/word.svg");
|
||||
}
|
||||
|
||||
&.wps:before {
|
||||
background-image: url("../images/file/dark/wps.svg");
|
||||
}
|
||||
@ -353,10 +396,12 @@ body.dark-mode-reverse {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.block-data {
|
||||
.block-num {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.taskfont {
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
@ -376,6 +421,7 @@ body.dark-mode-reverse {
|
||||
.icon-avatar {
|
||||
color: #1c1917;
|
||||
}
|
||||
|
||||
.dialog-box {
|
||||
.dialog-text {
|
||||
color: #555;
|
||||
@ -402,6 +448,7 @@ body.dark-mode-reverse {
|
||||
.page-index {
|
||||
.page-warp {
|
||||
background-color: #efefef;
|
||||
|
||||
.page-header {
|
||||
.header-nav {
|
||||
.header-nav-box {
|
||||
@ -409,28 +456,33 @@ body.dark-mode-reverse {
|
||||
.header-right-uplog {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.header-right-1 {
|
||||
.header-right-1-dropdown {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right-2 {
|
||||
.header-right-2-dropdown {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right-3 {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-content {
|
||||
.header-title, .header-tips {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
.page-header-bottom{
|
||||
|
||||
.page-header-bottom {
|
||||
background-color: #efefef;
|
||||
}
|
||||
}
|
||||
@ -441,16 +493,19 @@ body.dark-mode-reverse {
|
||||
.ql-container {
|
||||
.ql-editor {
|
||||
color: #ccc;
|
||||
|
||||
&.ql-blank {
|
||||
&::before {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.mention {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-toolbar {
|
||||
> li {
|
||||
&.chat-send {
|
||||
@ -462,4 +517,34 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.common-user-select-modal {
|
||||
.ivu-modal {
|
||||
.ivu-modal-body {
|
||||
.user-modal-search {
|
||||
.search-selected {
|
||||
ul {
|
||||
> li {
|
||||
.icon-avatar {
|
||||
color: #1c1917;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-modal-list {
|
||||
ul {
|
||||
> li {
|
||||
.user-modal-avatar {
|
||||
.icon-avatar {
|
||||
color: #1c1917;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 71ad67db7859b6e4488311a8b03d9f4851627166
|
||||
Subproject commit 777a8d12477bda3e34ec6c9290ea5dfd84b8fd81
|
||||
Loading…
x
Reference in New Issue
Block a user