Merge branch 'master' of github.com:kuaifan/dootask into develop

# Conflicts:
#	electron/package.json
#	package.json
#	public/js/app.js
#	public/js/build/161.js
#	public/js/build/400.js
#	public/js/build/616.js
#	public/js/build/845.js
This commit is contained in:
kuaifan 2022-01-24 01:45:08 +08:00
commit 8bacc3b6ba
53 changed files with 596 additions and 267 deletions

View File

@ -876,6 +876,7 @@ class ProjectController extends AbstractController
* - yes已完成 * - yes已完成
* - no未完成 * - no未完成
* @apiParam {String} [archived] 归档状态 * @apiParam {String} [archived] 归档状态
* - all所有
* - yes已归档 * - yes已归档
* - no未归档默认 * - no未归档默认
* @apiParam {Object} sorts 排序方式 * @apiParam {Object} sorts 排序方式
@ -911,7 +912,7 @@ class ProjectController extends AbstractController
// //
$scopeAll = false; $scopeAll = false;
if ($parent_id > 0) { if ($parent_id > 0) {
ProjectTask::userTask($parent_id); ProjectTask::userTask($parent_id, str_replace(['all', 'yes', 'no'], [null, false, true], $archived));
$scopeAll = true; $scopeAll = true;
$builder->where('project_tasks.parent_id', $parent_id); $builder->where('project_tasks.parent_id', $parent_id);
} elseif ($parent_id === -1) { } elseif ($parent_id === -1) {
@ -974,6 +975,10 @@ class ProjectController extends AbstractController
* @apiName task__one * @apiName task__one
* *
* @apiParam {Number} task_id 任务ID * @apiParam {Number} task_id 任务ID
* @apiParam {String} [archived] 归档状态
* - all所有
* - yes已归档
* - no未归档默认
* *
* @apiSuccess {Number} ret 返回状态码1正确、0错误 * @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {String} msg 返回信息(错误描述)
@ -984,8 +989,9 @@ class ProjectController extends AbstractController
User::auth(); User::auth();
// //
$task_id = intval(Request::input('task_id')); $task_id = intval(Request::input('task_id'));
$archived = Request::input('archived', 'no');
// //
$task = ProjectTask::userTask($task_id, true, false, ['taskUser', 'taskTag']); $task = ProjectTask::userTask($task_id, str_replace(['all', 'yes', 'no'], [null, false, true], $archived), false, ['taskUser', 'taskTag']);
// //
$data = $task->toArray(); $data = $task->toArray();
$data['project_name'] = $task->project?->name; $data['project_name'] = $task->project?->name;
@ -1013,7 +1019,7 @@ class ProjectController extends AbstractController
// //
$task_id = intval(Request::input('task_id')); $task_id = intval(Request::input('task_id'));
// //
$task = ProjectTask::userTask($task_id); $task = ProjectTask::userTask($task_id, null);
// //
return Base::retSuccess('success', $task->content ?: json_decode('{}')); return Base::retSuccess('success', $task->content ?: json_decode('{}'));
} }
@ -1038,7 +1044,7 @@ class ProjectController extends AbstractController
// //
$task_id = intval(Request::input('task_id')); $task_id = intval(Request::input('task_id'));
// //
$task = ProjectTask::userTask($task_id); $task = ProjectTask::userTask($task_id, null);
// //
return Base::retSuccess('success', $task->taskFile); return Base::retSuccess('success', $task->taskFile);
} }
@ -1381,7 +1387,11 @@ class ProjectController extends AbstractController
} elseif ($type == 'add') { } elseif ($type == 'add') {
$task->archivedTask(Carbon::now()); $task->archivedTask(Carbon::now());
} }
return Base::retSuccess('操作成功', ['id' => $task->id]); return Base::retSuccess('操作成功', [
'id' => $task->id,
'archived_at' => $task->archived_at,
'archived_userid' => $task->archived_userid,
]);
} }
/** /**
@ -1435,7 +1445,7 @@ class ProjectController extends AbstractController
return Base::retError('记录不存在'); return Base::retError('记录不存在');
} }
// //
$task = ProjectTask::userTask($projectLog->task_id, null, true); $task = ProjectTask::userTask($projectLog->task_id, true, true);
// //
$record = $projectLog->record; $record = $projectLog->record;
if ($record['flow'] && is_array($record['flow'])) { if ($record['flow'] && is_array($record['flow'])) {

View File

@ -18,6 +18,7 @@ use Illuminate\Support\Facades\DB;
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore() * @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue() * @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Model|object|static|null cancelAppend() * @method static \Illuminate\Database\Eloquent\Model|object|static|null cancelAppend()
* @method static \Illuminate\Database\Eloquent\Model|object|static|null cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|static with($relations) * @method static \Illuminate\Database\Eloquent\Builder|static with($relations)
* @method static \Illuminate\Database\Query\Builder|static select($columns = []) * @method static \Illuminate\Database\Query\Builder|static select($columns = [])
* @method static \Illuminate\Database\Query\Builder|static whereNotIn($column, $values, $boolean = 'and') * @method static \Illuminate\Database\Query\Builder|static whereNotIn($column, $values, $boolean = 'and')
@ -72,6 +73,15 @@ class AbstractModel extends Model
return $this->setAppends([]); return $this->setAppends([]);
} }
/**
* 取消隐藏值
* @return static
*/
protected function scopeCancelHidden()
{
return $this->setHidden([]);
}
/** /**
* 为数组 / JSON 序列化准备日期。 * 为数组 / JSON 序列化准备日期。
* @param DateTimeInterface $date * @param DateTimeInterface $date

View File

@ -686,11 +686,11 @@ class ProjectTask extends AbstractModel
$start_at = Carbon::parse($subTask->start_at); $start_at = Carbon::parse($subTask->start_at);
$end_at = Carbon::parse($subTask->end_at); $end_at = Carbon::parse($subTask->end_at);
$isUp = false; $isUp = false;
if ($start_at->eq($oldAt[0]) || $start_at->lt(Carbon::parse($this->start_at))) { if (empty($subTask->start_at) || $start_at->eq($oldAt[0]) || $start_at->lt(Carbon::parse($this->start_at))) {
$subTask->start_at = $this->start_at; $subTask->start_at = $this->start_at;
$isUp = true; $isUp = true;
} }
if ($end_at->eq($oldAt[1]) || $end_at->gt(Carbon::parse($this->end_at))) { if (empty($subTask->end_at) || $end_at->eq($oldAt[1]) || $end_at->gt(Carbon::parse($this->end_at))) {
$subTask->end_at = $this->end_at; $subTask->end_at = $this->end_at;
$isUp = true; $isUp = true;
} }
@ -918,7 +918,6 @@ class ProjectTask extends AbstractModel
$this->archived_userid = User::userid(); $this->archived_userid = User::userid();
$this->archived_follow = 0; $this->archived_follow = 0;
$this->addLog("任务取消归档"); $this->addLog("任务取消归档");
$this->pushMsg('add', ProjectTask::oneTask($this->id));
} else { } else {
// 归档任务 // 归档任务
if ($isAuto === true) { if ($isAuto === true) {
@ -932,8 +931,12 @@ class ProjectTask extends AbstractModel
$this->archived_userid = $userid; $this->archived_userid = $userid;
$this->archived_follow = 0; $this->archived_follow = 0;
$this->addLog($logText, [], $userid); $this->addLog($logText, [], $userid);
$this->pushMsg('archived');
} }
$this->pushMsg('update', [
'id' => $this->id,
'archived_at' => $this->archived_at,
'archived_userid' => $this->archived_userid,
]);
self::whereParentId($this->id)->update([ self::whereParentId($this->id)->update([
'archived_at' => $this->archived_at, 'archived_at' => $this->archived_at,
'archived_userid' => $this->archived_userid, 'archived_userid' => $this->archived_userid,
@ -1059,7 +1062,7 @@ class ProjectTask extends AbstractModel
/** /**
* 获取任务(会员有任务权限 会员存在项目内) * 获取任务(会员有任务权限 会员存在项目内)
* @param int $task_id * @param int $task_id
* @param bool $archived true:仅限未归档, false:不限制, null:不限制 * @param bool $archived true:仅限未归档, false:仅限已归档, null:不限制
* @param int|bool $mustOwner 0|false:不限制, 1|true:限制任务或项目负责人, 2:已有负责人才限制任务或项目负责人 * @param int|bool $mustOwner 0|false:不限制, 1|true:限制任务或项目负责人, 2:已有负责人才限制任务或项目负责人
* @param array $with * @param array $with
* @return self * @return self
@ -1072,7 +1075,7 @@ class ProjectTask extends AbstractModel
throw new ApiException('任务不存在', [ 'task_id' => $task_id ], -4002); throw new ApiException('任务不存在', [ 'task_id' => $task_id ], -4002);
} }
if ($archived === true && $task->archived_at != null) { if ($archived === true && $task->archived_at != null) {
throw new ApiException('任务已归档', [ 'task_id' => $task_id ], -4002); throw new ApiException('任务已归档', [ 'task_id' => $task_id ]);
} }
if ($archived === false && $task->archived_at == null) { if ($archived === false && $task->archived_at == null) {
throw new ApiException('任务未归档', [ 'task_id' => $task_id ]); throw new ApiException('任务未归档', [ 'task_id' => $task_id ]);

View File

@ -120,10 +120,10 @@ class WebSocketDialog extends AbstractModel
break; break;
case "group": case "group":
if ($dialog->group_type === 'project') { if ($dialog->group_type === 'project') {
$dialog->group_info = Project::withTrashed()->select(['id', 'name'])->whereDialogId($dialog->id)->first(); $dialog->group_info = Project::withTrashed()->select(['id', 'name', 'archived_at', 'deleted_at'])->whereDialogId($dialog->id)->first()?->cancelAppend()->cancelHidden();
$dialog->name = $dialog->group_info ? $dialog->group_info->name : ''; $dialog->name = $dialog->group_info ? $dialog->group_info->name : '';
} elseif ($dialog->group_type === 'task') { } elseif ($dialog->group_type === 'task') {
$dialog->group_info = ProjectTask::withTrashed()->select(['id', 'name'])->whereDialogId($dialog->id)->first(); $dialog->group_info = ProjectTask::withTrashed()->select(['id', 'name', 'complete_at', 'archived_at', 'deleted_at'])->whereDialogId($dialog->id)->first()?->cancelAppend()->cancelHidden();
$dialog->name = $dialog->group_info ? $dialog->group_info->name : ''; $dialog->name = $dialog->group_info ? $dialog->group_info->name : '';
} }
break; break;

View File

@ -1,6 +1,6 @@
{ {
"name": "DooTask", "name": "DooTask",
"version": "0.6.83", "version": "0.6.90",
"description": "DooTask is task management system.", "description": "DooTask is task management system.",
"main": "main.js", "main": "main.js",
"license": "MIT", "license": "MIT",

View File

@ -1,6 +1,6 @@
{ {
"name": "DooTask", "name": "DooTask",
"version": "0.6.83", "version": "0.6.90",
"description": "DooTask is task management system.", "description": "DooTask is task management system.",
"scripts": { "scripts": {
"start": "./cmd dev", "start": "./cmd dev",

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/build/113.js vendored Normal file

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,6 +1,6 @@
/*! /*!
* html2canvas 1.3.4 <https://html2canvas.hertzen.com> * html2canvas 1.4.0 <https://html2canvas.hertzen.com>
* Copyright (c) 2021 Niklas von Hertzen <https://hertzen.com> * Copyright (c) 2022 Niklas von Hertzen <https://hertzen.com>
* Released under MIT License * Released under MIT License
*/ */

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

