mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-12 19:35:50 +00:00
perf: 添加全局搜索功能
This commit is contained in:
parent
5bb17ddc6b
commit
027db7c0ec
@ -712,6 +712,44 @@ class DialogController extends AbstractController
|
||||
return Base::retSuccess('success', compact('data'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/esearch 14. 搜索消息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__esearch
|
||||
*
|
||||
* @apiParam {String} key 搜索关键词
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:50
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__esearch()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$key = trim(Request::input('key'));
|
||||
$list = [];
|
||||
//
|
||||
$es = new ElasticSearch(ElasticSearch::DUM);
|
||||
$searchResults = $es->searchDialogsByUserAndKeyword($user->userid, $key, Base::getPaginate(50, 20));
|
||||
if ($searchResults) {
|
||||
foreach ($searchResults as $item) {
|
||||
if ($dialog = WebSocketDialog::find($item['id'])) {
|
||||
$dialog = array_merge($dialog->toArray(), $item);
|
||||
$list[] = WebSocketDialog::synthesizeData($dialog, $user->userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', [
|
||||
'data' => $list,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/one 15. 获取单条消息
|
||||
*
|
||||
|
||||
347
resources/assets/js/components/SearchBox.vue
Executable file
347
resources/assets/js/components/SearchBox.vue
Executable file
@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<Modal
|
||||
v-model="showModal"
|
||||
class-name="common-search-box-modal"
|
||||
:closable="!isFullscreen"
|
||||
:fullscreen="isFullscreen"
|
||||
:mask-closable="false"
|
||||
:footer-hide="true"
|
||||
width="640">
|
||||
|
||||
<div class="search-header">
|
||||
<div class="search-input">
|
||||
<div class="search-pre">
|
||||
<Loading v-if="loadIng > 0"/>
|
||||
<Icon v-else type="ios-search" />
|
||||
</div>
|
||||
<Input ref="searchKey" v-model="searchKey" :placeholder="$L('请输入关键字')"/>
|
||||
</div>
|
||||
<i class="taskfont search-close" @click="onHide"></i>
|
||||
</div>
|
||||
|
||||
<div class="search-body">
|
||||
<template v-if="listLength === 0">
|
||||
<div v-if="loadIng > 0 || !searchKey.trim()" class="search-empty">
|
||||
<i class="taskfont"></i>
|
||||
<span>{{ $L(loadIng > 0 ? '正在拼命搜索...' : '请输入关键字搜索') }}</span>
|
||||
</div>
|
||||
<div v-else class="search-empty">
|
||||
<i class="taskfont"></i>
|
||||
<span class="empty-label">{{ $L('暂无相关结果') }}</span>
|
||||
<span>{{ $L('未搜到跟「(*)」相关的结果', searchKey) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="search-list">
|
||||
<ul v-for="(items, type) in list" :key="type">
|
||||
<li class="item-label">{{typeLabel(type)}}</li>
|
||||
<li v-for="item in items.slice(0, 10)" :key="item.id" @click="onClick(item)">
|
||||
<div class="item-icon">
|
||||
<EAvatar v-if="item.icon[0]==='avatar'" class="img-avatar" :src="item.icon[1]" :size="38"/>
|
||||
<i v-else-if="item.icon[0]==='department'" class="taskfont icon-avatar department"></i>
|
||||
<i v-else-if="item.icon[0]==='project'" class="taskfont icon-avatar project"></i>
|
||||
<i v-else-if="item.icon[0]==='task'" class="taskfont icon-avatar task"></i>
|
||||
<UserAvatarTip v-else-if="item.icon[0]==='user'" :userid="item.icon[1]" :size="38"/>
|
||||
<Icon v-else-if="item.icon[0]==='people'" class="icon-avatar" type="ios-people" />
|
||||
<Icon v-else class="icon-avatar" type="md-person" />
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-title">{{item.title}}</div>
|
||||
<div class="item-desc">
|
||||
<span
|
||||
class="desc-tag no-dark-content"
|
||||
v-if="item.tags"
|
||||
v-for="tag in item.tags"
|
||||
:style="tag.style">{{tag.name}}</span>
|
||||
<span class="desc-text" v-html="item.desc"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.action" class="item-action" :title="item.action">{{$A.timeFormat(item.action)}}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
import UserAvatarTip from "./UserAvatar/tip.vue";
|
||||
import emitter from "../store/events";
|
||||
|
||||
export default {
|
||||
name: 'SearchBox',
|
||||
components: {UserAvatarTip},
|
||||
props: {
|
||||
//
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loadIng: 0,
|
||||
searchKey: '',
|
||||
searchResults: [],
|
||||
|
||||
showModal: false,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
emitter.on('openSearch', this.onShow);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
emitter.off('openSearch', this.onShow);
|
||||
},
|
||||
|
||||
watch: {
|
||||
searchKey: {
|
||||
handler: function () {
|
||||
if (!this.searchKey.trim()) {
|
||||
return;
|
||||
}
|
||||
if (this.searchTimer) {
|
||||
clearTimeout(this.searchTimer)
|
||||
this.searchTimer = null;
|
||||
this.loadIng--;
|
||||
}
|
||||
this.loadIng++;
|
||||
this.searchTimer = setTimeout(() => {
|
||||
if (this.searchKey.trim()) {
|
||||
this.onSearch()
|
||||
}
|
||||
this.searchTimer = null;
|
||||
this.loadIng--;
|
||||
}, 500)
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState([
|
||||
'themeName',
|
||||
]),
|
||||
|
||||
list({searchKey, searchResults}) {
|
||||
const items = searchResults.filter(item => item.key === searchKey)
|
||||
const groups = {}
|
||||
items.forEach(item => {
|
||||
if (!groups[item.type]) {
|
||||
groups[item.type] = []
|
||||
}
|
||||
groups[item.type].push(item)
|
||||
})
|
||||
return groups
|
||||
},
|
||||
|
||||
listLength({searchKey, searchResults}) {
|
||||
const items = searchResults.filter(item => item.key === searchKey)
|
||||
return items.length
|
||||
},
|
||||
|
||||
isFullscreen({windowWidth}) {
|
||||
return windowWidth < 576
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
typeLabel(type) {
|
||||
switch (type) {
|
||||
case 'task':
|
||||
return this.$L('任务')
|
||||
case 'message':
|
||||
return this.$L('消息')
|
||||
case 'contact':
|
||||
return this.$L('联系人')
|
||||
case 'file':
|
||||
return this.$L('文件')
|
||||
case 'project':
|
||||
return this.$L('项目')
|
||||
}
|
||||
return type;
|
||||
},
|
||||
|
||||
within24Hours(date) {
|
||||
return ($A.dayjs(date).unix() - $A.dayjs().unix()) < 86400
|
||||
},
|
||||
|
||||
showProfessionDesc(dialog_user) {
|
||||
if (dialog_user && dialog_user.profession) {
|
||||
return `[${dialog_user.profession}]`
|
||||
}
|
||||
return ''
|
||||
},
|
||||
|
||||
onClick(item) {
|
||||
console.log(item);
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.showModal = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.searchKey?.focus()
|
||||
})
|
||||
},
|
||||
|
||||
onHide() {
|
||||
this.showModal = false
|
||||
},
|
||||
|
||||
onSearch() {
|
||||
this.searchTask(this.searchKey)
|
||||
this.searchMessage(this.searchKey)
|
||||
this.searchContact(this.searchKey)
|
||||
// todo searchFile、searchProject
|
||||
},
|
||||
|
||||
searchTask(key) {
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'project/task/lists',
|
||||
data: {
|
||||
keys: {name: key},
|
||||
archived: 'all',
|
||||
pagesize: 10,
|
||||
},
|
||||
}).then(({data}) => {
|
||||
const items = data.data.map(item => {
|
||||
const tags = [];
|
||||
if (item.complete_at) {
|
||||
tags.push({
|
||||
name: this.$L('已完成'),
|
||||
style: 'background-color:#666;color:#ddd',
|
||||
})
|
||||
} else if (item.overdue) {
|
||||
tags.push({
|
||||
name: this.$L('超期'),
|
||||
style: 'background-color:red;color:#ddd',
|
||||
})
|
||||
} else if (this.within24Hours(item.end_at)) {
|
||||
tags.push({
|
||||
name: this.$L('即将到期'),
|
||||
style: 'background-color:orange;color:#ddd',
|
||||
})
|
||||
}
|
||||
return {
|
||||
key,
|
||||
type: 'task',
|
||||
icon: ['task', null],
|
||||
tags,
|
||||
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
desc: item.desc,
|
||||
action: item.end_at,
|
||||
|
||||
rawData: item,
|
||||
};
|
||||
});
|
||||
items.forEach(item => {
|
||||
const index = this.searchResults.findIndex(it => it.id === item.id && it.type === 'task')
|
||||
if (index > -1) {
|
||||
this.searchResults.splice(index, 1, item)
|
||||
} else {
|
||||
this.searchResults.push(item)
|
||||
}
|
||||
})
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
})
|
||||
},
|
||||
|
||||
searchMessage(key) {
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'dialog/msg/esearch',
|
||||
data: {
|
||||
key,
|
||||
pagesize: 10,
|
||||
},
|
||||
}).then(({data}) => {
|
||||
const items = data.data.map(item => {
|
||||
let icon = 'person';
|
||||
let ival = null;
|
||||
if (item.type == 'group') {
|
||||
if (item.avatar) {
|
||||
icon = 'avatar';
|
||||
ival = item.avatar;
|
||||
} else if (item.group_type == 'department') {
|
||||
icon = 'department';
|
||||
} else if (item.group_type == 'project') {
|
||||
icon = 'project';
|
||||
} else if (['task', 'okr'].includes(item.group_type)) {
|
||||
icon = 'task';
|
||||
} else {
|
||||
icon = 'people';
|
||||
}
|
||||
} else if (item.dialog_user) {
|
||||
icon = 'user';
|
||||
ival = item.dialog_user.userid
|
||||
}
|
||||
return {
|
||||
key,
|
||||
type: 'message',
|
||||
icon: [icon, ival],
|
||||
tags: [],
|
||||
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
desc: $A.getMsgSimpleDesc(item.last_msg) || this.showProfessionDesc(item.dialog_user),
|
||||
action: item.last_at,
|
||||
|
||||
rawData: item,
|
||||
};
|
||||
})
|
||||
items.forEach(item => {
|
||||
const index = this.searchResults.findIndex(it => it.id === item.id && it.type === 'task')
|
||||
if (index > -1) {
|
||||
this.searchResults.splice(index, 1, item)
|
||||
} else {
|
||||
this.searchResults.push(item)
|
||||
}
|
||||
})
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
})
|
||||
},
|
||||
|
||||
searchContact(key) {
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'users/search',
|
||||
data: {
|
||||
keys: {key},
|
||||
pagesize: 10,
|
||||
},
|
||||
}).then(({data}) => {
|
||||
const items = data.map(item => {
|
||||
return {
|
||||
key,
|
||||
type: 'contact',
|
||||
icon: ['user', item.userid],
|
||||
tags: [],
|
||||
|
||||
id: item.userid,
|
||||
title: item.nickname,
|
||||
desc: this.showProfessionDesc(item),
|
||||
action: item.line_at,
|
||||
|
||||
rawData: item,
|
||||
};
|
||||
})
|
||||
items.forEach(item => {
|
||||
const index = this.searchResults.findIndex(it => it.id === item.id && it.type === 'contact')
|
||||
if (index > -1) {
|
||||
this.searchResults.splice(index, 1, item)
|
||||
} else {
|
||||
this.searchResults.push(item)
|
||||
}
|
||||
})
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@ -286,6 +286,9 @@
|
||||
<!--聊天窗口(移动端)-->
|
||||
<DialogModal ref="dialogModal"/>
|
||||
|
||||
<!--搜索框-->
|
||||
<SearchBox ref="searchBox"/>
|
||||
|
||||
<!--工作报告-->
|
||||
<DrawerOverlay
|
||||
v-model="workReportShow"
|
||||
@ -367,9 +370,11 @@ import ImgUpload from "../components/ImgUpload.vue";
|
||||
import ApproveDetails from "./manage/approve/details.vue";
|
||||
import notificationKoro from "notification-koro1";
|
||||
import emitter from "../store/events";
|
||||
import SearchBox from "../components/SearchBox.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SearchBox,
|
||||
ApproveDetails,
|
||||
ImgUpload,
|
||||
UserSelect,
|
||||
|
||||
@ -5,7 +5,12 @@
|
||||
<span @click="goForward({name: 'manage-setting-license'})">{{warningMsg}}</span>
|
||||
</Alert>
|
||||
<div class="dashboard-wrapper" :style="wrapperStyle">
|
||||
<div class="dashboard-hello">{{dashboardHello}}</div>
|
||||
<div class="dashboard-hello">
|
||||
<h2>{{dashboardHello}}</h2>
|
||||
<div class="dashboard-search" @click="openSearch">
|
||||
<i class="taskfont"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="systemConfig.timezoneDifference" class="dashboard-time">
|
||||
<span>{{$L('服务器时间')}}:</span>
|
||||
<span>{{$A.daytz().format('YYYY-MM-DD HH:mm:ss')}}</span>
|
||||
@ -101,6 +106,7 @@
|
||||
<script>
|
||||
import {mapGetters, mapState} from "vuex";
|
||||
import TaskMenu from "./components/TaskMenu";
|
||||
import emitter from "../../store/events";
|
||||
|
||||
const hiddenCaches = []
|
||||
export default {
|
||||
@ -248,6 +254,10 @@ export default {
|
||||
$A.IDBSave("dashboardHiddenColumns", this.hiddenColumns)
|
||||
},
|
||||
|
||||
openSearch() {
|
||||
emitter.emit('openSearch', null);
|
||||
},
|
||||
|
||||
openTask(task) {
|
||||
this.$store.dispatch("openTask", task)
|
||||
},
|
||||
|
||||
1
resources/assets/sass/components/_.scss
vendored
1
resources/assets/sass/components/_.scss
vendored
@ -7,6 +7,7 @@
|
||||
@import "loading";
|
||||
@import "mobile";
|
||||
@import "scroller-y";
|
||||
@import "search-box";
|
||||
@import "t-editor";
|
||||
@import "quick-edit";
|
||||
@import "tag-input";
|
||||
|
||||
255
resources/assets/sass/components/search-box.scss
vendored
Executable file
255
resources/assets/sass/components/search-box.scss
vendored
Executable file
@ -0,0 +1,255 @@
|
||||
.common-search-box-modal {
|
||||
.ivu-modal {
|
||||
max-width: 90%;
|
||||
|
||||
&.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-body {
|
||||
|
||||
.search-body {
|
||||
|
||||
.search-list {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ivu-modal-content {
|
||||
overflow: hidden;
|
||||
|
||||
.ivu-modal-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ivu-modal-body {
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.search-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px;
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
border-radius: 6px;
|
||||
height: 40px;
|
||||
padding: 8px 6px;
|
||||
|
||||
.search-pre {
|
||||
flex-shrink: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: -6px;
|
||||
font-size: 18px;
|
||||
color: #808695;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.common-loading {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ivu-input {
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-close {
|
||||
font-size: 16px;
|
||||
color: #808695;
|
||||
cursor: pointer;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-body {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
|
||||
.search-empty {
|
||||
height: 248px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
padding: 24px;
|
||||
|
||||
> i {
|
||||
font-size: 44px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
> span {
|
||||
opacity: 0.8;
|
||||
line-height: 1.6;
|
||||
max-width: 300px;
|
||||
word-break: break-all;
|
||||
text-align: center;
|
||||
|
||||
&.empty-label {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-list {
|
||||
overflow: auto;
|
||||
max-height: calc(100vh - 255px);
|
||||
|
||||
@media (max-height: 900px) {
|
||||
max-height: calc(100vh - 125px);
|
||||
}
|
||||
|
||||
> ul {
|
||||
padding: 10px;
|
||||
|
||||
> li {
|
||||
list-style: none;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
&.item-label {
|
||||
padding: 8px 10px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 9;
|
||||
border-radius: 0;
|
||||
cursor: default;
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
.img-avatar,
|
||||
.user-avatar,
|
||||
.icon-avatar {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
margin: 2px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.img-avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
> img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
font-size: 22px;
|
||||
background-color: #61B2F9;
|
||||
color: #ffffff;
|
||||
|
||||
&.department {
|
||||
background-color: #5BC7B0;
|
||||
}
|
||||
|
||||
&.project {
|
||||
background-color: #6E99EB;
|
||||
}
|
||||
|
||||
&.task {
|
||||
background-color: #9B96DF;
|
||||
}
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
margin-left: 12px;
|
||||
|
||||
.item-title,
|
||||
.item-desc {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-desc {
|
||||
padding-top: 2px;
|
||||
max-width: 100%;
|
||||
|
||||
.desc-tag {
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
margin-right: 6px;
|
||||
background-color: #f0f0f0;
|
||||
color: #303133;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
.desc-text {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-action {
|
||||
margin-left: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
resources/assets/sass/dark.scss
vendored
22
resources/assets/sass/dark.scss
vendored
@ -628,6 +628,28 @@ body.dark-mode-reverse {
|
||||
}
|
||||
}
|
||||
|
||||
.common-search-box-modal {
|
||||
.ivu-modal {
|
||||
.ivu-modal-content {
|
||||
.ivu-modal-body {
|
||||
.search-body {
|
||||
border-top-color: #e9e9e9;
|
||||
|
||||
.search-list {
|
||||
ul {
|
||||
> li {
|
||||
.icon-avatar {
|
||||
color: #1c1917;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-add {
|
||||
.task-add-template {
|
||||
> li {
|
||||
|
||||
41
resources/assets/sass/pages/page-dashboard.scss
vendored
41
resources/assets/sass/pages/page-dashboard.scss
vendored
@ -40,10 +40,43 @@
|
||||
}
|
||||
.dashboard-hello {
|
||||
padding: 6% 12px 0;
|
||||
color: $primary-title-color;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
word-wrap: break-word;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
> h2 {
|
||||
flex: 3;
|
||||
color: $primary-title-color;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.dashboard-search {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
max-width: 180px;
|
||||
height: 34px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 2px 0 24px;
|
||||
border-radius: 8px;
|
||||
color: #515a6e;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.3s;
|
||||
flex: none;
|
||||
> i {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dashboard-time,
|
||||
.dashboard-desc {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user