支持查看已归档任务

This commit is contained in:
kuaifan 2022-01-23 19:23:18 +08:00
parent e6e58a03a6
commit 834dc9bec9
10 changed files with 165 additions and 105 deletions

View File

@ -334,7 +334,7 @@
} }
if (dialog.group_info.deleted_at) { if (dialog.group_info.deleted_at) {
tags.push({ tags.push({
color: 'error', color: 'red',
text: '已删除' text: '已删除'
}) })
} else if (dialog.group_info.archived_at) { } else if (dialog.group_info.archived_at) {
@ -346,6 +346,15 @@
} }
} }
return tags; return tags;
},
/**
* 对话完成
* @param dialog
* @returns {*[]}
*/
dialogCompleted(dialog) {
return this.dialogTags(dialog).find(({color}) => color == 'success');
} }
}); });

View File

@ -6,9 +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">
<Tag v-for="(tag, ti) in $A.dialogTags(dialogData)" :key="`tag_${ti}`" :color="tag.color">{{$L(tag.text)}}</Tag> <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>

View File

@ -140,51 +140,63 @@ export default {
align: 'center', align: 'center',
width: 100, width: 100,
render: (h, params) => { render: (h, params) => {
const recoveryNode = h('Poptip', { const vNodes = [
props: { h('span', {
title: this.$L('你确定要还原归档吗?'), style: {
confirm: true, fontSize: '13px',
transfer: true, cursor: 'pointer',
placement: 'left', color: '#8bcf70',
}, },
style: { on: {
fontSize: '13px', 'click': () => {
cursor: 'pointer', this.$store.dispatch("openTask", params.row);
color: '#8bcf70', }
}, },
on: { }, this.$L('查看')),
'on-ok': () => { h('Poptip', {
this.recovery(params.row); props: {
} title: this.$L('你确定要还原归档吗?'),
}, confirm: true,
}, this.$L('还原')); transfer: true,
const deleteNode = h('Poptip', { placement: 'left',
props: { },
title: this.$L('你确定要删除任务吗?'), style: {
confirm: true, marginLeft: '6px',
transfer: true, fontSize: '13px',
placement: 'left', cursor: 'pointer',
}, color: '#8bcf70',
style: { },
marginLeft: '6px', on: {
fontSize: '13px', 'on-ok': () => {
cursor: 'pointer', this.recovery(params.row);
color: '#f00', }
}, },
on: { }, this.$L('还原')),
'on-ok': () => { h('Poptip', {
this.delete(params.row); props: {
} title: this.$L('你确定要删除任务吗?'),
}, confirm: true,
}, this.$L('删除')); transfer: true,
placement: 'left',
},
style: {
marginLeft: '6px',
fontSize: '13px',
cursor: 'pointer',
color: '#f00',
},
on: {
'on-ok': () => {
this.delete(params.row);
}
},
}, this.$L('删除'))
];
return h('TableAction', { return h('TableAction', {
props: { props: {
column: params.column column: params.column
} }
}, [ }, vNodes);
recoveryNode,
deleteNode,
]);
} }
} }
] ]
@ -239,12 +251,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', task_id: row.id,
data: { type: 'recovery'
task_id: row.id,
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>
@ -762,31 +765,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

@ -33,14 +33,16 @@
@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">
<Tag v-for="(tag, ti) in $A.dialogTags(dialog)" :key="`tag_${ti}`" :color="tag.color">{{$L(tag.text)}}</Tag> <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>
@ -289,16 +291,16 @@ export default {
} }
} }
if (dialog.group_info.deleted_at) { if (dialog.group_info.deleted_at) {
// 3 // 2
let time = Math.max($A.Date(dialog.last_at, true), $A.Date(dialog.group_info.deleted_at, true)) let time = Math.max($A.Date(dialog.last_at, true), $A.Date(dialog.group_info.deleted_at, true))
if (3 * 86400 + time < $A.Time()) { if (2 * 86400 + time < $A.Time()) {
return false return false
} }
} }
if (dialog.group_info.archived_at) { if (dialog.group_info.archived_at) {
// 7 // 3
let time = Math.max($A.Date(dialog.last_at, true), $A.Date(dialog.group_info.archived_at, true)) let time = Math.max($A.Date(dialog.last_at, true), $A.Date(dialog.group_info.archived_at, true))
if (7 * 86400 + time < $A.Time()) { if (3 * 86400 + time < $A.Time()) {
return false return false
} }
} }

View File

@ -1197,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("saveTask", result.data) 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)
}); });
}); });

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;

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;
@ -125,11 +139,9 @@
justify-content: space-between; justify-content: space-between;
line-height: 24px; line-height: 24px;
.ivu-tag { .ivu-tag {
margin: 0 4px 0 0;
padding: 0 5px; padding: 0 5px;
&.ivu-tag-error, &.ivu-tag-success {
&.ivu-tag-primary,
&.ivu-tag-success,
&.ivu-tag-warning {
padding: 0 6px; padding: 0 6px;
} }
} }
@ -147,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;