2
public/js/build/400.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
/*! /*!
* TOAST UI Calendar * TOAST UI Calendar
* @version 1.15.1 | Wed Dec 22 2021 * @version 1.15.1-5 | Sun Jan 09 2022
* @author NHN FE Development Lab <dl_javascript@nhn.com> * @author NHN FE Development Lab <dl_javascript@nhn.com>
* @license MIT * @license MIT
*/ */
@ -304,7 +304,7 @@
/*! ./weekdayInMonth */ /*! ./weekdayInMonth */
/*! @license DOMPurify 2.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.1/LICENSE */ /*! @license DOMPurify 2.3.4 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.4/LICENSE */
/*! dompurify */ /*! dompurify */

1
public/js/build/43.js vendored Normal file

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

1
public/js/build/893.js vendored Normal file

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,5 +1,6 @@
{ {
"/js/app.js": "/js/app.js", "/js/app.js": "/js/app.js",
"/css/app.css": "/css/app.css", "/css/app.css": "/css/app.css",
"/.DS_Store": "/.DS_Store",
"/favicon.ico": "/favicon.ico" "/favicon.ico": "/favicon.ico"
} }

View File

@ -315,6 +315,46 @@
return [new Date(), lastSecond(e.getTime())]; return [new Date(), lastSecond(e.getTime())];
} }
}]; }];
},
/**
* 对话标签
* @param dialog
* @returns {*[]}
*/
dialogTags(dialog) {
let tags = [];
if (dialog.type == 'group') {
if (['project', 'task'].includes(dialog.group_type) && $A.isJson(dialog.group_info)) {
if (dialog.group_type == 'task' && dialog.group_info.complete_at) {
tags.push({
color: 'success',
text: '已完成'
})
}
if (dialog.group_info.deleted_at) {
tags.push({
color: 'red',
text: '已删除'
})
} else if (dialog.group_info.archived_at) {
tags.push({
color: 'default',
text: '已归档'
})
}
}
}
return tags;
},
/**
* 对话完成
* @param dialog
* @returns {*[]}
*/
dialogCompleted(dialog) {
return this.dialogTags(dialog).find(({color}) => color == 'success');
} }
}); });

View File

