pref: 优化任务日志的显示

This commit is contained in:
kuaifan 2022-01-13 00:29:31 +08:00
parent 1df55b78c2
commit 87cdee4fe8
8 changed files with 166 additions and 46 deletions

View File

@ -310,7 +310,9 @@ class ProjectController extends AbstractController
$project = Project::userProject($project_id, true, true); $project = Project::userProject($project_id, true, true);
// //
if ($project->name != $name) { if ($project->name != $name) {
$project->addLog("修改项目名称:{$project->name} => {$name}"); $project->addLog("修改项目名称", [
'change' => [$project->name, $name]
]);
$project->name = $name; $project->name = $name;
} }
if ($project->desc != $desc) { if ($project->desc != $desc) {
@ -495,7 +497,7 @@ class ProjectController extends AbstractController
$projectInvite->save(); $projectInvite->save();
// //
$projectInvite->project->syncDialogUser(); $projectInvite->project->syncDialogUser();
$projectInvite->project->addLog("会员ID" . $user->userid . " 通过邀请链接加入项目"); $projectInvite->project->addLog("通过邀请链接加入项目");
// //
$data = $projectInvite->toArray(); $data = $projectInvite->toArray();
$data['already'] = true; $data['already'] = true;
@ -539,7 +541,7 @@ class ProjectController extends AbstractController
'owner' => 1, 'owner' => 1,
]); ]);
$project->syncDialogUser(); $project->syncDialogUser();
$project->addLog("移交项目给会员ID" . $owner_userid); $project->addLog("移交项目给", ['userid' => $owner_userid]);
}); });
// //
$project->pushMsg('detail'); $project->pushMsg('detail');
@ -634,7 +636,7 @@ class ProjectController extends AbstractController
$row = ProjectUser::whereProjectId($project->id)->whereUserid($user->userid)->first(); $row = ProjectUser::whereProjectId($project->id)->whereUserid($user->userid)->first();
$row?->exitProject(); $row?->exitProject();
$project->syncDialogUser(); $project->syncDialogUser();
$project->addLog("会员ID" . $user->userid . " 退出项目"); $project->addLog("退出项目");
$project->pushMsg('delete', null, $user->userid); $project->pushMsg('delete', null, $user->userid);
}); });
return Base::retSuccess('退出成功', ['id' => $project->id]); return Base::retSuccess('退出成功', ['id' => $project->id]);
@ -1429,23 +1431,27 @@ class ProjectController extends AbstractController
if (empty($projectLog) || empty($projectLog->task_id)) { if (empty($projectLog) || empty($projectLog->task_id)) {
return Base::retError('记录不存在'); return Base::retError('记录不存在');
} }
$record = $projectLog->record;
// //
$task = ProjectTask::userTask($projectLog->task_id, null, true); $task = ProjectTask::userTask($projectLog->task_id, null, true);
// //
if ($record['type'] == 'flow') { $record = $projectLog->record;
$newFlowItem = ProjectFlowItem::find(intval($record['flow_item_id'])); if ($record['flow'] && is_array($record['flow'])) {
$rawData = $record['flow'];
$newFlowItem = ProjectFlowItem::find(intval($rawData['flow_item_id']));
if (empty($newFlowItem)) { if (empty($newFlowItem)) {
return Base::retError('流程不存在或已被删除'); return Base::retError('流程不存在或已被删除');
} }
return AbstractModel::transaction(function() use ($record, $task, $newFlowItem) { return AbstractModel::transaction(function() use ($rawData, $task, $newFlowItem) {
$data = array_intersect_key($record, array_flip(['complete_at', 'owner', 'assist']));
$currentFlowItem = $task->flow_item_id ? ProjectFlowItem::find($task->flow_item_id) : null; $currentFlowItem = $task->flow_item_id ? ProjectFlowItem::find($task->flow_item_id) : null;
// 更新任务 //
$task->flow_item_id = $newFlowItem->id; $task->flow_item_id = $newFlowItem->id;
$task->flow_item_name = $newFlowItem->name; $task->flow_item_name = $newFlowItem->name;
$task->addLog("重置{任务}状态", [
'change' => [$currentFlowItem?->name, $newFlowItem->name]
]);
//
$updateMarking = []; $updateMarking = [];
$task->addLog("重置{任务}状态:{$currentFlowItem?->name} => {$newFlowItem->name}"); $data = array_intersect_key($rawData, array_flip(['complete_at', 'owner', 'assist']));
$task->updateTask($data, $updateMarking); $task->updateTask($data, $updateMarking);
// //
$data = ProjectTask::oneTask($task->id)->toArray(); $data = ProjectTask::oneTask($task->id)->toArray();

View File

@ -285,18 +285,23 @@ class Project extends AbstractModel
/** /**
* 添加项目日志 * 添加项目日志
* @param string $detail * @param string $detail
* @param array $record
* @param int $userid * @param int $userid
* @return ProjectLog * @return ProjectLog
*/ */
public function addLog($detail, $userid = 0) public function addLog($detail, $record = [], $userid = 0)
{ {
$log = ProjectLog::createInstance([ $array = [
'project_id' => $this->id, 'project_id' => $this->id,
'column_id' => 0, 'column_id' => 0,
'task_id' => 0, 'task_id' => 0,
'userid' => $userid ?: User::userid(), 'userid' => $userid ?: User::userid(),
'detail' => $detail, 'detail' => $detail,
]); ];
if ($record) {
$array['record'] = $record;
}
$log = ProjectLog::createInstance($array);
$log->save(); $log->save();
return $log; return $log;
} }

View File

@ -472,7 +472,7 @@ class ProjectTask extends AbstractModel
self::addTask($subtask); self::addTask($subtask);
} }
} }
$task->addLog("创建{任务}" . $task->name); $task->addLog("创建{任务}");
return $task; return $task;
}); });
} }
@ -495,8 +495,7 @@ class ProjectTask extends AbstractModel
if ($this->flow_item_id == $data['flow_item_id']) { if ($this->flow_item_id == $data['flow_item_id']) {
throw new ApiException('任务状态未发生改变'); throw new ApiException('任务状态未发生改变');
} }
$recordData = [ $flowData = [
'type' => 'flow',
'flow_item_id' => $this->flow_item_id, 'flow_item_id' => $this->flow_item_id,
'flow_item_name' => $this->flow_item_name, 'flow_item_name' => $this->flow_item_name,
]; ];
@ -526,23 +525,23 @@ class ProjectTask extends AbstractModel
if ($newFlowItem->status == 'end') { if ($newFlowItem->status == 'end') {
// 判断自动完成 // 判断自动完成
if (!$this->complete_at) { if (!$this->complete_at) {
$recordData['complete_at'] = $this->complete_at; $flowData['complete_at'] = $this->complete_at;
$data['complete_at'] = date("Y-m-d H:i"); $data['complete_at'] = date("Y-m-d H:i");
} }
} else { } else {
// 判断自动打开 // 判断自动打开
if ($this->complete_at) { if ($this->complete_at) {
$recordData['complete_at'] = $this->complete_at; $flowData['complete_at'] = $this->complete_at;
$data['complete_at'] = false; $data['complete_at'] = false;
} }
} }
if ($newFlowItem->userids) { if ($newFlowItem->userids) {
// 判断自动添加负责人 // 判断自动添加负责人
$recordData['owner'] = $data['owner'] = $this->taskUser->where('owner', 1)->pluck('userid')->toArray(); $flowData['owner'] = $data['owner'] = $this->taskUser->where('owner', 1)->pluck('userid')->toArray();
if ($newFlowItem->usertype == "replace") { if ($newFlowItem->usertype == "replace") {
// 流转模式 // 流转模式
if ($this->parent_id === 0) { if ($this->parent_id === 0) {
$recordData['assist'] = $data['assist'] = $this->taskUser->where('owner', 0)->pluck('userid')->toArray(); $flowData['assist'] = $data['assist'] = $this->taskUser->where('owner', 0)->pluck('userid')->toArray();
$data['assist'] = array_merge($data['assist'], $data['owner']); $data['assist'] = array_merge($data['assist'], $data['owner']);
} }
$data['owner'] = $newFlowItem->userids; $data['owner'] = $newFlowItem->userids;
@ -557,7 +556,10 @@ class ProjectTask extends AbstractModel
} }
$this->flow_item_id = $newFlowItem->id; $this->flow_item_id = $newFlowItem->id;
$this->flow_item_name = $newFlowItem->status . "|" . $newFlowItem->name; $this->flow_item_name = $newFlowItem->status . "|" . $newFlowItem->name;
$this->addLog("修改{任务}状态:{$currentFlowItem?->name} => {$newFlowItem->name}", 0, $recordData); $this->addLog("修改{任务}状态", [
'flow' => $flowData,
'change' => [$currentFlowItem?->name, $newFlowItem->name]
]);
} }
// 状态 // 状态
if (Arr::exists($data, 'complete_at')) { if (Arr::exists($data, 'complete_at')) {
@ -584,7 +586,9 @@ class ProjectTask extends AbstractModel
} elseif (mb_strlen($data['name']) > 255) { } elseif (mb_strlen($data['name']) > 255) {
throw new ApiException('任务描述最多只能设置255个字'); throw new ApiException('任务描述最多只能设置255个字');
} }
$this->addLog("修改{任务}标题:{$this->name} => {$data['name']}"); $this->addLog("修改{任务}标题", [
'change' => [$this->name, $data['name']]
]);
$this->name = $data['name']; $this->name = $data['name'];
} }
// 负责人 // 负责人
@ -616,12 +620,12 @@ class ProjectTask extends AbstractModel
if ($count == 0 && count($array) == 1 && $array[0] == User::userid()) { if ($count == 0 && count($array) == 1 && $array[0] == User::userid()) {
$this->addLog("认领{任务}"); $this->addLog("认领{任务}");
} else { } else {
$this->addLog("修改{任务}负责人" . implode(",", $array)); $this->addLog("修改{任务}负责人", ['userid' => $array]);
} }
} }
$rows = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->whereNotIn('userid', $array)->get(); $rows = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->whereNotIn('userid', $array)->get();
if ($rows->isNotEmpty()) { if ($rows->isNotEmpty()) {
$this->addLog("删除{任务}负责人" . $rows->implode('userid', ',')); $this->addLog("删除{任务}负责人", ['userid' => $rows->implode('userid', ',')]);
foreach ($rows as $row) { foreach ($rows as $row) {
$row->delete(); $row->delete();
} }
@ -719,11 +723,11 @@ class ProjectTask extends AbstractModel
$array[] = $uid; $array[] = $uid;
} }
if ($array) { if ($array) {
$this->addLog("修改{任务}协助人员" . implode(",", $array)); $this->addLog("修改{任务}协助人员", ['userid' => $array]);
} }
$rows = ProjectTaskUser::whereTaskId($this->id)->whereOwner(0)->whereNotIn('userid', $array)->get(); $rows = ProjectTaskUser::whereTaskId($this->id)->whereOwner(0)->whereNotIn('userid', $array)->get();
if ($rows->isNotEmpty()) { if ($rows->isNotEmpty()) {
$this->addLog("删除{任务}协助人员" . $rows->implode('userid', ',')); $this->addLog("删除{任务}协助人员", ['userid' => $rows->implode('userid', ',')]);
foreach ($rows as $row) { foreach ($rows as $row) {
$row->delete(); $row->delete();
} }
@ -732,7 +736,9 @@ class ProjectTask extends AbstractModel
} }
// 背景色 // 背景色
if (Arr::exists($data, 'color') && $this->color != $data['color']) { if (Arr::exists($data, 'color') && $this->color != $data['color']) {
$this->addLog("修改{任务}背景色:{$this->color} => {$data['color']}"); $this->addLog("修改{任务}背景色", [
'change' => [$this->color, $data['color']]
]);
$this->color = $data['color']; $this->color = $data['color'];
} }
// 列表 // 列表
@ -742,7 +748,9 @@ class ProjectTask extends AbstractModel
if (empty($column)) { if (empty($column)) {
throw new ApiException('请选择正确的列表'); throw new ApiException('请选择正确的列表');
} }
$this->addLog("修改{任务}列表:{$oldName} => {$column->name}"); $this->addLog("修改{任务}列表", [
'change' => [$oldName, $column->name]
]);
$this->column_id = $column->id; $this->column_id = $column->id;
} }
// 内容 // 内容
@ -892,7 +900,7 @@ class ProjectTask extends AbstractModel
if ($complete_at === null) { if ($complete_at === null) {
// 标记未完成 // 标记未完成
$this->complete_at = null; $this->complete_at = null;
$this->addLog("{任务}标记未完成" . $this->name); $this->addLog("{任务}标记未完成");
} else { } else {
// 标记已完成 // 标记已完成
if ($this->parent_id == 0) { if ($this->parent_id == 0) {
@ -904,7 +912,7 @@ class ProjectTask extends AbstractModel
throw new ApiException('请先领取任务'); throw new ApiException('请先领取任务');
} }
$this->complete_at = $complete_at; $this->complete_at = $complete_at;
$this->addLog("{任务}标记已完成" . $this->name); $this->addLog("{任务}标记已完成");
} }
$this->save(); $this->save();
}); });
@ -924,21 +932,21 @@ class ProjectTask extends AbstractModel
$this->archived_at = null; $this->archived_at = null;
$this->archived_userid = User::userid(); $this->archived_userid = User::userid();
$this->archived_follow = 0; $this->archived_follow = 0;
$this->addLog("任务取消归档" . $this->name); $this->addLog("任务取消归档");
$this->pushMsg('add', ProjectTask::oneTask($this->id)); $this->pushMsg('add', ProjectTask::oneTask($this->id));
} else { } else {
// 归档任务 // 归档任务
if ($isAuto === true) { if ($isAuto === true) {
$logText = "自动任务归档" . $this->name; $logText = "自动任务归档";
$userid = 0; $userid = 0;
} else { } else {
$logText = "任务归档" . $this->name; $logText = "任务归档";
$userid = User::userid(); $userid = User::userid();
} }
$this->archived_at = $archived_at; $this->archived_at = $archived_at;
$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('archived');
} }
self::whereParentId($this->id)->update([ self::whereParentId($this->id)->update([
@ -964,7 +972,7 @@ class ProjectTask extends AbstractModel
$dialog?->deleteDialog(); $dialog?->deleteDialog();
} }
self::whereParentId($this->id)->delete(); self::whereParentId($this->id)->delete();
$this->addLog("删除{任务}" . $this->name); $this->addLog("删除{任务}");
$this->delete(); $this->delete();
}); });
if ($pushMsg) { if ($pushMsg) {
@ -976,13 +984,13 @@ class ProjectTask extends AbstractModel
/** /**
* 添加任务日志 * 添加任务日志
* @param string $detail * @param string $detail
* @param array $record
* @param int $userid * @param int $userid
* @param $record
* @return ProjectLog * @return ProjectLog
*/ */
public function addLog($detail, $userid = 0, $record = null) public function addLog($detail, $record = [], $userid = 0)
{ {
$detail = str_replace("{任务}", $this->parent_id > 0 ? "子任务" : "任务", $detail); $detail = str_replace("{任务}", $this->parent_id ? "子任务" : "任务", $detail);
$array = [ $array = [
'project_id' => $this->project_id, 'project_id' => $this->project_id,
'column_id' => $this->column_id, 'column_id' => $this->column_id,
@ -990,6 +998,9 @@ class ProjectTask extends AbstractModel
'userid' => $userid ?: User::userid(), 'userid' => $userid ?: User::userid(),
'detail' => $detail, 'detail' => $detail,
]; ];
if ($this->parent_id) {
$record['subtitle'] = $this->name;
}
if ($record) { if ($record) {
$array['record'] = $record; $array['record'] = $record;
} }

View File

@ -13,7 +13,7 @@
</div> </div>
<div class="log-summary"> <div class="log-summary">
<span class="log-creator">{{item.user ? item.user.nickname : $L('系统')}}</span> <span class="log-creator">{{item.user ? item.user.nickname : $L('系统')}}</span>
<span class="log-text">{{$L(item.detail)}}</span> <ProjectLogDetail class="log-text" :render="logDetail" :item="item"/>
<span v-if="operationList(item).length > 0" class="log-operation"> <span v-if="operationList(item).length > 0" class="log-operation">
<Button v-for="(op, oi) in operationList(item)" :key="oi" size="small" @click="onOperation(op)">{{op.button}}</Button> <Button v-for="(op, oi) in operationList(item)" :key="oi" size="small" @click="onOperation(op)">{{op.button}}</Button>
</span> </span>
@ -30,8 +30,12 @@
</template> </template>
<script> <script>
import {VNode} from "vue";
import ProjectLogDetail from "./ProjectLogDetail";
export default { export default {
name: "ProjectLog", name: "ProjectLog",
components: {ProjectLogDetail},
props: { props: {
projectId: { projectId: {
type: Number, type: Number,
@ -130,21 +134,77 @@ export default {
this.getLists(); this.getLists();
}, },
/**
* 日志详情
* @param h
* @param detail
* @param record
* @returns {*[]}
*/
logDetail(h, {detail, record}) {
let vNode = [h('span', this.$L(detail))];
if ($A.isJson(record)) {
if ($A.isArray(record.change)) {
let [before, now] = record.change
vNode.push(h('span', ': '))
if (before) {
vNode.push(h('span', `${before} => ${now}`))
} else {
vNode.push(h('span', now))
}
}
if (record.userid) {
let userids = $A.isArray(record.userid) ? record.userid : [record.userid]
let userNode = [];
userids.some(userid => {
userNode.push(h('UserAvatar', {
props: {
size: 18,
userid
}
}))
})
if (userNode.length > 0) {
vNode.push(h('div', {
class: 'detail-user'
}, [
h('div', {
class: 'detail-user-wrap'
}, userNode)
]))
}
}
}
return h('span', {
class: 'log-text'
}, vNode)
},
/**
* 可操作事件
* @param id
* @param record
* @returns {*[]}
*/
operationList({id, record}) { operationList({id, record}) {
let list = []; let list = [];
if (!$A.isJson(record)) { if (!$A.isJson(record)) {
return list return list
} }
if (this.taskId > 0 && record.type === 'flow') { if (this.taskId > 0 && $A.isJson(record.flow)) {
list.push({ list.push({
id, id,
button: '重置', button: '重置',
content: `确定重置为【${record.flow_item_name}】吗?`, content: `确定重置为【${$A.getMiddle(record.flow.flow_item_name, "|")}】吗?`,
}) })
} }
return list; return list;
}, },
/**
* 执行操作
* @param item
*/
onOperation(item) { onOperation(item) {
$A.modalConfirm({ $A.modalConfirm({
content: item.content, content: item.content,

View File

@ -0,0 +1,11 @@
export default {
name: 'ProjectLogDetail',
functional: true,
props: {
render: Function,
item: Object,
},
render: (h, ctx) => {
return ctx.props.render(h, ctx.props.item);
}
};

View File

@ -2,7 +2,11 @@
<!--子任务--> <!--子任务-->
<li v-if="ready && taskDetail.parent_id > 0"> <li v-if="ready && taskDetail.parent_id > 0">
<div class="subtask-icon"> <div class="subtask-icon">
<TaskMenu :ref="`taskMenu_${taskDetail.id}`" :task="taskDetail" :load-status="taskDetail.loading === true"/> <TaskMenu
:ref="`taskMenu_${taskDetail.id}`"
:task="taskDetail"
:load-status="taskDetail.loading === true"
@on-update="getLogLists"/>
</div> </div>
<div v-if="taskDetail.flow_item_name" class="subtask-flow"> <div v-if="taskDetail.flow_item_name" class="subtask-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>
@ -65,7 +69,13 @@
<div v-else-if="ready" v-show="taskDetail.id > 0" :class="{'task-detail':true, 'open-dialog': hasOpenDialog, 'completed': taskDetail.complete_at}"> <div v-else-if="ready" v-show="taskDetail.id > 0" :class="{'task-detail':true, 'open-dialog': hasOpenDialog, 'completed': taskDetail.complete_at}">
<div class="task-info"> <div class="task-info">
<div class="head"> <div class="head">
<TaskMenu :ref="`taskMenu_${taskDetail.id}`" :task="taskDetail" class="icon" size="medium" :color-show="false"/> <TaskMenu
:ref="`taskMenu_${taskDetail.id}`"
:task="taskDetail"
class="icon"
size="medium"
:color-show="false"
@on-update="getLogLists"/>
<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>
@ -105,7 +115,13 @@
<i class="taskfont open" @click="openNewWin">&#xe776;</i> <i class="taskfont open" @click="openNewWin">&#xe776;</i>
</ETooltip> </ETooltip>
<div class="menu"> <div class="menu">
<TaskMenu :task="taskDetail" icon="ios-more" completed-icon="ios-more" size="medium" :color-show="false"/> <TaskMenu
:task="taskDetail"
icon="ios-more"
completed-icon="ios-more"
size="medium"
:color-show="false"
@on-update="getLogLists"/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -233,9 +233,10 @@ export default {
// //
this.$store.dispatch("taskUpdate", Object.assign(updata, { this.$store.dispatch("taskUpdate", Object.assign(updata, {
task_id: this.task.id, task_id: this.task.id,
})).then(({msg}) => { })).then(({data, msg}) => {
$A.messageSuccess(msg); $A.messageSuccess(msg);
resolve() resolve()
this.$emit("on-update", data)
}).catch(({msg}) => { }).catch(({msg}) => {
$A.modalError(msg); $A.modalError(msg);
this.$store.dispatch("getTaskOne", this.task.id); this.$store.dispatch("getTaskOne", this.task.id);

View File

@ -86,7 +86,17 @@
color:rgba(0, 0, 0, 0.85) color:rgba(0, 0, 0, 0.85)
} }
.log-text { .log-text {
display: inline-block;
color: rgba(0,0,0,.54); color: rgba(0,0,0,.54);
.detail-user {
display: inline-block;
.detail-user-wrap {
display: flex;
.common-avatar {
margin: 0 2px;
}
}
}
} }
.log-operation { .log-operation {
> button { > button {