feat: 首页改版

This commit is contained in:
weifashi 2023-11-23 19:12:14 +08:00
parent ecdbf8765f
commit 32aae08ef2
37 changed files with 1452 additions and 327 deletions

View File

@ -81,6 +81,7 @@ class ProjectTaskContent extends AbstractModel
Base::makeDir(dirname($publicPath));
$result = file_put_contents($publicPath, $content);
if(!$result){
// todo 记录失败日志便于追查具体原因
info("保存任务详情至文件失败");
info($publicPath);
info($oldContent);

View File

@ -1401,3 +1401,5 @@ APP推送
必须大于0
忍心拒绝
请等待打包完成
选择一个项目查看更多任务
首页

View File

@ -17834,7 +17834,7 @@
"key": "应用",
"zh": "",
"zh-CHT": "應用",
"en": "Application",
"en": "Apps",
"ko": "응용",
"ja": "応用です",
"de": "Anwendung",
@ -18478,5 +18478,38 @@
"de": "Ich kann das einfach nicht.",
"fr": "Tolérance et refus",
"id": "Punya hati untuk menolak"
},
{
"key": "请等待打包完成",
"zh": "",
"zh-CHT": "請等待打包完成",
"en": "Please wait for packing to complete",
"ko": "패키지가 끝날 때까지 기다려 주세요",
"ja": "梱包が完了するまでお待ちいただけます。",
"de": "Bitte warten sie, bis ihr koffer fertig ist",
"fr": "Veuillez attendre que lemballage soit terminé",
"id": "Tunggu sampai paket selesai"
},
{
"key": "选择一个项目查看更多任务",
"zh": "",
"zh-CHT": "選擇一個項目查看更多任務",
"en": "Select a project to view more tasks",
"ko": "더 많은 작업을 보려면 항목을 선택하십시오",
"ja": "1つのプロジェクトを選んでより多くのタスクを見ます",
"de": "Wählen sie ein projekt, um weitere aufgaben wahrzunehmen",
"fr": "Sélectionnez un projet pour voir plus de tâches",
"id": "Pilih satu item untuk melihat lebih banyak tugas"
},
{
"key": "首页",
"zh": "",
"zh-CHT": "首頁",
"en": "Home",
"ko": "첫 페이지",
"ja": "トップページです",
"de": "Die titelseite.",
"fr": "La page de couverture",
"id": "Halaman depan"
}
]

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
if(typeof window.LANGUAGE_DATA==="undefined")window.LANGUAGE_DATA={};window.LANGUAGE_DATA["zh"]=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""]
if(typeof window.LANGUAGE_DATA==="undefined")window.LANGUAGE_DATA={};window.LANGUAGE_DATA["zh"]=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""]

View File