@ -345,7 +345,7 @@ export default {
msgAllUnread() { msgAllUnread() {
let num = 0; let num = 0;
this.cacheDialogs.map(({unread}) => { this.cacheDialogs.some(({unread}) => {
if (unread) { if (unread) {
num += unread; num += unread;
} }

View File

@ -95,6 +95,9 @@ export default {
list() { list() {
const {cacheTasks, taskCompleteTemps} = this; const {cacheTasks, taskCompleteTemps} = this;
const filterTask = (task, chackCompleted = true) => { const filterTask = (task, chackCompleted = true) => {
if (task.archived_at) {
return false;
}
if (task.complete_at && chackCompleted === true) { if (task.complete_at && chackCompleted === true) {
return false; return false;
} }

View File

@ -6,8 +6,11 @@
@dragover.prevent="chatDragOver(true, $event)" @dragover.prevent="chatDragOver(true, $event)"
@dragleave.prevent="chatDragOver(false, $event)"> @dragleave.prevent="chatDragOver(false, $event)">
<slot name="head"> <slot name="head">
<div class="dialog-title"> <div class="dialog-title" :class="{completed:$A.dialogCompleted(dialogData)}">
<div class="main-title"> <div class="main-title">
<template v-for="tag in $A.dialogTags(dialogData)" v-if="tag.color != 'success'">
<Tag :color="tag.color" :fade="false">{{$L(tag.text)}}</Tag>
</template>
<h2>{{dialogData.name}}</h2> <h2>{{dialogData.name}}</h2>
<em v-if="peopleNum > 0">({{peopleNum}})</em> <em v-if="peopleNum > 0">({{peopleNum}})</em>
</div> </div>
@ -86,6 +89,20 @@
<div v-if="dialogDrag" class="drag-over" @click="dialogDrag=false"> <div v-if="dialogDrag" class="drag-over" @click="dialogDrag=false">
<div class="drag-text">{{$L('拖动到这里发送')}}</div> <div class="drag-text">{{$L('拖动到这里发送')}}</div>
</div> </div>
<Modal
v-model="pasteShow"
:title="$L(pasteTitle)"
:cancel-text="$L('取消')"
:ok-text="$L('发送')"
@on-ok="pasteSend">
<div class="dialog-wrapper-paste">
<template v-for="item in pasteItem">
<img v-if="item.type == 'image'" :src="item.result"/>
<div v-else>{{$L('文件')}}: {{item.name}} ({{$A.bytesToSize(item.size)}})</div>
</template>
</div>
</Modal>
</div> </div>
</template> </template>
@ -122,6 +139,10 @@ export default {
tempMsgs: [], tempMsgs: [],
dialogMsgSubscribe: null, dialogMsgSubscribe: null,
pasteShow: false,
pasteFile: [],
pasteItem: [],
} }
}, },
@ -177,6 +198,18 @@ export default {
peopleNum() { peopleNum() {
return this.dialogData.type === 'group' ? $A.runNum(this.dialogData.people) : 0; return this.dialogData.type === 'group' ? $A.runNum(this.dialogData.people) : 0;
},
pasteTitle() {
const {pasteItem} = this;
let hasImage = pasteItem.find(({type}) => type == 'image')
let hasFile = pasteItem.find(({type}) => type != 'image')
if (hasImage && hasFile) {
return '发送文件/图片'
} else if (hasImage) {
return '发送图片'
}
return '发送文件'
} }
}, },
@ -264,12 +297,31 @@ export default {
const postFiles = Array.prototype.slice.call(files); const postFiles = Array.prototype.slice.call(files);
if (postFiles.length > 0) { if (postFiles.length > 0) {
e.preventDefault(); e.preventDefault();
postFiles.forEach((file) => { this.pasteFile = [];
this.$refs.chatUpload.upload(file); this.pasteItem = [];
postFiles.some(file => {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = ({target}) => {
this.pasteFile.push(file)
this.pasteItem.push({
type: $A.getMiddle(file.type, null, '/'),
name: file.name,
size: file.size,
result: target.result
})
this.pasteShow = true
}
}); });
} }
}, },
pasteSend() {
this.pasteFile.some(file => {
this.$refs.chatUpload.upload(file)
});
},
chatDragOver(show, e) { chatDragOver(show, e) {
let random = (this.__dialogDrag = $A.randomString(8)); let random = (this.__dialogDrag = $A.randomString(8));
if (!show) { if (!show) {
@ -329,7 +381,6 @@ export default {
sendSuccess(data) { sendSuccess(data) {
this.$store.dispatch("saveDialogMsg", data); this.$store.dispatch("saveDialogMsg", data);
this.$store.dispatch("increaseTaskMsgNum", this.dialogId); this.$store.dispatch("increaseTaskMsgNum", this.dialogId);
this.$store.dispatch("moveDialogTop", this.dialogId);
this.$store.dispatch("updateDialogLastMsg", data); this.$store.dispatch("updateDialogLastMsg", data);
this.onActive(); this.onActive();
}, },
@ -414,7 +465,7 @@ export default {
} }
}) })
} }
} },
} }
} }
</script> </script>

View File

@ -75,7 +75,7 @@ Vue.use(Minder)
const MDEditor = () => import('../../../components/MDEditor/index'); const MDEditor = () => import('../../../components/MDEditor/index');
const TEditor = () => import('../../../components/TEditor'); const TEditor = () => import('../../../components/TEditor');
const LuckySheet = () => import('../../../components/LuckySheet'); const LuckySheet = () => import('../../../components/LuckySheet');
const Flow = () => import('../../../components/flow'); const Flow = () => import('../../../components/Flow');
const OnlyOffice = () => import('../../../components/OnlyOffice'); const OnlyOffice = () => import('../../../components/OnlyOffice');
export default { export default {

View File

@ -51,7 +51,7 @@ Vue.use(Minder)
const MDPreview = () => import('../../../components/MDEditor/preview'); const MDPreview = () => import('../../../components/MDEditor/preview');
const TEditor = () => import('../../../components/TEditor'); const TEditor = () => import('../../../components/TEditor');
const LuckySheet = () => import('../../../components/LuckySheet'); const LuckySheet = () => import('../../../components/LuckySheet');
const Flow = () => import('../../../components/flow'); const Flow = () => import('../../../components/Flow');
const OnlyOffice = () => import('../../../components/OnlyOffice'); const OnlyOffice = () => import('../../../components/OnlyOffice');
export default { export default {

View File

@ -69,7 +69,8 @@
</li> </li>
</ul> </ul>
</div> </div>
<div v-if="projectData.desc" class="project-subtitle">{{projectData.desc}}</div> <div class="project-subbox">
<div class="project-subtitle">{{projectData.desc}}</div>
<div class="project-switch"> <div class="project-switch">
<div v-if="completedCount > 0" class="project-checkbox"> <div v-if="completedCount > 0" class="project-checkbox">
<Checkbox :value="projectParameter('completedTask')" @on-change="toggleCompleted">{{$L('显示已完成')}}</Checkbox> <Checkbox :value="projectParameter('completedTask')" @on-change="toggleCompleted">{{$L('显示已完成')}}</Checkbox>
@ -80,6 +81,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div v-if="projectParameter('card')" class="project-column"> <div v-if="projectParameter('card')" class="project-column">
<Draggable <Draggable
:list="columnList" :list="columnList"
@ -595,7 +597,10 @@ export default {
return a.id - b.id; return a.id - b.id;
}); });
list.forEach((column) => { list.forEach((column) => {
column.tasks = this.transforTasks(cacheTasks.filter((task) => { column.tasks = this.transforTasks(cacheTasks.filter(task => {
if (task.archived_at) {
return false;
}
return task.column_id == column.id; return task.column_id == column.id;
})).sort((a, b) => { })).sort((a, b) => {
if (a.sort != b.sort) { if (a.sort != b.sort) {
@ -655,7 +660,10 @@ export default {
unList() { unList() {
const {projectId, cacheTasks, searchText, sortField, sortType} = this; const {projectId, cacheTasks, searchText, sortField, sortType} = this;
const array = cacheTasks.filter((task) => { const array = cacheTasks.filter(task => {
if (task.archived_at) {
return false;
}
if (task.project_id != projectId || task.parent_id > 0) { if (task.project_id != projectId || task.parent_id > 0) {
return false; return false;
} }
@ -683,7 +691,10 @@ export default {
completedList() { completedList() {
const {projectId, cacheTasks, searchText} = this; const {projectId, cacheTasks, searchText} = this;
const array = cacheTasks.filter((task) => { const array = cacheTasks.filter(task => {
if (task.archived_at) {
return false;
}
if (task.project_id != projectId || task.parent_id > 0) { if (task.project_id != projectId || task.parent_id > 0) {
return false; return false;
} }
@ -703,7 +714,10 @@ export default {
completedCount() { completedCount() {
const {projectId, cacheTasks} = this; const {projectId, cacheTasks} = this;
return cacheTasks.filter((task) => { return cacheTasks.filter(task => {
if (task.archived_at) {
return false;
}
if (task.project_id != projectId || task.parent_id > 0) { if (task.project_id != projectId || task.parent_id > 0) {
return false; return false;
} }
@ -766,7 +780,12 @@ export default {
sort = -1; sort = -1;
upTask.push(...item.task.map(id => { upTask.push(...item.task.map(id => {
sort++; sort++;
upTask.push(...this.cacheTasks.filter(({parent_id}) => parent_id == id).map(({id}) => { upTask.push(...this.cacheTasks.filter(task => {
if (task.archived_at) {
return false;
}
return task.parent_id == id
}).map(({id}) => {
return { return {
id, id,
sort, sort,
@ -1161,6 +1180,9 @@ export default {
}, },
myFilter(task, chackCompleted = true) { myFilter(task, chackCompleted = true) {
if (task.archived_at) {
return false;
}
if (task.project_id != this.projectId) { if (task.project_id != this.projectId) {
return false; return false;
} }
@ -1178,6 +1200,9 @@ export default {
}, },
helpFilter(task, chackCompleted = true) { helpFilter(task, chackCompleted = true) {
if (task.archived_at) {
return false;
}
if (task.project_id != this.projectId || task.parent_id > 0) { if (task.project_id != this.projectId || task.parent_id > 0) {
return false; return false;
} }

View File

@ -30,7 +30,7 @@
</Timeline> </Timeline>
</div> </div>
</li> </li>
<li v-if="loadIng > 0" class="logs-loading"><Loading/></li> <li v-if="loadIng > 0 && showLoad" class="logs-loading"><Loading/></li>
<li v-else-if="hasMorePages" class="logs-more" @click="getMore">{{$L('加载更多')}}</li> <li v-else-if="hasMorePages" class="logs-more" @click="getMore">{{$L('加载更多')}}</li>
<li v-else-if="totalNum == 0" class="logs-none" @click="getLists(true)">{{$L('没有任何动态')}}</li> <li v-else-if="totalNum == 0" class="logs-none" @click="getLists(true)">{{$L('没有任何动态')}}</li>
</ul> </ul>
@ -52,6 +52,10 @@ export default {
type: Number, type: Number,
default: 0 default: 0
}, },
showLoad: {
type: Boolean,
default: true
},
}, },
data() { data() {
return { return {

View File

@ -74,7 +74,7 @@ export default {
}, },
computed: { computed: {
...mapState(['windowMax768']) ...mapState(['cacheTasks', 'windowMax768'])
}, },
watch: { watch: {
projectId: { projectId: {
@ -98,7 +98,13 @@ export default {
key: 'name', key: 'name',
minWidth: 200, minWidth: 200,
render: (h, {row}) => { render: (h, {row}) => {
return h('AutoTip', row.name); return h('AutoTip', {
on: {
'on-click': () => {
this.$store.dispatch("openTask", row);
}
}
}, row.name);
} }
}, },
{ {
@ -140,7 +146,27 @@ export default {
align: 'center', align: 'center',
width: 100, width: 100,
render: (h, params) => { render: (h, params) => {
const recoveryNode = h('Poptip', { if (this.cacheTasks.find(task => task.id == params.row.id && !task.archived_at)) {
return h('div', {
style: {
color: '#888',
},
}, this.$L('已还原'));
}
const vNodes = [
h('span', {
style: {
fontSize: '13px',
cursor: 'pointer',
color: '#8bcf70',
},
on: {
'click': () => {
this.$store.dispatch("openTask", params.row);
}
},
}, this.$L('查看')),
h('Poptip', {
props: { props: {
title: this.$L('你确定要还原归档吗?'), title: this.$L('你确定要还原归档吗?'),
confirm: true, confirm: true,
@ -148,6 +174,7 @@ export default {
placement: 'left', placement: 'left',
}, },
style: { style: {
marginLeft: '6px',
fontSize: '13px', fontSize: '13px',
cursor: 'pointer', cursor: 'pointer',
color: '#8bcf70', color: '#8bcf70',
@ -157,8 +184,8 @@ export default {
this.recovery(params.row); this.recovery(params.row);
} }
}, },
}, this.$L('还原')); }, this.$L('还原')),
const deleteNode = h('Poptip', { h('Poptip', {
props: { props: {
title: this.$L('你确定要删除任务吗?'), title: this.$L('你确定要删除任务吗?'),
confirm: true, confirm: true,
@ -176,15 +203,13 @@ export default {
this.delete(params.row); this.delete(params.row);
} }
}, },
}, this.$L('删除')); }, this.$L('删除'))
];
return h('TableAction', { return h('TableAction', {
props: { props: {
column: params.column column: params.column
} }
}, [ }, vNodes);
recoveryNode,
deleteNode,
]);
} }
} }
] ]
@ -239,12 +264,9 @@ export default {
recovery(row) { recovery(row) {
this.list = this.list.filter(({id}) => id != row.id); this.list = this.list.filter(({id}) => id != row.id);
this.loadIng++; this.loadIng++;
this.$store.dispatch("call", { this.$store.dispatch("archivedTask", {
url: 'project/task/archived',
data: {
task_id: row.id, task_id: row.id,
type: 'recovery' type: 'recovery'
},
}).then(({msg}) => { }).then(({msg}) => {
$A.messageSuccess(msg); $A.messageSuccess(msg);
this.loadIng--; this.loadIng--;

View File

@ -79,6 +79,9 @@
<div v-if="taskDetail.flow_item_name" class="flow"> <div v-if="taskDetail.flow_item_name" class="flow">
<span :class="taskDetail.flow_item_status" @click.stop="openMenu(taskDetail)">{{taskDetail.flow_item_name}}</span> <span :class="taskDetail.flow_item_status" @click.stop="openMenu(taskDetail)">{{taskDetail.flow_item_name}}</span>
</div> </div>
<div v-if="taskDetail.archived_at" class="flow">
<span class="archived" @click.stop="openMenu(taskDetail)">{{$L('已归档')}}</span>
</div>
<div class="nav"> <div class="nav">
<p v-if="projectName"><span>{{projectName}}</span></p> <p v-if="projectName"><span>{{projectName}}</span></p>
<p v-if="columnName"><span>{{columnName}}</span></p> <p v-if="columnName"><span>{{columnName}}</span></p>
@ -254,8 +257,10 @@
transfer> transfer>
<div class="picker-time"> <div class="picker-time">
<div @click="openTime" class="time">{{taskDetail.end_at ? cutTime : '--'}}</div> <div @click="openTime" class="time">{{taskDetail.end_at ? cutTime : '--'}}</div>
<Tag v-if="!taskDetail.complete_at && taskDetail.today" color="blue"><i class="taskfont">&#xe71d;</i>{{expiresFormat(taskDetail.end_at)}}</Tag> <template v-if="!taskDetail.complete_at">
<Tag v-if="!taskDetail.complete_at && taskDetail.overdue" color="red">{{$L('超期未完成')}}</Tag> <Tag v-if="within24Hours(taskDetail.end_at)" color="blue"><i class="taskfont">&#xe71d;</i>{{expiresFormat(taskDetail.end_at)}}</Tag>
<Tag v-if="taskDetail.overdue" color="red">{{$L('超期未完成')}}</Tag>
</template>
</div> </div>
</DatePicker> </DatePicker>
</li> </li>
@ -368,7 +373,7 @@
</div> </div>
</div> </div>
</div> </div>
<ProjectLog v-if="navActive=='log' && taskId > 0" ref="log" :task-id="taskDetail.id" @on-load-change="logLoadChange"/> <ProjectLog v-if="navActive=='log' && taskId > 0" ref="log" :task-id="taskDetail.id" :show-load="false" @on-load-change="logLoadChange"/>
<div v-else class="no-dialog"> <div v-else class="no-dialog">
<div class="no-tip">{{$L('暂无消息')}}</div> <div class="no-tip">{{$L('暂无消息')}}</div>
<div class="no-input"> <div class="no-input">
@ -565,8 +570,11 @@ export default {
if (!this.taskId) { if (!this.taskId) {
return []; return [];
} }
return this.cacheTasks.filter(({parent_id}) => { return this.cacheTasks.filter(task => {
return parent_id == this.taskId if (task.archived_at) {
return false;
}
return task.parent_id == this.taskId
}).sort((a, b) => { }).sort((a, b) => {
return a.id - b.id; return a.id - b.id;
}); });
@ -708,6 +716,10 @@ export default {
this.innerHeight = Math.min(1100, window.innerHeight); this.innerHeight = Math.min(1100, window.innerHeight);
}, },
within24Hours(date) {
return Math.round($A.Date(date).getTime() / 1000) - this.nowTime < 86400
},
expiresFormat(date) { expiresFormat(date) {
return $A.countDownFormat(date, this.nowTime) return $A.countDownFormat(date, this.nowTime)
}, },
@ -762,31 +774,6 @@ export default {
}) })
}, },
archivedOrRemoveTask(type) {
let typeDispatch = type == 'remove' ? 'removeTask' : 'archivedTask';
let typeName = type == 'remove' ? '删除' : '归档';
let typeTask = this.taskDetail.parent_id > 0 ? '子任务' : '任务';
$A.modalConfirm({
title: typeName + typeTask,
content: '你确定要' + typeName + typeTask + '【' + this.taskDetail.name + '】吗?',
loading: true,
onOk: () => {
if (this.taskDetail.loading === true) {
this.$Modal.remove();
return;
}
this.$set(this.taskDetail, 'loading', true);
this.$store.dispatch(typeDispatch, this.taskDetail.id).then(({msg}) => {
$A.messageSuccess(msg);
this.$Modal.remove();
}).catch(({msg}) => {
$A.modalError(msg, 301);
this.$Modal.remove();
});
}
});
},
openOwner() { openOwner() {
const list = this.getOwner.map(({userid}) => userid) const list = this.getOwner.map(({userid}) => userid)
this.$set(this.taskDetail, 'owner_userid', list) this.$set(this.taskDetail, 'owner_userid', list)

View File

@ -48,7 +48,7 @@
<template v-if="task.parent_id === 0"> <template v-if="task.parent_id === 0">
<EDropdownItem :divided="turns.length > 0" command="archived"> <EDropdownItem :divided="turns.length > 0" command="archived">
<div class="item"> <div class="item">
<Icon type="ios-filing" />{{$L('归档')}} <Icon type="ios-filing" />{{$L(task.archived_at ? '还原归档' : '归档')}}
</div> </div>
</EDropdownItem> </EDropdownItem>
<EDropdownItem command="remove"> <EDropdownItem command="remove">
@ -246,9 +246,21 @@ export default {
}, },
archivedOrRemoveTask(type) { archivedOrRemoveTask(type) {
let typeDispatch = type == 'remove' ? 'removeTask' : 'archivedTask'; let typeDispatch = 'removeTask';
let typeName = type == 'remove' ? '删除' : '归档'; let typeName = '删除';
let typeData = this.task.id;
let typeTask = this.task.parent_id > 0 ? '子任务' : '任务'; let typeTask = this.task.parent_id > 0 ? '子任务' : '任务';
if (type == 'archived') {
typeDispatch = 'archivedTask'
typeName = '归档'
if (this.task.archived_at) {
typeName = '还原归档'
typeData = {
task_id: this.task.id,
type: 'recovery'
}
}
}
$A.modalConfirm({ $A.modalConfirm({
title: typeName + typeTask, title: typeName + typeTask,
content: '你确定要' + typeName + typeTask + '【' + this.task.name + '】吗?', content: '你确定要' + typeName + typeTask + '【' + this.task.name + '】吗?',
@ -258,7 +270,7 @@ export default {
this.$Modal.remove(); this.$Modal.remove();
return; return;
} }
this.$store.dispatch(typeDispatch, this.task.id).then(({msg}) => { this.$store.dispatch(typeDispatch, typeData).then(({msg}) => {
$A.messageSuccess(msg); $A.messageSuccess(msg);
this.$Modal.remove(); this.$Modal.remove();
}).catch(({msg}) => { }).catch(({msg}) => {

View File

@ -163,8 +163,11 @@ export default {
subTask() { subTask() {
return function(task_id) { return function(task_id) {
return this.cacheTasks.filter(({parent_id}) => { return this.cacheTasks.filter(task => {
return parent_id == task_id if (task.archived_at) {
return false;
}
return task.parent_id == task_id
}).sort((a, b) => { }).sort((a, b) => {
return a.id - b.id; return a.id - b.id;
}); });

View File

@ -14,7 +14,7 @@
v-for="(item, key) in dialogType" v-for="(item, key) in dialogType"
:key="key" :key="key"
:class="{active:dialogActive==item.type}" :class="{active:dialogActive==item.type}"
@click="dialogActive=item.type"> @click="onActive(item.type)">
<Badge class="nav-num" :count="msgUnread(item.type)"/> <Badge class="nav-num" :count="msgUnread(item.type)"/>
{{$L(item.name)}} {{$L(item.name)}}
</p> </p>
@ -27,18 +27,22 @@
<ul v-if="tabActive==='dialog'" class="dialog"> <ul v-if="tabActive==='dialog'" class="dialog">
<li <li
v-for="(dialog, key) in dialogList" v-for="(dialog, key) in dialogList"
:ref="`dialog_${dialog.id}`"
:key="key" :key="key"
:class="{active: dialog.id == dialogId}" :class="{active: dialog.id == dialogId}"
@click="openDialog(dialog, true)"> @click="openDialog(dialog, true)">
<template v-if="dialog.type=='group'"> <template v-if="dialog.type=='group'">
<i v-if="dialog.group_type=='project'" class="taskfont icon-avatar project">&#xe6f9;</i> <i v-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=='task'" class="taskfont icon-avatar task" :class="{completed:$A.dialogCompleted(dialog)}">&#xe6f4;</i>
<Icon v-else class="icon-avatar" type="ios-people" /> <Icon v-else class="icon-avatar" type="ios-people" />
</template> </template>
<div v-else-if="dialog.dialog_user" class="user-avatar"><UserAvatar :userid="dialog.dialog_user.userid" :size="42"/></div> <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" /> <Icon v-else class="icon-avatar" type="md-person" />
<div class="dialog-box"> <div class="dialog-box">
<div class="dialog-title"> <div class="dialog-title">
<template v-for="tag in $A.dialogTags(dialog)" v-if="tag.color != 'success'">
<Tag :color="tag.color" :fade="false">{{$L(tag.text)}}</Tag>
</template>
<span>{{dialog.name}}</span> <span>{{dialog.name}}</span>
<Icon v-if="dialog.type == 'user' && lastMsgReadDone(dialog.last_msg)" :type="lastMsgReadDone(dialog.last_msg)"/> <Icon v-if="dialog.type == 'user' && lastMsgReadDone(dialog.last_msg)" :type="lastMsgReadDone(dialog.last_msg)"/>
<em v-if="dialog.last_at">{{$A.formatTime(dialog.last_at)}}</em> <em v-if="dialog.last_at">{{$A.formatTime(dialog.last_at)}}</em>
@ -123,22 +127,24 @@ export default {
dialogList() { dialogList() {
const {dialogActive, dialogKey} = this; const {dialogActive, dialogKey} = this;
if (dialogActive == '' && dialogKey == '') { if (dialogActive == '' && dialogKey == '') {
return this.cacheDialogs.filter(({name}) => name !== undefined); return this.cacheDialogs.filter(dialog => this.filterDialog(dialog)).sort((a, b) => {
return $A.Date(b.last_at) - $A.Date(a.last_at);
});
} }
return this.cacheDialogs.filter(({name, type, group_type, last_msg}) => { return this.cacheDialogs.filter(dialog => {
if (name === undefined) { if (!this.filterDialog(dialog)) {
return false; return false;
} }
if (dialogActive) { if (dialogActive) {
switch (dialogActive) { switch (dialogActive) {
case 'project': case 'project':
case 'task': case 'task':
if (group_type != dialogActive) { if (dialog.group_type != dialogActive) {
return false; return false;
} }
break; break;
case 'user': case 'user':
if (type != 'user') { if (dialog.type != 'user') {
return false; return false;
} }
break; break;
@ -147,20 +153,22 @@ export default {
} }
} }
if (dialogKey) { if (dialogKey) {
let existName = $A.strExists(name, dialogKey); let existName = $A.strExists(dialog.name, dialogKey);
let existMsg = last_msg && last_msg.type === 'text' && $A.strExists(last_msg.msg.text, dialogKey); let existMsg = dialog.last_msg && dialog.last_msg.type === 'text' && $A.strExists(dialog.last_msg.msg.text, dialogKey);
if (!existName && !existMsg) { if (!existName && !existMsg) {
return false; return false;
} }
} }
return true; return true;
}).sort((a, b) => {
return $A.Date(b.last_at) - $A.Date(a.last_at);
}) })
}, },
msgUnread() { msgUnread() {
return function (type) { return function (type) {
let num = 0; let num = 0;
this.cacheDialogs.map((dialog) => { this.cacheDialogs.some((dialog) => {
if (dialog.unread) { if (dialog.unread) {
switch (type) { switch (type) {
case 'project': case 'project':
@ -219,6 +227,24 @@ export default {
} }
}, },
onActive(type) {
if (this.dialogActive == type) {
//
const dialog = this.dialogList.find(({unread}) => unread > 0)
if (dialog) {
try {
this.$refs[`dialog_${dialog.id}`][0].scrollIntoView();
} catch (e) {
scrollIntoView(this.$refs[`dialog_${dialog.id}`][0], {
behavior: 'instant',
inline: 'end',
})
}
}
}
this.dialogActive = type
},
closeDialog() { closeDialog() {
this.dialogId = 0; this.dialogId = 0;
$A.setStorage("messenger::dialogId", 0) $A.setStorage("messenger::dialogId", 0)
@ -245,6 +271,44 @@ export default {
}); });
}, },
filterDialog(dialog) {
if (dialog.unread > 0 || dialog.id == this.dialogId) {
return true
}
if (dialog.name === undefined) {
return false;
}
if (!dialog.last_at) {
return false;
}
if (dialog.type == 'group') {
if (['project', 'task'].includes(dialog.group_type) && $A.isJson(dialog.group_info)) {
if (dialog.group_type == 'task' && dialog.group_info.complete_at) {
// 5
let time = Math.max($A.Date(dialog.last_at, true), $A.Date(dialog.group_info.complete_at, true))
if (5 * 86400 + time < $A.Time()) {
return false
}
}
if (dialog.group_info.deleted_at) {
// 2
let time = Math.max($A.Date(dialog.last_at, true), $A.Date(dialog.group_info.deleted_at, true))
if (2 * 86400 + time < $A.Time()) {
return false
}
}
if (dialog.group_info.archived_at) {
// 3
let time = Math.max($A.Date(dialog.last_at, true), $A.Date(dialog.group_info.archived_at, true))
if (3 * 86400 + time < $A.Time()) {
return false
}
}
}
}
return true;
},
getContactsList(page) { getContactsList(page) {
if (this.contactsData === null) { if (this.contactsData === null) {
this.contactsData = {}; this.contactsData = {};

View File

@ -65,7 +65,10 @@ export default {
return; return;
} }
this.loadIng++; this.loadIng++;
this.$store.dispatch("getTaskOne", task_id).then(({data}) => { this.$store.dispatch("getTaskOne", {
task_id,
archived: 'all'
}).then(({data}) => {
this.loadIng--; this.loadIng--;
this.taskInfo = data; this.taskInfo = data;
this.$store.dispatch("getTaskContent", task_id); this.$store.dispatch("getTaskContent", task_id);

View File

@ -538,6 +538,19 @@ export default {
} }
state.cacheProjects.push(data); state.cacheProjects.push(data);
} }
//
state.cacheDialogs.some(dialog => {
if (dialog.type == 'group' && dialog.group_type == 'project' && dialog.group_info.id == data.id) {
if (data.name !== undefined) {
dialog.name = data.name
}
for (let key in dialog.group_info) {
if (!dialog.group_info.hasOwnProperty(key) || data[key] === undefined) continue;
dialog.group_info[key] = data[key];
}
}
})
//
setTimeout(() => { setTimeout(() => {
$A.setStorage("cacheProjects", state.cacheProjects); $A.setStorage("cacheProjects", state.cacheProjects);
}) })
@ -896,6 +909,18 @@ export default {
dispatch("getTaskForParent", data.id).catch(() => {}) dispatch("getTaskForParent", data.id).catch(() => {})
} }
// //
state.cacheDialogs.some(dialog => {
if (dialog.type == 'group' && dialog.group_type == 'task' && dialog.group_info.id == data.id) {
if (data.name !== undefined) {
dialog.name = data.name
}
for (let key in dialog.group_info) {
if (!dialog.group_info.hasOwnProperty(key) || data[key] === undefined) continue;
dialog.group_info[key] = data[key];
}
}
})
//
setTimeout(() => { setTimeout(() => {
$A.setStorage("cacheTasks", state.cacheTasks); $A.setStorage("cacheTasks", state.cacheTasks);
}) })
@ -1010,20 +1035,21 @@ export default {
* 获取单个任务 * 获取单个任务
* @param state * @param state
* @param dispatch * @param dispatch
* @param task_id * @param data Number|JSONObject{task_id, ?archived_at}
* @returns {Promise<unknown>} * @returns {Promise<unknown>}
*/ */
getTaskOne({state, dispatch}, task_id) { getTaskOne({state, dispatch}, data) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if ($A.runNum(task_id) === 0) { if (/^\d+$/.test(data)) {
data = {task_id: data}
}
if ($A.runNum(data.task_id) === 0) {
reject({msg: 'Parameter error'}); reject({msg: 'Parameter error'});
return; return;
} }
dispatch("call", { dispatch("call", {
url: 'project/task/one', url: 'project/task/one',
data: { data,
task_id,
},
}).then(result => { }).then(result => {
dispatch("saveTask", result.data); dispatch("saveTask", result.data);
resolve(result) resolve(result)
@ -1125,7 +1151,10 @@ export default {
const newIds = state.cacheTasks.filter(task => task.parent_id == parent_id && task._time >= time).map(({id}) => id) const newIds = state.cacheTasks.filter(task => task.parent_id == parent_id && task._time >= time).map(({id}) => id)
dispatch("forgetTask", currentIds.filter(v => newIds.indexOf(v) == -1)) dispatch("forgetTask", currentIds.filter(v => newIds.indexOf(v) == -1))
} }
dispatch("getTasks", {parent_id}).then(() => { dispatch("getTasks", {
parent_id,
archived: 'all'
}).then(() => {
call() call()
resolve() resolve()
}).catch(() => { }).catch(() => {
@ -1168,32 +1197,33 @@ export default {
}, },
/** /**
* 归档任务 * 归档还原任务
* @param state * @param state
* @param dispatch * @param dispatch
* @param task_id * @param data Number|JSONObject{task_id, ?archived_at}
* @returns {Promise<unknown>} * @returns {Promise<unknown>}
*/ */
archivedTask({state, dispatch}, task_id) { archivedTask({state, dispatch}, data) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if ($A.runNum(task_id) === 0) { if (/^\d+$/.test(data)) {
data = {task_id: data}
}
if ($A.runNum(data.task_id) === 0) {
reject({msg: 'Parameter error'}); reject({msg: 'Parameter error'});
return; return;
} }
dispatch("taskLoadStart", task_id) dispatch("taskLoadStart", data.task_id)
dispatch("call", { dispatch("call", {
url: 'project/task/archived', url: 'project/task/archived',
data: { data,
task_id: task_id,
},
}).then(result => { }).then(result => {
dispatch("forgetTask", task_id) dispatch("saveTask", result.data)
dispatch("taskLoadEnd", task_id) dispatch("taskLoadEnd", data.task_id)
resolve(result) resolve(result)
}).catch(e => { }).catch(e => {
console.warn(e); console.warn(e);
dispatch("getTaskOne", task_id).catch(() => {}) dispatch("getTaskOne", data.task_id).catch(() => {})
dispatch("taskLoadEnd", task_id) dispatch("taskLoadEnd", data.task_id)
reject(e) reject(e)
}); });
}); });
@ -1292,7 +1322,10 @@ export default {
} }
state.taskId = task_id; state.taskId = task_id;
if (task_id > 0) { if (task_id > 0) {
dispatch("getTaskOne", task_id).then(() => { dispatch("getTaskOne", {
task_id,
archived: 'all'
}).then(() => {
dispatch("getTaskContent", task_id); dispatch("getTaskContent", task_id);
dispatch("getTaskFiles", task_id); dispatch("getTaskFiles", task_id);
dispatch("getTaskForParent", task_id).catch(() => {}) dispatch("getTaskForParent", task_id).catch(() => {})
@ -1749,22 +1782,6 @@ export default {
}); });
}, },
/**
* 将会话移动到首位
* @param state
* @param dialog_id
*/
moveDialogTop({state}, dialog_id) {
$A.execMainDispatch("moveDialogTop", dialog_id)
//
const index = state.cacheDialogs.findIndex(({id}) => id == dialog_id);
if (index > -1) {
const tmp = $A.cloneJSON(state.cacheDialogs[index]);
state.cacheDialogs.splice(index, 1);
state.cacheDialogs.unshift(tmp);
}
},
/** /**
* 忘记对话数据 * 忘记对话数据
* @param state * @param state
@ -2026,8 +2043,6 @@ export default {
if (dialog) { if (dialog) {
// 新增未读数 // 新增未读数
dialog.unread++; dialog.unread++;
// 移动到首位
dispatch("moveDialogTop", dialog_id);
} }
Store.set('dialogMsgPush', data); Store.set('dialogMsgPush', data);
} }
@ -2105,7 +2120,6 @@ export default {
case 'filedelete': case 'filedelete':
dispatch("forgetTaskFile", data.id) dispatch("forgetTaskFile", data.id)
break; break;
case 'archived':
case 'delete': case 'delete':
dispatch("forgetTask", data.id) dispatch("forgetTask", data.id)
break; break;

View File

@ -119,6 +119,9 @@ export default {
todayEnd = $A.Date($A.formatDate("Y-m-d 23:59:59")), todayEnd = $A.Date($A.formatDate("Y-m-d 23:59:59")),
todayNow = $A.Date($A.formatDate("Y-m-d H:i:s")); todayNow = $A.Date($A.formatDate("Y-m-d H:i:s"));
const filterTask = (task, chackCompleted = true) => { const filterTask = (task, chackCompleted = true) => {
if (task.archived_at) {
return false;
}
if (task.complete_at && chackCompleted === true) { if (task.complete_at && chackCompleted === true) {
return false; return false;
} }

View File

@ -15,7 +15,7 @@
padding: 0 30px; padding: 0 30px;
height: 68px; height: 68px;
position: relative; position: relative;
&:after { &:before {
content: ""; content: "";
position: absolute; position: absolute;
left: 0; left: 0;
@ -24,11 +24,41 @@
height: 1px; height: 1px;
background-color: #f4f5f5; background-color: #f4f5f5;
} }
&.completed {
&:after {
content: "\f373";
font-family: Ionicons, serif;
pointer-events: none;
position: absolute;
top: 50%;
right: 24px;
transform: translateY(-50%);
font-size: 52px;
color: #19be6b;
opacity: .2;
z-index: 1;
}
}
.main-title { .main-title {
display: flex; display: flex;
align-items: center; align-items: center;
line-height: 22px; line-height: 22px;
max-width: 100%; max-width: 100%;
.ivu-tag {
margin: 0 6px 0 0;
padding: 0 5px;
&.ivu-tag-success {
padding: 0 6px;
}
}
.ivu-icon {
font-size: 18px;
margin-right: 6px;
&.completed {
color: $primary-color;
}
}
> h2 { > h2 {
font-size: 17px; font-size: 17px;
font-weight: 600; font-weight: 600;
@ -411,6 +441,13 @@
} }
} }
.dialog-wrapper-paste {
img {
max-width: 100%;
max-height: 1000px;
}
}
@media (max-width: 768px) { @media (max-width: 768px) {
.dialog-wrapper { .dialog-wrapper {
.dialog-footer { .dialog-footer {

View File

@ -112,23 +112,26 @@
} }
} }
} }
.project-subtitle { .project-subbox {
width: 100%;
color: #999999;
line-height: 24px;
margin-top: -6px;
margin-bottom: -18px;
padding-right: 260px;
}
.project-switch {
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: flex-end; justify-content: space-between;
.project-subtitle {
flex: 1;
color: #999999;
line-height: 24px;
}
.project-switch {
margin-left: 80px;
flex-shrink: 0;
display: flex;
align-items: flex-end;
.project-checkbox { .project-checkbox {
display: flex; display: flex;
align-items: center; align-items: center;
margin-right: 14px; margin-right: 14px;
opacity: 0.9; opacity: 0.9;
height: 30px;
.ivu-checkbox-focus { .ivu-checkbox-focus {
box-shadow: none; box-shadow: none;
} }
@ -138,6 +141,7 @@
align-items: center; align-items: center;
background-color: #ffffff; background-color: #ffffff;
border-radius: 6px; border-radius: 6px;
height: 30px;
position: relative; position: relative;
transition: box-shadow 0.2s; transition: box-shadow 0.2s;
&:hover { &:hover {
@ -188,6 +192,7 @@
} }
} }
} }
}
.project-column { .project-column {
display: flex; display: flex;
height: 100%; height: 100%;
@ -934,10 +939,16 @@
justify-content: flex-end; justify-content: flex-end;
} }
} }
.project-subbox {
display: block;
.project-subtitle { .project-subtitle {
padding-right: 12px;
margin-bottom: 6px; margin-bottom: 6px;
} }
.project-switch {
margin-left: 0;
justify-content: flex-end;
}
}
} }
.project-column { .project-column {
> ul { > ul {

View File

@ -118,7 +118,7 @@
.log-text { .log-text {
display: inline-block; display: inline-block;
color: rgba(0, 0, 0, .8); color: rgba(0, 0, 0, .72);
.detail-user { .detail-user {
display: inline-block; display: inline-block;

View File

@ -27,6 +27,7 @@
background-color: #f4f5f5; background-color: #f4f5f5;
} }
.icon { .icon {
margin-right: 18px;
.task-menu-icon { .task-menu-icon {
display: flex; display: flex;
align-items: center; align-items: center;
@ -47,8 +48,7 @@
} }
} }
.flow { .flow {
margin-left: 18px; margin-right: 10px;
margin-right: -3px;
> span { > span {
font-size: 14px; font-size: 14px;
height: 26px; height: 26px;
@ -60,7 +60,8 @@
display: inline-block; display: inline-block;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
&.start { &.start,
&.archived {
background-color: rgba(38, 38, 38, 0.05); background-color: rgba(38, 38, 38, 0.05);
border-color: rgba(38, 38, 38, 0.05); border-color: rgba(38, 38, 38, 0.05);
color: #595959; color: #595959;
@ -81,7 +82,6 @@
flex: 1; flex: 1;
display: flex; display: flex;
align-items: center; align-items: center;
margin-left: 18px;
font-size: 12px; font-size: 12px;
width: 0; width: 0;
height: 40px; height: 40px;

View File

@ -111,6 +111,20 @@
background-color: #9B96DF; background-color: #9B96DF;
font-size: 24px; font-size: 24px;
} }
&.completed {
&:after {
content: "\f373";
font-family: Ionicons, serif;
pointer-events: none;
position: absolute;
bottom: 0;
right: 12px;
font-size: 32px;
color: #19be6b;
opacity: .2;
z-index: 1;
}
}
} }
.dialog-box { .dialog-box {
flex: 1; flex: 1;
@ -124,6 +138,13 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
line-height: 24px; line-height: 24px;
.ivu-tag {
margin: 0 4px 0 0;
padding: 0 5px;
&.ivu-tag-success {
padding: 0 6px;
}
}
> span { > span {
flex: 1; flex: 1;
color: #333333; color: #333333;
@ -138,6 +159,11 @@
transform: scale(0.9); transform: scale(0.9);
font-size: 12px; font-size: 12px;
color: $primary-color; color: $primary-color;
&.completed {
font-size: 18px;
margin: 0 4px 0 0;
transform: scale(1);
}
} }
> em { > em {
flex-shrink: 0; flex-shrink: 0;