@ -5,17 +5,13 @@
class="page-manage-menu-dropdown main-menu"
trigger="click"
@on-click="settingRoute"
@on-visible-change="menuVisibleChange">
@on-visible-change="menuVisibleChange"
transfer>
<div :class="['manage-box-title', visibleMenu ? 'menu-visible' : '']">
<div class="manage-box-avatar">
<UserAvatar :userid="userId" :size="36" tooltipDisabled/>
<UserAvatar :userid="userId" :size="48" tooltipDisabled/>
</div>
<span>{{userInfo.nickname}}</span>
<Badge v-if="!!clientNewVersion" class="manage-box-top-report" dot/>
<div class="manage-box-arrow">
<Icon type="ios-arrow-up" />
<Icon type="ios-arrow-down" />
</div>
</div>
<DropdownMenu slot="list">
<template v-for="item in menu">
@ -96,21 +92,24 @@
<div class="menu-base">
<ul>
<li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')">
<i class="taskfont">&#xe6fb;</i>
<div class="menu-title">{{$L('仪表盘')}}</div>
<i class="taskfont" v-if="!classNameRoute('dashboard')?.active">&#xe7f8;</i>
<i class="taskfont" v-else>&#xe7f7;</i>
<div class="menu-title">{{$L('首页')}}</div>
<Badge v-if="dashboardTask.overdue_count > 0" class="menu-badge" type="error" :overflow-count="999" :count="dashboardTask.overdue_count"/>
<Badge v-else-if="dashboardTask.today_count > 0" class="menu-badge" type="info" :overflow-count="999" :count="dashboardTask.today_count"/>
<Badge v-else-if="dashboardTask.all_count > 0" class="menu-badge" type="primary" :overflow-count="999" :count="dashboardTask.all_count"/>
</li>
<li @click="toggleRoute('calendar')" :class="classNameRoute('calendar')">
<i class="taskfont">&#xe6f5;</i>
<div class="menu-title">{{$L('日历')}}</div>
</li>
<li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')">
<i class="taskfont">&#xe6eb;</i>
<i class="taskfont" v-if="!classNameRoute('messenger')?.active">&#xe6eb;</i>
<i class="taskfont" v-else>&#xe7f5;</i>
<div class="menu-title">{{$L('消息')}}</div>
<Badge class="menu-badge" :overflow-count="999" :text="msgUnreadMention"/>
</li>
<li @click="toggleRoute('project')" :class="classNameRoute('project')">
<i class="taskfont" v-if="!classNameRoute('project')?.active">&#xe6fa;</i>
<i class="taskfont" v-else>&#xe6f9;</i>
<div class="menu-title">{{$L('项目')}}</div>
</li>
<li @click="toggleRoute('file')" :class="classNameRoute('file')">
<i class="taskfont">&#xe6f3;</i>
<div class="menu-title">{{$L('文件')}}</div>
@ -122,77 +121,31 @@
</li>
</ul>
</div>
<div ref="menuProject" class="menu-project">
<ul>
<li
v-for="(item, key) in projectLists"
:ref="`project_${item.id}`"
:key="key"
:class="classNameProject(item)"
:data-id="item.id"
@click="toggleRoute('project', {projectId: item.id})"
v-longpress="handleLongpress">
<div class="project-h1">
<em @click.stop="toggleOpenMenu(item.id)"></em>
<div class="title">{{item.name}}</div>
<div v-if="item.top_at" class="icon-top"></div>
<div v-if="item.task_my_num - item.task_my_complete > 0" class="num">{{item.task_my_num - item.task_my_complete}}</div>
</div>
<div class="project-h2">
<p>
<em>{{$L('我的')}}:</em>
<span>{{item.task_my_complete}}/{{item.task_my_num}}</span>
<Progress :percent="item.task_my_percent" :stroke-width="6" />
</p>
<p>
<em>{{$L('全部')}}:</em>
<span>{{item.task_complete}}/{{item.task_num}}</span>
<Progress :percent="item.task_percent" :stroke-width="6" />
</p>
</div>
</li>
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
</ul>
</div>
</Scrollbar>
<div class="operate-position" :style="operateStyles" v-show="operateVisible">
<Dropdown
trigger="custom"
:placement="windowLandscape ? 'bottom' : 'top'"
:visible="operateVisible"
@on-clickoutside="operateVisible = false"
transfer>
<div :style="{userSelect:operateVisible ? 'none' : 'auto', height: operateStyles.height}"></div>
<DropdownMenu slot="list">
<DropdownItem @click.native="handleTopClick">
{{ $L(operateItem.top_at ? '取消置顶' : '置顶该项目') }}
</DropdownItem>
</DropdownMenu>
</Dropdown>
<div class="manage-box-new-group">
<ul>
<li>
<Tooltip :content="$L('新建项目') + ' ('+mateName+'+B)'" placement="top-start" transfer :delay="300">
<i class="taskfont" @click="onAddShow">&#xe7b9;</i>
</Tooltip>
</li>
<li>
<Tooltip :content="$L('新建任务') + ' ('+mateName+'+K)'" placement="top-start" transfer :delay="300">
<i class="taskfont" @click="onAddMenu('task')">&#xe7b5;</i>
</Tooltip>
</li>
<li>
<Tooltip :content="$L('新会议') + ' ('+mateName+'+J)'" placement="top-start" transfer :delay="300">
<i class="taskfont" @click="onAddMenu('createMeeting')">&#xe7c1;</i>
</Tooltip>
</li>
<li>
<Tooltip :content="$L('加入会议')" placement="top-start" transfer :delay="300">
<i class="taskfont" @click="onAddMenu('joinMeeting')">&#xe794;</i>
</Tooltip>
</li>
</ul>
</div>
<div
v-if="(projectSearchShow || projectTotal > 20) && windowHeight > 600"
class="manage-project-search">
<Input v-model="projectKeyValue" :placeholder="$L(`共${projectTotal || cacheProjects.length}个项目,搜索...`)" clearable>
<div class="search-pre" slot="prefix">
<Loading v-if="projectKeyLoading > 0"/>
<Icon v-else type="ios-search" />
</div>
</Input>
</div>
<ButtonGroup class="manage-box-new-group">
<Button class="manage-box-new" type="primary" icon="md-add" @click="onAddShow">{{$L('新建项目')}}</Button>
<Dropdown @on-click="onAddMenu" trigger="click">
<Button type="primary">
<Icon type="ios-arrow-down"></Icon>
</Button>
<DropdownMenu slot="list">
<DropdownItem name="task">{{$L('新建任务')}} ({{mateName}}+K)</DropdownItem>
<DropdownItem name="createMeeting">{{$L('新会议')}} ({{mateName}}+J)</DropdownItem>
<DropdownItem name="joinMeeting">{{$L('加入会议')}}</DropdownItem>
</DropdownMenu>
</Dropdown>
</ButtonGroup>
</div>
<div class="manage-box-main">
@ -373,14 +326,8 @@ export default {
exportCheckinShow: false,
exportApproveShow: false,
dialogMsgSubscribe: null,
projectKeyValue: '',
projectKeyLoading: 0,
projectSearchShow: false,
openMenu: {},
visibleMenu: false,
showMobileMenu: false,
@ -394,10 +341,6 @@ export default {
reportTabs: "my",
operateStyles: {},
operateVisible: false,
operateItem: {},
needStartHome: false,
}
},
@ -448,8 +391,6 @@ export default {
'cacheUserBasic',
'cacheTasks',
'cacheDialogs',
'cacheProjects',
'projectTotal',
'wsOpenNum',
'columnTemplate',
@ -608,19 +549,6 @@ export default {
return array
},
projectLists() {
const {projectKeyValue, cacheProjects} = this;
const data = $A.cloneJSON(cacheProjects).sort((a, b) => {
if (a.top_at || b.top_at) {
return $A.Date(b.top_at) - $A.Date(a.top_at);
}
return b.id - a.id;
});
if (projectKeyValue) {
return data.filter(item => $A.strExists(`${item.name} ${item.desc}`, projectKeyValue));
}
return data;
},
taskBrowseLists() {
const {cacheTasks, cacheTaskBrowse, userId} = this;
@ -646,17 +574,6 @@ export default {
this.chackPass();
},
projectKeyValue(val) {
if (val == '') {
return;
}
setTimeout(() => {
if (this.projectKeyValue == val) {
this.searchProject();
}
}, 600);
},
wsOpenNum(num) {
if (num <= 1) return
this.$store.dispatch("getBasicData", 600)
@ -668,25 +585,6 @@ export default {
}
},
'cacheProjects.length': {
handler() {
this.$nextTick(_ => {
const menuProject = this.$refs.menuProject
const lastEl = $A.last($A.getObject(menuProject, 'children.0.children'))
if (lastEl) {
const lastRect = lastEl.getBoundingClientRect()
const menuRect = menuProject.getBoundingClientRect()
if (lastRect.top > menuRect.top + menuRect.height) {
this.projectSearchShow = true
return
}
}
this.projectSearchShow = false
})
},
immediate: true
},
unreadAndOverdue: {
handler(val) {
if (this.$Electron) {
@ -733,10 +631,6 @@ export default {
this.goForward(location);
},
toggleOpenMenu(id) {
this.$set(this.openMenu, id, !this.openMenu[id])
},
settingRoute(path) {
switch (path) {
case 'allUser':
@ -786,8 +680,8 @@ export default {
return;
case 'okrManage':
case 'okrAnalyze':
this.goForward({
path:'/manage/apps/' + ( path == 'okrManage' ? '/#/list' : '/#/analysis'),
this.goForward({
path:'/manage/apps/' + ( path == 'okrManage' ? '/#/list' : '/#/analysis'),
});
return;
case 'logout':
@ -833,7 +727,7 @@ export default {
classNameRoute(path) {
let routeName = this.routeName
if(routeName == 'manage-approve' || routeName == 'manage-apps'){
if(routeName == 'manage-approve' || routeName == 'manage-apps' || routeName == 'manage-calendar'){
routeName = `manage-application`
}
return {
@ -841,14 +735,6 @@ export default {
};
},
classNameProject(item) {
return {
"active": this.routeName === 'manage-project' && this.$route.params.projectId == item.id,
"open-menu": this.openMenu[item.id] === true,
"operate": item.id == this.operateItem.id && this.operateVisible
};
},
onAddMenu(name) {
switch (name) {
case 'task':
@ -900,18 +786,6 @@ export default {
});
},
searchProject() {
setTimeout(() => {
this.projectKeyLoading++;
}, 1000)
this.$store.dispatch("getProjects", {
keys: {
name: this.projectKeyValue
}
}).finally(_ => {
this.projectKeyLoading--;
});
},
selectChange(index) {
this.$nextTick(() => {
@ -1063,45 +937,6 @@ export default {
}
},
handleLongpress(event, el) {
const projectId = $A.getAttr(el, 'data-id')
const projectItem = this.projectLists.find(item => item.id == projectId)
if (!projectItem) {
return
}
this.operateVisible = false;
this.operateItem = $A.isJson(projectItem) ? projectItem : {};
this.$nextTick(() => {
const projectRect = el.getBoundingClientRect();
const wrapRect = this.$refs.menuProject.getBoundingClientRect();
this.operateStyles = {
left: `${event.clientX - wrapRect.left}px`,
top: `${projectRect.top + this.windowScrollY}px`,
height: projectRect.height + 'px',
}
this.operateVisible = true;
})
},
handleTopClick() {
this.$store.dispatch("call", {
url: 'project/top',
data: {
project_id: this.operateItem.id,
},
}).then(({data}) => {
this.$store.dispatch("saveProject", data);
this.$nextTick(() => {
const active = this.$refs.menuProject.querySelector(".active")
if (active) {
$A.scrollIntoViewIfNeeded(active);
}
});
}).catch(({msg}) => {
$A.modalError(msg);
});
},
onTabbarClick(act) {
switch (act) {
case 'addTask':

View File

@ -309,7 +309,8 @@ export default {
{ value: "okr", label: "OKR管理" },
{ value: "robot", label: "AI机器人" },
{ value: "signin", label: "签到" },
{ value: "meeting", label: "会议" }
{ value: "meeting", label: "会议" },
{ value: "calendar", label: "日历" },
];
// wap
let appApplyList = this.windowOrientation != 'portrait' ? (
@ -317,7 +318,6 @@ export default {
{ value: "scan", label: "扫一扫" }
] : []
) : [
{ value: "calendar", label: "日历" },
{ value: "file", label: "文件" },
{ value: "addProject", label: "创建项目" },
{ value: "addTask", label: "添加任务" },

View File

@ -4,7 +4,7 @@
<div class="calendar-head">
<div class="calendar-titbox">
<div class="calendar-title">
<div class="common-nav-back portrait" @click="goForward({name: 'manage-application'},true)"><i class="taskfont">&#xe676;</i></div>
<div class="common-nav-back" @click="goForward({name: 'manage-application'},true)"><i class="taskfont">&#xe676;</i></div>
<h1>{{rangeText}}</h1>
</div>
<ButtonGroup class="calendar-arrow" size="small">

View File

@ -151,25 +151,28 @@ export default {
}
},
mounted() {
console.log(this.view)
console.log(this.calendars)
this.calendarInstance = new Calendar(this.$refs.tuiCalendar, {
defaultView: this.view,
taskView: this.taskView,
scheduleView: this.scheduleView,
theme: this.theme,
template: this.template,
week: this.week,
month: this.month,
calendars: this.calendars,
useCreationPopup: this.useCreationPopup,
useDetailPopup: this.useDetailPopup,
timezones: this.timezones,
disableDblClick: this.disableDblClick,
disableClick: this.disableClick,
isReadOnly: this.isReadOnly,
usageStatistics: this.usageStatistics
calendars: [],
// taskView: this.taskView,
// scheduleView: this.scheduleView,
// theme: this.theme,
// template: this.template,
// week: this.week,
// month: this.month,
// calendars: this.calendars,
// useCreationPopup: this.useCreationPopup,
// useDetailPopup: this.useDetailPopup,
// timezones: this.timezones,
// disableDblClick: this.disableDblClick,
// disableClick: this.disableClick,
// isReadOnly: this.isReadOnly,
// usageStatistics: this.usageStatistics
});
this.addEventListeners();
this.reflectSchedules();
// this.addEventListeners();
// this.reflectSchedules();
},
beforeDestroy() {
this.calendarInstance.off();

View File

@ -0,0 +1,454 @@
<template>
<div class="home-calendar">
<div class="calendar-header">
<div class="calendar-header-menu">
<h4>{{$L('(*).(*)', year, month)}}</h4>
</div>
<ButtonGroup size="small" >
<Button><Icon type="ios-arrow-back" class="month-less" @click="prevMonth"/></Button>
<Button><Icon type="ios-arrow-forward" class="month-add" @click="nextMonth"/></Button>
</ButtonGroup>
<Button class="calendar-header-back" size="small" @click="nowMonth">{{$L('今天')}}</Button>
</div>
<Scrollbar class="calendar-content">
<div class="calendar-content" @scroll="handleScroll">
<transition name="slide-up">
<table class="calendar-table" >
<thead>
<tr>
<th>{{$L('日')}}</th>
<th>{{$L('一')}}</th>
<th>{{$L('二')}}</th>
<th>{{$L('三')}}</th>
<th>{{$L('四')}}</th>
<th>{{$L('五')}}</th>
<th>{{$L('六')}}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in dateArray">
<template v-for="data in item">
<td v-if="data.month" :class="{today: data.today}">
<ETooltip max-width="auto" :disabled="true">
<div slot="content" v-html="getTimes(data.date)"></div>
<div @click="onDayClick(data)" class="item-day">
<div>{{data.day}}</div>
<i v-if="isCheck(data)" class="badge"></i>
</div>
</ETooltip>
</td>
<td v-else class="disabled">
<div @click="onDayClick(data)" class="item-day">
<div>{{data.day}}</div>
</div>
</td>
</template>
</tr>
</tbody>
</table>
</transition>
<div v-if="loadIng" class="calendar-loading">
<Loading/>
</div>
<!-- -->
<div class="calendar-tui">
<div style="height: 250px;">
<Calendar
ref="cal"
:view="calendarView"
:theme="calendarTheme"
:template="calendarTemplate"
:schedules="list"
:taskView="false"
:useCreationPopup="false"
@beforeCreateSchedule="onBeforeCreateSchedule"
@beforeClickSchedule="onBeforeClickSchedule"
@beforeUpdateSchedule="onBeforeUpdateSchedule"
disable-click/>
</div>
</div>
</div>
</Scrollbar>
</div>
</template>
<script>
import 'tui-date-picker/dist/tui-date-picker.css';
import 'tui-time-picker/dist/tui-time-picker.css';
import 'tui-calendar-hi/dist/tui-calendar-hi.css'
import {mapState, mapGetters} from "vuex";
import Calendar from "./Calendar";
import {Store} from "le5le-store";
import TaskMenu from "./TaskMenu";
import {addLanguage} from "../../../language";
export default {
name: 'HomeCalendar',
components: {TaskMenu, Calendar},
props: {
checkin: {
type: Array
}
},
data() {
return {
loadIng: 0,
//
year: '',
month: '',
startTime: '',
endTime: '',
dateArray: [],
historys: [],
showTable: true,
//
lists: [],
rangeText: 'Calendar',
rangeTime: [],
calendarView: 'day',
calendarWeek: {},
calendarMonth: {},
calendarTheme: {},
calendarTemplate: {},
calendarTask: {},
calendarMenuStyles: {
top: 0,
left: 0
},
loadTimeout: null,
};
},
created() {
const today = new Date()
this.year = today.getFullYear();
this.month = today.getMonth() + 1;
this.generateCalendar();
this.generateCalendarInstance();
},
computed: {
...mapState(['cacheTasks', 'taskCompleteTemps', 'wsOpenNum', 'themeIsDark']),
...mapGetters(['transforTasks']),
list() {
const {cacheTasks, taskCompleteTemps} = this;
const filterTask = (task, chackCompleted = true) => {
if (task.archived_at) {
return false;
}
if (task.complete_at && chackCompleted === true) {
return false;
}
if (!task.end_at) {
return false;
}
return task.owner == 1;
}
let array = cacheTasks.filter(task => filterTask(task));
if (taskCompleteTemps.length > 0) {
let tmps = cacheTasks.filter(task => taskCompleteTemps.includes(task.id) && filterTask(task, false));
if (tmps.length > 0) {
array = $A.cloneJSON(array)
array.push(...tmps);
}
}
return this.transforTasks(array).map(data => {
const isAllday = $A.rightExists(data.start_at, "00:00:00") && $A.rightExists(data.end_at, "23:59:59")
const task = {
id: data.id,
calendarId: String(data.project_id),
title: data.name,
body: data.desc,
isAllDay: isAllday,
category: isAllday ? 'allday' : 'time',
start: $A.Date(data.start_at).toISOString(),
end: $A.Date(data.end_at).toISOString(),
color: "#515a6e",
bgColor: data.color || '#E3EAFD',
borderColor: data.p_color,
priority: '',
preventClick: true,
preventCheckHide: true,
isChecked: !!data.complete_at,
//
complete_at: data.complete_at,
start_at: data.start_at,
end_at: data.end_at,
_time: data._time,
};
if (data.p_name) {
let priorityStyle = `background-color:${data.p_color}`;
if (this.themeIsDark) {
priorityStyle = `color:${data.p_color};border:1px solid ${data.p_color};padding:1px 3px;`;
}
task.priority = `<span class="priority" style="${priorityStyle}">${data.p_name}</span>`;
}
if (data.sub_my && data.sub_my.length > 0) {
task.title = `[+${data.sub_my.length}] ${task.title}`
}
if (data.sub_top === true) {
task.title = `[${this.$L('子任务')}] ${task.title}`
}
if (data.flow_item_name) {
task.title = `[${data.flow_item_name}] ${task.title}`
}
if (data.complete_at) {
task.color = "#c3c2c2"
task.bgColor = "#f3f3f3"
task.borderColor = "#e3e3e3"
} else if (data.overdue) {
task.title = `[${this.$L('超期')}] ${task.title}`
task.color = "#f56c6c"
task.bgColor = data.color || "#fef0f0"
task.priority+= `<span class="overdue">${this.$L('超期未完成')}</span>`;
}
if (!task.borderColor) {
task.borderColor = task.bgColor;
}
return task;
});
}
},
methods: {
handleScroll(event) {
//
if(event.target.scrollTop >10){
this.showTable = false;
console.log('滚动事件:', event.target.scrollTop);
}
},
isCheck(data){
let time = new Date(data.date).getTime()
return this.list.find(h=>{
let start = new Date(h.start).getTime()
let end = new Date(h.end).getTime()
return start <= time && end >= time;
})
},
getTimes(date) {
const data = this.historys.find(item => item.date == date)
return data?.section.map(item => {
return `${item[0]} - ${item[1] || 'None'}`
}).join('<br/>')
},
generateCalendar(date) {
let today = new Date($A.formatDate("Y/m/d",date))
let one = new Date(this.year, this.month - 1, 1)
let calcTime = one.getTime() - one.getDay() * 86400 * 1000
let array = []
for (let i = 0; i < 5; i++) {
array[i] = []
for (let j = 0; j < 7; j++) {
let curDate = new Date(calcTime)
let curMonth = curDate.getMonth() + 1
array[i][j] = {
day: curDate.getDate(),
date: `${curDate.getFullYear()}/${curMonth}/${curDate.getDate()}`,
today: today.getTime() == curDate.getTime(),
future: today.getTime() < curDate.getTime(),
month: curMonth == this.month
}
calcTime += 86400 * 1000
}
}
this.dateArray = array
this.startTime = array[0][0].date;
this.endTime = array[4][6].date;
},
nextMonth() {
if (this.month == 12) {
this.year++;
this.month = 1;
} else {
this.month++;
}
this.generateCalendar();
},
prevMonth() {
if (this.month == 1) {
this.year--;
this.month = 12;
} else {
this.month--;
}
this.generateCalendar();
},
nowMonth() {
this.year = parseInt($A.formatDate("Y"));
this.month = parseInt($A.formatDate("m"));
this.generateCalendar();
this.$refs.cal.getInstance().setDate(new Date());
},
onDayClick(item){
const date = new Date(item.date);
this.year = date.getFullYear();
this.month = date.getMonth() + 1;
this.generateCalendar(item.date);
this.$refs.cal.getInstance().setDate(date);
},
//
generateCalendarInstance(){
addLanguage([
{"key": "{日}", "zh": "日", "general": "Sun"},
{"key": "{一}", "zh": "一", "general": "Mon"},
{"key": "{二}", "zh": "二", "general": "Tue"},
{"key": "{三}", "zh": "三", "general": "Wed"},
{"key": "{四}", "zh": "四", "general": "Thu"},
{"key": "{五}", "zh": "五", "general": "Fri"},
{"key": "{六}", "zh": "六", "general": "Sat"},
]);
let daynames = [
this.$L('{日}'),
this.$L('{一}'),
this.$L('{二}'),
this.$L('{三}'),
this.$L('{四}'),
this.$L('{五}'),
this.$L('{六}')
];
this.calendarWeek = {daynames};
this.calendarMonth = {daynames};
this.calendarTheme = {
'common.border': '1px solid rgba(0,0,0,0)',
'month.dayname.fontSize': '14px',
'month.dayname.borderLeft': '1px solid rgba(0,0,0,0)',
'month.dayname.height': '50px',
}
if (this.windowLandscape) {
this.calendarTheme = {
'common.border': '1px solid #f4f5f5',
'month.dayname.fontSize': '14px',
'month.dayname.borderLeft': '1px solid #f4f5f5',
'month.dayname.height': '50px',
}
}
this.calendarTemplate = {
titlePlaceholder: () => {
return this.$L("任务描述")
},
popupSave: () => {
return this.$L("保存");
},
popupEdit: () => {
return this.$L("详情");
},
popupDelete: () => {
return this.$L("删除");
},
}
},
onBeforeCreateSchedule({start, end, isAllDay, guide}) {
if (isAllDay || this.calendarView == 'month') {
start = $A.date2string(start.toDate(), "Y-m-d 00:00:00")
end = $A.date2string(end.toDate(), "Y-m-d 23:59:59")
} else {
start = $A.date2string(start.toDate(), "Y-m-d H:i:s")
end = $A.date2string(end.toDate(), "Y-m-d H:i:s")
}
Store.set('addTask', {
times: [start, end],
owner: [this.userId],
beforeClose: () => guide.clearGuideElement()
});
},
onBeforeClickSchedule(event) {
const {type, schedule} = event;
let data = this.cacheTasks.find(({id}) => id === schedule.id);
if (!data) {
return;
}
switch (type) {
case "check":
this.calendarMenuStyles = {
left: `${this.getElementLeft(event.target)}px`,
top: `${this.getElementTop(event.target) - 8}px`
}
this.calendarTask = data;
this.$nextTick(this.$refs.calendarTaskMenu.show);
break;
case "edit":
this.$store.dispatch("openTask", data)
break;
case "delete":
$A.modalConfirm({
title: '删除任务',
content: '你确定要删除任务【' + data.name + '】吗?',
loading: true,
onOk: () => {
return new Promise((resolve, reject) => {
this.$store.dispatch("removeTask", {task_id: data.id}).then(({msg}) => {
resolve(msg);
}).catch(({msg}) => {
reject(msg);
this.setRenderRange();
});
})
}
});
break;
}
},
onBeforeUpdateSchedule(res) {
const {changes, schedule} = res;
let data = this.cacheTasks.find(({id}) => id === schedule.id);
if (!data) {
return;
}
if (changes.start || changes.end) {
const cal = this.$refs.cal.getInstance();
cal.updateSchedule(schedule.id, schedule.calendarId, changes);
//
this.$store.dispatch("taskUpdate", {
task_id: data.id,
times: [
(changes.start || schedule.start).toDate(),
(changes.end || schedule.end).toDate(),
],
}).then(({msg}) => {
$A.messageSuccess(msg);
}).catch(({msg}) => {
$A.modalError(msg);
this.setRenderRange();
});
}
},
getElementLeft(element) {
let actualLeft = element.offsetLeft;
let current = element.offsetParent;
while (current !== null) {
if (current == this.$el) break;
actualLeft += (current.offsetLeft + current.clientLeft);
current = current.offsetParent;
}
return actualLeft;
},
getElementTop(element) {
let actualTop = element.offsetTop;
let current = element.offsetParent;
while (current !== null) {
if (current == this.$el) break;
actualTop += (current.offsetTop + current.clientTop);
current = current.offsetParent;
}
return actualTop;
}
}
};
</script>

View File

@ -0,0 +1,229 @@
<template>
<div class="project-menu">
<PageTitle :title="$L('项目')"/>
<div class="list-search">
<div class="search-wrapper">
<Input v-model="projectKeyValue" type="text" :placeholder="$L(loadProjects ? '更新中...' : '搜索项目')" clearable>
<div class="search-pre" slot="prefix">
<Loading v-if="loadProjects"/>
<Icon v-else type="ios-search" />
</div>
</Input>
</div>
</div>
<div ref="menuProject" class="menu-project" >
<ul v-if="projectLists.length > 0">
<li
v-for="(item, key) in projectLists"
:ref="`project_${item.id}`"
:key="key"
:class="classNameProject(item)"
:data-id="item.id"
@click="toggleRoute('project', {projectId: item.id})"
v-longpress="handleLongpress">
<div class="project-h1">
<em @click.stop="toggleOpenMenu(item.id)"></em>
<div class="title">{{item.name}}</div>
<div v-if="item.top_at" class="icon-top"></div>
<div v-if="item.task_my_num - item.task_my_complete > 0" class="num">{{item.task_my_num - item.task_my_complete}}</div>
</div>
<div class="project-h2">
<p>
<em>{{$L('我的')}}:</em>
<span>{{item.task_my_complete}}/{{item.task_my_num}}</span>
<Progress :percent="item.task_my_percent" :stroke-width="6" />
</p>
<p>
<em>{{$L('全部')}}:</em>
<span>{{item.task_complete}}/{{item.task_num}}</span>
<Progress :percent="item.task_percent" :stroke-width="6" />
</p>
</div>
</li>
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
</ul>
<div v-else>
{{$L(projectKeyValue ? `没有与"${projectKeyValue}"相关的项目` : `没有任何项目`)}}
</div>
</div>
<div class="operate-position" :style="operateStyles" v-show="operateVisible">
<Dropdown
trigger="custom"
:placement="windowLandscape ? 'bottom' : 'top'"
:visible="operateVisible"
@on-clickoutside="operateVisible = false"
transfer>
<div :style="{userSelect:operateVisible ? 'none' : 'auto', height: operateStyles.height}"></div>
<DropdownMenu slot="list">
<DropdownItem @click.native="handleTopClick">
{{ $L(operateItem.top_at ? '取消置顶' : '置顶该项目') }}
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
</template>
<script>
import {mapState} from "vuex";
import longpress from "../../../directives/longpress";
export default {
name: "ProjectLists",
directives: {longpress},
props: {
projectId: {
type: Number,
default: 0
},
},
data() {
return {
openMenu: {},
projectKeyValue: '',
projectKeyLoading: 0,
projectSearchShow: false,
operateStyles: {},
operateVisible: false,
operateItem: {},
}
},
computed: {
...mapState(['cacheProjects', 'loadProjects']),
routeName() {
return this.$route.name
},
projectLists() {
const {projectKeyValue, cacheProjects} = this;
const data = $A.cloneJSON(cacheProjects).sort((a, b) => {
if (a.top_at || b.top_at) {
return $A.Date(b.top_at) - $A.Date(a.top_at);
}
return b.id - a.id;
});
if (projectKeyValue) {
return data.filter(item => $A.strExists(`${item.name} ${item.desc}`, projectKeyValue));
}
return data;
},
},
watch: {
projectKeyValue(val) {
if (val == '') {
return;
}
setTimeout(() => {
if (this.projectKeyValue == val) {
this.searchProject();
}
}, 600);
},
'cacheProjects.length': {
handler() {
this.$nextTick(_ => {
const menuProject = this.$refs.menuProject
const lastEl = $A.last($A.getObject(menuProject, 'children.0.children'))
if (lastEl) {
const lastRect = lastEl.getBoundingClientRect()
const menuRect = menuProject.getBoundingClientRect()
if (lastRect.top > menuRect.top + menuRect.height) {
this.projectSearchShow = true
return
}
}
this.projectSearchShow = false
})
},
immediate: true
},
},
methods: {
handleLongpress(event, el) {
const projectId = $A.getAttr(el, 'data-id')
const projectItem = this.projectLists.find(item => item.id == projectId)
if (!projectItem) {
return
}
this.operateVisible = false;
this.operateItem = $A.isJson(projectItem) ? projectItem : {};
this.$nextTick(() => {
const projectRect = el.getBoundingClientRect();
const wrapRect = this.$refs.menuProject.getBoundingClientRect();
this.operateStyles = {
left: `${event.clientX - wrapRect.left}px`,
top: `${projectRect.top + this.windowScrollY}px`,
height: projectRect.height + 'px',
}
this.operateVisible = true;
})
},
classNameProject(item) {
return {
"active": this.routeName === 'manage-project' && (this.projectId || this.$route.params.projectId) == item.id,
"open-menu": this.openMenu[item.id] === true,
"operate": item.id == this.operateItem.id && this.operateVisible
};
},
toggleOpenMenu(id) {
this.$set(this.openMenu, id, !this.openMenu[id])
},
async toggleRoute(path, params) {
this.showMobileMenu = false;
let location = {name: 'manage-' + path, params: params || {}};
let fileFolderId = await $A.IDBInt("fileFolderId");
if (path === 'file' && fileFolderId > 0) {
location.params.folderId = fileFolderId
}
this.goForward(location);
},
searchProject() {
setTimeout(() => {
this.projectKeyLoading++;
}, 1000)
this.$store.dispatch("getProjects", {
keys: {
name: this.projectKeyValue
}
}).finally(_ => {
this.projectKeyLoading--;
});
},
handleTopClick() {
this.$store.dispatch("call", {
url: 'project/top',
data: {
project_id: this.operateItem.id,
},
}).then(({data}) => {
this.$store.dispatch("saveProject", data);
this.$nextTick(() => {
const active = this.$refs.menuProject.querySelector(".active")
if (active) {
$A.scrollIntoViewIfNeeded(active);
}
});
}).catch(({msg}) => {
$A.modalError(msg);
});
}
}
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="page-dashboard">
<div class="page-dashboard" style="flex-direction: row;">
<PageTitle :title="$L('仪表盘')"/>
<Alert v-if="warningMsg" class="dashboard-warning" type="warning" show-icon>
<span @click="goForward({name: 'manage-setting-license'})">{{warningMsg}}</span>
@ -83,15 +83,19 @@
</template>
</Scrollbar>
</div>
<div v-if="1" style="width: 35%;min-width:400px;height: 100%;border-left: 1px solid #F4F5F7;">
<HomeCalendar/>
</div>
</div>
</template>
<script>
import {mapGetters, mapState} from "vuex";
import TaskMenu from "./components/TaskMenu";
import HomeCalendar from "./components/HomeCalendar";
export default {
components: {TaskMenu},
components: {TaskMenu, HomeCalendar},
data() {
return {
nowTime: $A.Time(),

View File

@ -52,62 +52,63 @@
@touchstart.native="listTouch"
@on-scroll="listScroll">
<ul v-if="tabActive==='dialog'" ref="ul" class="dialog">
<li
v-if="dialogList.length > 0"
v-for="(dialog, key) in dialogList"
:ref="`dialog_${dialog.id}`"
:key="key"
:data-id="dialog.id"
:class="dialogClass(dialog)"
@click="openDialog({
dialog_id: dialog.id,
search_msg_id: dialog.search_msg_id
})"
v-longpress="handleLongpress"
:style="{'background-color':dialog.color}">
<template v-if="dialog.type=='group'">
<EAvatar v-if="dialog.avatar" class="img-avatar" :src="dialog.avatar" :size="42"></EAvatar>
<i v-else-if="dialog.group_type=='department'" class="taskfont icon-avatar department">&#xe75c;</i>
<i v-else-if="dialog.group_type=='project'" class="taskfont icon-avatar project">&#xe6f9;</i>
<i v-else-if="dialog.group_type=='task'" class="taskfont icon-avatar task">&#xe6f4;</i>
<i v-else-if="dialog.group_type=='okr'" class="taskfont icon-avatar task">&#xe6f4;</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="42"/></div>
<Icon v-else class="icon-avatar" type="md-person" />
<div class="dialog-box">
<div class="dialog-title">
<div v-if="dialog.todo_num" class="todo">[{{$L('待办')}}{{formatTodoNum(dialog.todo_num)}}]</div>
<div v-if="$A.getDialogMention(dialog) > 0" class="mention">[@{{$A.getDialogMention(dialog)}}]</div>
<div v-if="dialog.bot" class="taskfont bot">&#xe68c;</div>
<template v-for="tag in $A.dialogTags(dialog)" v-if="tag.color != 'success'">
<Tag :color="tag.color" :fade="false" @on-click="openDialog(dialog.id)">{{$L(tag.text)}}</Tag>
</template>
<span>{{dialog.name}}</span>
<Icon v-if="dialog.type == 'user' && lastMsgReadDone(dialog.last_msg) && dialog.dialog_user.userid != userId" :type="lastMsgReadDone(dialog.last_msg)"/>
<em v-if="dialog.last_at">{{$A.formatTime(dialog.last_at)}}</em>
</div>
<div class="dialog-text no-dark-content">
<template v-if="dialog.extra_draft_has && dialog.id != dialogId">
<div class="last-draft">[{{$L('草稿')}}]</div>
<div class="last-text"><span>{{formatDraft(dialog.extra_draft_content)}}</span></div>
</template>
<template v-else>
<template v-if="dialog.type=='group' && dialog.last_msg && dialog.last_msg.userid">
<div v-if="dialog.last_msg.userid == userId" class="last-self">{{$L('')}}</div>
<UserAvatar v-else :userid="dialog.last_msg.userid" :show-name="true" :show-icon="false" tooltip-disabled/>
<template v-if="dialogList.length > 0">
<li
v-for="(dialog, key) in dialogList"
:ref="`dialog_${dialog.id}`"
:key="key"
:data-id="dialog.id"
:class="dialogClass(dialog)"
@click="openDialog({
dialog_id: dialog.id,
search_msg_id: dialog.search_msg_id
})"
v-longpress="handleLongpress"
:style="{'background-color':dialog.color}">
<template v-if="dialog.type=='group'">
<EAvatar v-if="dialog.avatar" class="img-avatar" :src="dialog.avatar" :size="42"></EAvatar>
<i v-else-if="dialog.group_type=='department'" class="taskfont icon-avatar department">&#xe75c;</i>
<i v-else-if="dialog.group_type=='project'" class="taskfont icon-avatar project">&#xe6f9;</i>
<i v-else-if="dialog.group_type=='task'" class="taskfont icon-avatar task">&#xe6f4;</i>
<i v-else-if="dialog.group_type=='okr'" class="taskfont icon-avatar task">&#xe6f4;</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="42"/></div>
<Icon v-else class="icon-avatar" type="md-person" />
<div class="dialog-box">
<div class="dialog-title">
<div v-if="dialog.todo_num" class="todo">[{{$L('待办')}}{{formatTodoNum(dialog.todo_num)}}]</div>
<div v-if="$A.getDialogMention(dialog) > 0" class="mention">[@{{$A.getDialogMention(dialog)}}]</div>
<div v-if="dialog.bot" class="taskfont bot">&#xe68c;</div>
<template v-for="tag in $A.dialogTags(dialog)" v-if="tag.color != 'success'">
<Tag :color="tag.color" :fade="false" @on-click="openDialog(dialog.id)">{{$L(tag.text)}}</Tag>
</template>
<div class="last-text">
<em v-if="formatMsgEmojiDesc(dialog.last_msg)">{{formatMsgEmojiDesc(dialog.last_msg)}}</em>
<span>{{$A.getMsgSimpleDesc(dialog.last_msg)}}</span>
</div>
</template>
<div v-if="dialog.silence" class="taskfont last-silence">&#xe7d7;</div>
<span>{{dialog.name}}</span>
<Icon v-if="dialog.type == 'user' && lastMsgReadDone(dialog.last_msg) && dialog.dialog_user.userid != userId" :type="lastMsgReadDone(dialog.last_msg)"/>
<em v-if="dialog.last_at">{{$A.formatTime(dialog.last_at)}}</em>
</div>
<div class="dialog-text no-dark-content">
<template v-if="dialog.extra_draft_has && dialog.id != dialogId">
<div class="last-draft">[{{$L('草稿')}}]</div>
<div class="last-text"><span>{{formatDraft(dialog.extra_draft_content)}}</span></div>
</template>
<template v-else>
<template v-if="dialog.type=='group' && dialog.last_msg && dialog.last_msg.userid">
<div v-if="dialog.last_msg.userid == userId" class="last-self">{{$L('')}}</div>
<UserAvatar v-else :userid="dialog.last_msg.userid" :show-name="true" :show-icon="false" tooltip-disabled/>
</template>
<div class="last-text">
<em v-if="formatMsgEmojiDesc(dialog.last_msg)">{{formatMsgEmojiDesc(dialog.last_msg)}}</em>
<span>{{$A.getMsgSimpleDesc(dialog.last_msg)}}</span>
</div>
</template>
<div v-if="dialog.silence" class="taskfont last-silence">&#xe7d7;</div>
</div>
</div>
</div>
<Badge class="dialog-num" :type="dialog.silence ? 'normal' : 'error'" :overflow-count="999" :count="$A.getDialogUnread(dialog, true)"/>
<div class="dialog-line"></div>
</li>
<Badge class="dialog-num" :type="dialog.silence ? 'normal' : 'error'" :overflow-count="999" :count="$A.getDialogUnread(dialog, true)"/>
<div class="dialog-line"></div>
</li>
</template>
<li v-else-if="dialogSearchLoad === 0" class="nothing">
{{$L(dialogSearchKey ? `没有任何与"${dialogSearchKey}"相关的会话` : `没有任何会话`)}}
</li>

View File

@ -1,10 +1,15 @@
<template>
<div class="page-project">
<template v-if="projectId > 0">
<ProjectMenu v-if="!windowPortrait" :projectId="projId"/>
<template v-if="projId > 0">
<ProjectPanel/>
<ProjectDialog/>
</template>
<ProjectList v-if="windowPortrait" v-show="projectId === 0"/>
<div v-else class="page-project-empty">
<div><i class="taskfont">&#xe6f9;</i></div>
<span>{{ $L('选择一个项目查看更多任务') }}</span>
</div>
<ProjectList v-if="windowPortrait" v-show="projId === 0"/>
</div>
</template>
@ -13,24 +18,25 @@ import {mapState} from "vuex";
import ProjectPanel from "./components/ProjectPanel";
import ProjectDialog from "./components/ProjectDialog";
import ProjectList from "./components/ProjectList";
import ProjectMenu from "./components/ProjectMenu";
export default {
components: {ProjectList, ProjectDialog, ProjectPanel},
components: {ProjectList, ProjectMenu, ProjectDialog, ProjectPanel},
deactivated() {
this.$store.dispatch("forgetTaskCompleteTemp", true);
},
computed: {
...mapState(['cacheProjects', 'wsOpenNum']),
...mapState(['cacheProjects', 'wsOpenNum', 'projectId']),
projectId() {
projId() {
const {projectId} = this.$route.params;
return parseInt(/^\d+$/.test(projectId) ? projectId : 0);
return parseInt(/^\d+$/.test(projectId) ? projectId : 0) || this.projectId || 0;
}
},
watch: {
projectId: {
projId: {
handler() {
this.getProjectData();
},
@ -48,15 +54,15 @@ export default {
methods: {
getProjectData() {
if (this.projectId <= 0) return;
const projectId = this.projectId;
if (this.projId <= 0) return;
const projId = this.projId;
this.$nextTick(() => {
this.$store.state.projectId = projectId;
this.$store.dispatch("getProjectOne", projectId).then(() => {
this.$store.dispatch("getColumns", projectId).catch(() => {});
this.$store.dispatch("getTaskForProject", projectId).catch(() => {})
this.$store.state.projectId = projId;
this.$store.dispatch("getProjectOne", projId).then(() => {
this.$store.dispatch("getColumns", projId).catch(() => {});
this.$store.dispatch("getTaskForProject", projId).catch(() => {})
}).catch(({msg}) => {
if (projectId !== this.projectId) {
if (projId !== this.projId) {
return;
}
$A.modalWarning({

View File

@ -113,7 +113,7 @@ export default [
},
{
name: 'manage-project',
path: 'project/:projectId',
path: 'project/:projectId?',
component: () => import('./pages/manage/project.vue'),
},
{

View File

@ -25,3 +25,5 @@
@import "team-management";
@import "update-log";
@import "task-exist-tips";
@import "project-menu";
@import "home-calendar";

View File

@ -0,0 +1,270 @@
.home-calendar {
width: 100%;
height: 100%;
color: #555;
position: relative;
border-radius: 3px;
display: flex;
flex-direction: column;
padding: 32px 0;
overflow: auto;
.calendar-header {
display: flex;
align-items: center;
margin-bottom: 20px;
padding: 0 24px;
.calendar-header-menu {
position: relative;
flex: 1;
font-size: 24px;
margin-left: 10px;
}
.calendar-header-back {
margin-left: 12px;
margin-right: 12px;
}
}
.scrollbar-content{
padding: 0 24px;
}
.calendar-content{
overflow: auto;
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
.slide-up-enter-active,
.slide-up-leave-active {
transition: all 0.1s ease-out;
}
.slide-up-enter,
.slide-up-leave-to {
transform: translateY(-100%);
}
.calendar-loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
background-color: rgba(55, 55, 55, .15);
display: flex;
align-items: center;
justify-content: center;
}
.calendar-table {
width: 100%;
border: 0;
border-spacing: 0;
border-collapse: collapse;
table-layout: fixed;
th {
text-align: center;
height: 48px;
font-weight: 700;
opacity: 0.4;
}
td {
position: relative;
text-align: center;
font-size: 14px;
height: 46px;
.item-day {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
cursor: pointer;
position: relative;
> div {
width: 32px;
height: 32px;
line-height: 32px;
max-width: 100%;
padding: 0 4px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
border-radius: 30px;
}
.badge{
width: 6px;
height: 6px;
background-color: #8FCE78;
border-radius: 100%;
position: absolute;
bottom: -2px;
}
}
&:last-child {
border-right: 0;
}
&.disabled {
color: #ccc;
background: none;
* {
color: #ccc;
}
}
&.today {
.item-day {
> div {
background-color: #8FCE78;
color: #fff;
}
.badge{
display: none;
}
}
}
}
}
//
.calendar-tui{
overflow: auto;
flex: 1;
margin-top: 22px;
border-top: 1px solid #F2F2F2;
// .tui-full-calendar-vlayout-area>div:nth-child(3){
// height: 300px !important;
// }
// .tui-full-calendar-dayname-layout{
// display: none;
// }
// .tui-full-calendar-left,.tui-full-calendar-timegrid-left{
// width: 45px !important;
// }
// .tui-full-calendar-timegrid-right{
// margin-left: 45px !important;
// }
// .tui-full-calendar-popup {
// box-shadow: none;
// margin-left: 5px;
// .tui-full-calendar-section-header {
// .tui-full-calendar-ic-checkbox-checked {
// background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAhFBMVEUAAACLz3CLz3CLz3CKzm6Gy2+Lz3CLz3CL0HCLz3CLz3CLz3CLz3CMz3GLz3CKz3CLz3CL0HCJ0G+KznCN0HCL0HCLz3CKz3CLz3CLz3CLz3CMz3CLz3CLz3GL0XCL0HCN0XKLz3CLz3CMz3CLz3CM0HCM0G+FzHCLz3CKz3CMz3CLz3Bod5CFAAAAK3RSTlMA18RAOQ3s8+Pc0rmyq3tpiUwTgBnovyDMjmNSRjUvJQX5yKB0WisKppuUFLaY7gAAAotJREFUeNrtm+FymkAUhc8KqIAgSkyUtkmsmqa97/9+HWeSudpCd8qZ7E0m+73A9/1gxmXx4IK0nbipvDFTN2lT9JDVToLh6gx/sHQSFLfEFZUEp8IFczFgrv5CTChe/TsxYvfy/IkZy7M/c2KGywDUYkgNIBdDciARUxI0YkqDiZgygRNTHKZiyhRiTAyIATEgBsSAGBADYsAnCJgdmtIywKUAOrsA94QzJyKA95/pbALcPV7piADGryzDB+Rnv3IkAhi/UhMBjF9pQwbk3/A3JRXA+7GjAng/DqEC5v3+dMYE8P6nQsIEzFOPnw7g/QpC+R8K4QJ4f5iAzaA/TMAm6fdvZQgY+wW2fn9A0a66Bee/2xLH8kp/Rr1MB/3jAxaP0ALCPzagzPBCM9r/XcYH3K4BLfD4vw75xwdMbqB4CmaD/vEB2ztc0RD+MQHFg+c85fffDPv9AbPUd6Lz+ydCBDyjh1//6WcCGngK1L8a8lMBJTwFfj8XIC16+SnX7Af9bIDUngLSrwFEwaD/VogA5YheDj7/Wv1MgL9g/8XjpwNk+c8Cyq8BYwqePX46QOkGCmi/BowrqFi/Bvg4wYv6y7e4Kz4Rfj5AnzY/WSlcwHAB5+cD9ivKzwfIbEX4qQA99RB+NkDfO4b5sRAigHjzpP0C6u6D9wuo2z/eL+DvXx8Zv4C4gVd/qABxCeFnApQT5+cDpOP8fICcLv2VxbfjlQZUNh+vU/XbBIj6jQLm9wDWlZgFyOaYHJ3Ix/gDQwyIATEgBsSAGODBfuBgPvEwH7m0YkqLVExJYfsQuPcwdjOf+5kPHoFKjKio0e3Hn90WUCyGt7v3Nf0GsjqXYOR1hh6SJsz8v0mg/AZRXmaRKXtJBwAAAABJRU5ErkJggg==);
// }
// }
// .tui-full-calendar-popup-container {
// border: 0;
// box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
// border-radius: 6px;
// }
// .tui-full-calendar-arrow-top .tui-full-calendar-popup-arrow-border {
// top: -8px;
// border-bottom-color: rgba(217, 217, 217, .5);
// }
// }
// .tui-full-calendar-dropdown-menu {
// border-color: #e8e8e8;
// width: calc(100% - 14px);
// }
// .tui-full-calendar-popup-creation {
// .tui-full-calendar-icon {
// &.tui-full-calendar-ic-title,
// &.tui-full-calendar-calendar-dot {
// display: none;
// }
// &.tui-full-calendar-ic-date {
// background-image: url("data:image/svg+xml;base64,PHN2ZyB0PSIxNjIzODU5NjcwNjA3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE2Mzg4IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTk2MCAxMjhIODMzYzAtNTMtNDMtOTYtOTYtOTZoLTE2Yy01MyAwLTk2IDQzLTk2IDk2SDQwMGMwLTI2LjUtMTAuNy01MC41LTI4LjEtNjcuOUMzNTQuNSA0Mi43IDMzMC41IDMyIDMwNCAzMmgtMTZjLTUzIDAtOTYgNDMtOTYgOTZINjRjLTM1LjMgMC02NCAyOC42LTY0IDY0djczNmMwIDM1LjMgMjguNyA2NCA2NCA2NGg4OTZjMzUuMyAwIDY0LTI4LjcgNjQtNjRWMTkyYzAtMzUuNC0yOC43LTY0LTY0LTY0eiBtLTI3MSA4YzAtMjIuMSAxNy45LTQwIDQwLTQwczQwIDE3LjkgNDAgNDB2ODBjMCAyMi4xLTE3LjkgNDAtNDAgNDAtMTEgMC0yMS00LjUtMjguMy0xMS43QzY5My41IDIzNyA2ODkgMjI3IDY4OSAyMTZ2LTgweiBtLTQzMyAwYzAtMjIuMSAxNy45LTQwIDQwLTQwczQwIDE3LjkgNDAgNDB2ODBjMCAyMi4xLTE3LjkgNDAtNDAgNDAtMTEgMC0yMS00LjUtMjguMy0xMS43QzI2MC41IDIzNyAyNTYgMjI3IDI1NiAyMTZ2LTgweiBtNzA0IDc2MGMwIDE3LjctMTQuMyAzMi0zMiAzMkg5NmMtMTcuNyAwLTMyLTE0LjMtMzItMzJWNDQ4aDg5NnY0NDh6IiBwLWlkPSIxNjM4OSIgZmlsbD0iIzUxNTE1MSI+PC9wYXRoPjwvc3ZnPg==");
// background-size: contain;
// }
// }
// .tui-full-calendar-content {
// padding-left: 0;
// }
// .tui-full-calendar-popup-section {
// display: flex;
// justify-content: space-between;
// margin-bottom: 10px;
// .tui-full-calendar-popup-section-item {
// height: 36px;
// line-height: 34px;
// border-color: #e8e8e8;
// border-radius: 4px;
// }
// .tui-full-calendar-popup-section-item input {
// height: 34px;
// }
// }
// .tui-full-calendar-section-title {
// width: 100%;
// input {
// width: 100%;
// }
// }
// .tui-full-calendar-section-start-date,
// .tui-full-calendar-section-end-date {
// width: 210px;
// .tui-full-calendar-content {
// padding-left: 8px;
// }
// }
// .tui-full-calendar-popup-location,
// .tui-full-calendar-section-private,
// .tui-full-calendar-section-allday,
// .tui-full-calendar-section-state {
// display: none;
// }
// }
// .tui-full-calendar-popup-task {
// .priority {
// color: #ffffff;
// padding: 2px 4px;
// border-radius: 4px;
// margin-right: 6px;
// }
// .overdue {
// color: #f5222d;
// background: #fff1f0;
// border: 1px solid #ffa39e;
// padding: 1px 3px;
// border-radius: 4px;
// margin-right: 6px;
// }
// .tui-full-calendar-calendar-dot,
// .tui-full-calendar-ic-priority {
// opacity: 0;
// }
// .tui-full-calendar-ic-edit {
// top: -2px;
// background-image: url("data:image/svg+xml;base64,PHN2ZyB0PSIxNjIzODU5MzY4MTg5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjExMTkiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNODMzLjQyODU3MTY4IDYySDE5MC41NzE0MjgzMmExMjguNTcxNDI4MzIgMTI4LjU3MTQyODMyIDAgMCAwLTEyOC41NzE0MjgzMiAxMjguNTcxNDI4MzJ2NjQyLjg1NzE0MzM2YTEyOC41NzE0MjgzMiAxMjguNTcxNDI4MzIgMCAwIDAgMTI4LjU3MTQyODMyIDEyOC41NzE0MjgzMmg2NDIuODU3MTQzMzZhMTI4LjU3MTQyODMyIDEyOC41NzE0MjgzMiAwIDAgMCAxMjguNTcxNDI4MzItMTI4LjU3MTQyODMyVjE5MC41NzE0MjgzMmExMjguNTcxNDI4MzIgMTI4LjU3MTQyODMyIDAgMCAwLTEyOC41NzE0MjgzMi0xMjguNTcxNDI4MzJ6IG02NC4yODU3MTQxNiA3NzEuNDI4NTcxNjhhNjQuMjg1NzE0MTYgNjQuMjg1NzE0MTYgMCAwIDEtNjQuMjg1NzE0MTcgNjQuMjg1NzE0MTZIMTkwLjU3MTQyODMyYTY0LjI4NTcxNDE2IDY0LjI4NTcxNDE2IDAgMCAxLTY0LjI4NTcxNDE2LTY0LjI4NTcxNDE2VjE5MC41NzE0MjgzMmE2NC4yODU3MTQxNiA2NC4yODU3MTQxNiAwIDAgMSA2NC4yODU3MTQxNy02NC4yODU3MTQxNmg2NDIuODU3MTQzMzVhNjQuMjg1NzE0MTYgNjQuMjg1NzE0MTYgMCAwIDEgNjQuMjg1NzE0MTYgNjQuMjg1NzE0MTd6IiBwLWlkPSIxMTIwIiBmaWxsPSIjNTE1MTUxIj48L3BhdGg+PHBhdGggZD0iTTE5MC41NzE0MjgzMiAyNTQuODU3MTQyNDhoNjQuMjg1NzE0MTZ2NjQuMjg1NzE1MDRIMTkwLjU3MTQyODMyek0zMTkuMTQyODU3NTIgMjU0Ljg1NzE0MjQ4aDQ1MHY2NC4yODU3MTUwNEgzMTkuMTQyODU3NTJ6TTE5MC41NzE0MjgzMiA0NDcuNzE0Mjg1ODRoNjQuMjg1NzE0MTZ2NjQuMjg1NzE0MTZIMTkwLjU3MTQyODMyek0zMTkuMTQyODU3NTIgNDQ3LjcxNDI4NTg0aDQ1MHY2NC4yODU3MTQxNkgzMTkuMTQyODU3NTJ6TTE5MC41NzE0MjgzMiA2NDAuNTcxNDI4MzJoNjQuMjg1NzE0MTZ2NjQuMjg1NzE0MTZIMTkwLjU3MTQyODMyek0zMTkuMTQyODU3NTIgNjQwLjU3MTQyODMyaDMyMS40Mjg1NzA4djY0LjI4NTcxNDE2SDMxOS4xNDI4NTc1MnoiIHAtaWQ9IjExMjEiIGZpbGw9IiM1MTUxNTEiPjwvcGF0aD48L3N2Zz4=");
// }
// .tui-full-calendar-ic-delete {
// top: -2px;
// background-image: url("data:image/svg+xml;base64,PHN2ZyB0PSIxNjIzODU5MzMwMTc2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijc5MiIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik04OTIuMjg4IDI1NmgtMTkxLjE2OEEyMDIuMjQgMjAyLjI0IDAgMCAwIDUwOS42MzIgNjIuMDggMjAxLjIxNiAyMDEuMjE2IDAgMCAwIDMxOC44NDggMjU2SDEyOGMtMTguNjg4IDAtNjYuMDQ4LTQuMjI0LTY2LjA0OCAyNC43NjhDNjEuOTUyIDMyNy43NDQgMTA5LjM3NiAzMjAgMTI4IDMyMGg2NHY1MTJhMTQ2LjQ5NiAxNDYuNDk2IDAgMCAwIDEyNy40MjQgMTI4aDM4Mi4yNzJBMTUwLjAxNiAxNTAuMDE2IDAgMCAwIDgzMiA4MzJsLTMuMzkyLTUxMmg2NGMxOC4zNjggMCA2NS4wMjQgMS40NzIgNjUuMDI0LTM5Ljc0NEE3Mi4zODQgNzIuMzg0IDAgMCAwIDg5Mi4yODggMjU2ek01MDkuNjMyIDEyOC41MTJBMTM4LjE3NiAxMzguMTc2IDAgMCAxIDYzNy40NCAyNTZIMzgyLjU5MmExMzcuOTIgMTM3LjkyIDAgMCAxIDEyNy4wNC0xMjcuNDg4ek03NjggODMyYTk3Ljk4NCA5Ny45ODQgMCAwIDEtNjYuODggNjRIMzE4Ljg0OGE5My41NjggOTMuNTY4IDAgMCAxLTY0LTY0VjMyMEg3Njh2NTEyeiBtLTM4NS40MDgtNjRWNTEyYzAtMTguNDk2IDAuOTYtNjAuOTkyIDM2LjczNi02MC45OTIgMjcuMzI4IDAgMjYuNDk2IDQzLjAwOCAyNi45NDQgNjAuOTkydjI1NmMwIDE4LjQ5Ni02LjQgMjAuMDMyLTI0Ljk2IDIwLjAzMnMtMzguNzItMS41MzYtMzguNzItMjAuMDMyeiBtMTkxLjE2OCAwVjUxMmE2NCA2NCAwIDAgMSAyMy44MDgtNjAuOTkyYzQyLjQzMiAwIDM5LjM2IDQzLjAwOCAzOS44NzIgNjAuOTkydjI1NmMwIDE4LjQ5Ni0xOS41ODQgMjAuMDMyLTM3Ljk1MiAyMC4wMzJzLTI1Ljc5Mi0xLjUzNi0yNS43OTItMjAuMDMyeiIgcC1pZD0iNzkzIiBmaWxsPSIjNTE1MTUxIj48L3BhdGg+PC9zdmc+");
// }
// .tui-full-calendar-popup-detail-item-separate {
// padding-left: 22px;
// }
// }
// .tui-datepicker {
// border-color: #e8e8e8;
// .tui-calendar {
// th,
// td {
// height: 32px;
// }
// .tui-calendar-prev-month.tui-calendar-date,
// .tui-calendar-next-month.tui-calendar-date {
// visibility: visible;
// }
// }
// .tui-datepicker-body .tui-timepicker,
// .tui-datepicker-footer .tui-timepicker {
// padding: 16px 46px 16px 47px;
// }
// }
// .tui-full-calendar-popup-detail-item {
// word-break: break-all;
// }
}
}
}

View File

@ -0,0 +1,220 @@
.project-menu {
padding-top: 20px;
background-color: #ffffff;
width: 240px;
max-width: 240px;
overflow: auto;
height: 100%;
padding-bottom: 20px;
display: flex;
flex-direction: column;
border-right:1px solid #F4F5F7;
.list-search {
width: 100%;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
height: 54px;
padding: 0 12px;
.search-wrapper {
flex: 1;
background-color: #F4F5F7;
padding: 0 8px;
margin: 0 4px;
border-radius: 12px;
overflow: hidden;
.search-pre {
width: 100%;
height: 100%;
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;
}
}
}
}
.menu-project {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
cursor: default;
margin: 0 auto;
width: 90%;
overflow: auto;
padding: 5px 5px 0 0;
> ul {
width: 100%;
> li {
display: flex;
flex-direction: column;
list-style: none;
cursor: pointer;
width: 100%;
margin: 2px auto;
border: 2px solid transparent;
.project-h1 {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8px 0 28px;
border-radius: 8px;
> em {
position: absolute;
top: 50%;
left: 2px;
width: 24px;
height: 24px;
cursor: pointer;
transform: translateY(-50%);
display: flex;
align-items: center;
justify-content: center;
&:before {
content: "";
width: 12px;
height: 12px;
background: url("data:image/svg+xml;base64,PHN2ZyB0PSIxNjIyMzkwODExNTQxIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI0OTk3IiB3aWR0aD0iNDgiIGhlaWdodD0iNDgiPjxwYXRoIGQ9Ik0zNjYuMTgyNCAxMDguMjM2OEw4MTIuMDMyIDQyOC4wMzJhMTAyLjQgMTAyLjQgMCAwIDEgMCAxNjYuNTAyNEwzNjYuMTgyNCA5MTQuMzI5NmExMDIuNCAxMDIuNCAwIDAgMS0xNjIuMDk5Mi04My4yNTEyVjE5MS40ODhhMTAyLjQgMTAyLjQgMCAwIDEgMTYyLjA5OTItODMuMjUxMnoiIHAtaWQ9IjI0OTk4IiBmaWxsPSIjOTk5OTk5Ij48L3BhdGg+PC9zdmc+") no-repeat center center;
background-size: contain;
transition: transform 0.2s;
}
}
.title {
flex: 1;
color: $primary-title-color;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 38px;
line-height: 38px;
}
.icon-top {
padding-left: 8px;
width: 14px;
height: 14px;
background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IuWbvuWxgl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiCiAgICAgeT0iMHB4IgogICAgIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMjQgMjQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojOEJDRjcwO30KPC9zdHlsZT4wCiAgICA8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjAuNyw4LjFjLTEuNS0xLjUtNC40LTQuNC00LjQtNC40Yy0xLjItMS4yLTIuNS0xLjQtMy40LTAuN2MtMC41LDAuNC0wLjcsMC45LTAuOCwxLjRjLTAuMSwwLjUtMC40LDEtMC44LDEuMwoJbC0wLjEsMC4xYy0yLDEuNS00LjMsMi44LTYuOCwzLjJDMy45LDkuMiwzLjMsOS41LDMsMTBjLTAuNiwwLjktMC40LDIuMSwwLjMsMi45bDMuNCwzLjRjMCwwLDAsMCwwLDBsLTMuMSwzLjEKCWMtMC4zLDAuMy0wLjMsMC44LDAsMS4xYzAuMSwwLjEsMC4zLDAuMiwwLjUsMC4yYzAuMiwwLDAuNC0wLjEsMC41LTAuMmwzLjEtMy4xYzAsMCwwLDAsMCwwbDIuOSwyLjljMS4zLDEuMywyLjUsMS41LDMuNCwwLjkKCWMwLjQtMC4zLDAuNy0wLjcsMC45LTEuMmMwLjYtMi4zLDEuNC00LjYsMi44LTYuNWwwLjUtMC43YzAuMy0wLjQsMC44LTAuOCwxLjMtMC44YzAuNS0wLjEsMS4xLTAuMywxLjQtMC44CglDMjEuNywxMC4yLDIxLjUsOC45LDIwLjcsOC4xeiIvPgo8L3N2Zz4K") no-repeat center center;
background-size: contain;
}
.num {
padding-left: 8px;
font-size: 12px;
}
}
.project-h2 {
display: none;
margin: 16px 4px;
padding: 0 8px 0 24px;
cursor: default;
> p {
display: flex;
align-items: center;
padding: 4px 0;
height: 36px;
em,
span {
font-style: normal;
font-size: 12px;
flex-shrink: 0;
padding-right: 6px;
}
.ivu-progress {
margin-right: -18px;
.ivu-progress-inner {
background-color: #e4e4e4;
}
}
}
}
&.active {
.project-h1 {
background: #F4F5F7;
}
}
&.open-menu {
.project-h1 {
> em {
&:before {
transform: rotate(90deg);
}
}
}
.project-h2 {
display: block;
}
}
&.operate {
border-color: $primary-color;
}
&.loading {
display: flex;
align-items: center;
justify-content: center;
padding: 6px;
.common-loading {
margin: 6px;
width: 22px;
height: 22px;
}
}
}
}
}
.operate-position {
position: absolute;
top: 0;
left: 0;
width: 1px;
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.project-menu-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;
}
}
}

View File

@ -5,7 +5,7 @@
flex-shrink: 0;
display: flex;
align-items: flex-start;
margin: 32px 32px 16px;
margin: 32px 20px 16px;
border-bottom: 1px solid #F4F4F5;
.calendar-titbox {
flex: 1;
@ -180,6 +180,9 @@
padding: 16px 46px 16px 47px;
}
}
.tui-full-calendar-popup-detail-item {
word-break: break-all;
}
}
}
.calendar-menu {

View File

@ -26,19 +26,23 @@
max-height: 100%;
display: flex;
flex-direction: column;
align-items: center;
align-items: flex-start;
width: 1px;
height: 100%;
flex: 1;
overflow: hidden;
.dashboard-hello,
.dashboard-desc ,
.dashboard-block ,
.dashboard-list .dashboard-title,
.dashboard-list .dashboard-ul {
width: 660px;
max-width: 80%;
margin: 0 auto;
width: 90%;
max-width: 90%;
margin: 0 30px;
}
.dashboard-hello {
padding: 6% 12px 0;
padding: 32px 12px 0;
color: $primary-title-color;
font-size: 24px;
font-weight: 600;
@ -118,8 +122,8 @@
}
.dashboard-list {
width: 100%;
margin-top: 48px;
padding-bottom: 6%;
margin-top: 22px;
padding-bottom: 32px;
.dashboard-ref {
height: 0;
}

View File

@ -17,7 +17,7 @@
position: relative;
flex-grow: 0;
flex-shrink: 0;
width: 255px;
width: 88px;
height: 100%;
background: #F4F5F7;
display: flex;
@ -44,21 +44,20 @@
display: flex;
flex-direction: column;
> li {
list-style: none;
flex-shrink: 0;
display: flex;
text-align: center;
align-items: center;
height: 36px;
color: #6b6e72;
color: #999999;
cursor: pointer;
position: relative;
width: 100%;
width: 64px;
margin: 5px auto;
padding: 0 4%;
border-radius: 4px;
padding: 8px 4%;
border-radius: 8px;
font-size: 12px;
> i {
opacity: 0.3;
font-size: 20px;
margin-right: 10px;
}
.menu-title {
flex: 1;
@ -67,14 +66,17 @@
text-overflow: ellipsis;
}
.menu-badge {
margin-left: 12px;
transform: scale(0.9);
position: absolute;
top: 0;
left: 35px;
}
&:first-child {
margin-top: 12px;
}
&.active {
background-color: #ffffff;
color: #8FCE78;
}
}
}
@ -244,11 +246,30 @@
}
}
.manage-box-new-group {
width: 80%;
width: 60%;
margin-top: 16px;
margin-bottom: 20px;
display: flex;
align-items: center;
text-align: center;
ul{
list-style-type: none;
width: 100%;
li{
margin-top: 12px;
color: #999999;
.taskfont{
font-size: 22px;
padding: 8px;
cursor: pointer;
border-radius: 8px;
&:hover{
background-color: #ffffff;
color: #8FCE78;
}
}
}
}
.manage-box-new {
flex: 1;
}
@ -299,18 +320,18 @@
display: flex;
align-items: center;
flex-shrink: 0;
padding: 6px 10px;
padding: 6px 14px;
margin-top: 27px;
border-radius: 8px;
background-color: #ffffff;
cursor: pointer;
transition: box-shadow 0.3s;
position: relative;
&.menu-visible {
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
}
.manage-box-avatar {
width: 36px;
height: 36px;
width: 48px;
height: 48px;
}
> span {
flex: 1;
@ -340,12 +361,17 @@
flex-direction: column;
justify-content: center;
flex: 0 0 auto;
position: absolute;
bottom: 12px;
right: 19px;
> i {
font-size: 12px;
margin: -1px;
}
.ivu-badge-dot {
margin-right: 4px;
width: 10px;
height: 10px;
}
}
}

View File

@ -2,6 +2,7 @@
flex: 1;
display: flex;
align-items: flex-start;
background-color: #fafafa;
.project-panel {
flex: 1;
width: 0;
@ -16,6 +17,35 @@
max-width: 520px;
flex-shrink: 0;
}
.page-project-empty{
flex: 1;
height: 90%;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
>div{
width: 86px;
height: 86px;
display: flex;
align-items: center;
justify-content: center;
background: #F4F5F7;
border-radius: 100%;
margin-bottom: 16px;
.taskfont{
color: rgba(157, 167, 175, 0.6);
font-size: 46px;
}
}
span{
background: #F4F5F7;
color: #9DA7AF;
padding: 8px 16px;
border-radius: 20px;
}
}
}
@media (max-height: 700px) {

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB