mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-11 18:42:54 +00:00
feat: 添加任务复制功能
This commit is contained in:
parent
7c5a966944
commit
a03dec91c5
@ -2601,7 +2601,133 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/project/task/ai_generate 40. 使用 AI 助手生成任务
|
||||
* @api {post} api/project/task/copy 40. 复制任务
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup project
|
||||
* @apiName task__copy
|
||||
*
|
||||
* @apiParam {Number} task_id 任务ID
|
||||
* @apiParam {Number} project_id 目标项目ID
|
||||
* @apiParam {Number} column_id 目标列表ID
|
||||
* @apiParam {Number} flow_item_id 工作流id
|
||||
* @apiParam {Array} owner 负责人
|
||||
* @apiParam {Array} assist 协助人
|
||||
* @apiParam {String} [completed] 是否已完成(仅在没有工作流时生效)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function task__copy()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$task_id = intval(Request::input('task_id'));
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
$column_id = intval(Request::input('column_id'));
|
||||
$flow_item_id = intval(Request::input('flow_item_id'));
|
||||
$owner = Request::input('owner', []);
|
||||
$assist = Request::input('assist', []);
|
||||
$completed = Request::exists('completed') ? (bool)Request::input('completed') : null;
|
||||
//
|
||||
$task = ProjectTask::userTask($task_id);
|
||||
//
|
||||
$sourceProject = Project::userProject($task->project_id);
|
||||
ProjectPermission::userTaskPermission($sourceProject, ProjectPermission::TASK_MOVE, $task);
|
||||
//
|
||||
$project = Project::userProject($project_id);
|
||||
ProjectPermission::userTaskPermission($project, ProjectPermission::TASK_ADD);
|
||||
//
|
||||
$column = ProjectColumn::whereProjectId($project->id)->whereId($column_id)->first();
|
||||
if (empty($column)) {
|
||||
return Base::retError('列表不存在');
|
||||
}
|
||||
if (ProjectTask::whereProjectId($project->id)
|
||||
->whereNull('project_tasks.complete_at')
|
||||
->whereNull('project_tasks.archived_at')
|
||||
->count() > 2000) {
|
||||
return Base::retError('项目内未完成任务最多不能超过2000个');
|
||||
}
|
||||
if (ProjectTask::whereColumnId($column->id)
|
||||
->whereNull('project_tasks.complete_at')
|
||||
->whereNull('project_tasks.archived_at')
|
||||
->count() > 500) {
|
||||
return Base::retError('单个列表未完成任务最多不能超过500个');
|
||||
}
|
||||
$flowItem = null;
|
||||
if ($flow_item_id) {
|
||||
$flowItem = ProjectFlowItem::whereProjectId($project->id)->whereId($flow_item_id)->first();
|
||||
if (empty($flowItem)) {
|
||||
return Base::retError('任务状态不存在');
|
||||
}
|
||||
} else {
|
||||
if (ProjectFlowItem::whereProjectId($project->id)->count() > 0) {
|
||||
return Base::retError('请选择移动后状态', [], 102);
|
||||
}
|
||||
}
|
||||
//
|
||||
$projectUserIds = ProjectUser::whereProjectId($project->id)->pluck('userid')->toArray();
|
||||
$owner = array_values(array_filter(array_unique(array_map('intval', Arr::wrap($owner)))));
|
||||
$assist = array_values(array_filter(array_unique(array_map('intval', Arr::wrap($assist)))));
|
||||
$owner = array_values(array_intersect($owner, $projectUserIds));
|
||||
$assist = array_values(array_diff(array_intersect($assist, $projectUserIds), $owner));
|
||||
//
|
||||
$newTask = AbstractModel::transaction(function () use ($task, $project, $column, $flowItem, $owner, $assist, $completed) {
|
||||
/** @var ProjectTask $task */
|
||||
$copy = $task->copyTask();
|
||||
$copy->project_id = $project->id;
|
||||
$copy->column_id = $column->id;
|
||||
$copy->sort = intval(ProjectTask::whereColumnId($column->id)->orderByDesc('sort')->value('sort')) + 1;
|
||||
$copy->flow_item_id = 0;
|
||||
$copy->flow_item_name = '';
|
||||
$copy->save();
|
||||
$copy->load(['content', 'taskFile', 'taskTag', 'taskUser']);
|
||||
if ($copy->content) {
|
||||
$copy->content->project_id = $project->id;
|
||||
$copy->content->save();
|
||||
}
|
||||
foreach ($copy->taskFile as $taskFile) {
|
||||
$taskFile->project_id = $project->id;
|
||||
$taskFile->save();
|
||||
}
|
||||
foreach ($copy->taskTag as $taskTag) {
|
||||
$taskTag->project_id = $project->id;
|
||||
$taskTag->save();
|
||||
}
|
||||
ProjectTaskUser::whereTaskId($copy->id)->delete();
|
||||
$copy->setRelation('taskUser', collect());
|
||||
$copy->setRelation('project', $project);
|
||||
$updateData = [
|
||||
'task_id' => $copy->id,
|
||||
'owner' => $owner,
|
||||
];
|
||||
if ($copy->parent_id === 0) {
|
||||
$updateData['assist'] = $assist;
|
||||
}
|
||||
if ($flowItem) {
|
||||
$updateData['flow_item_id'] = $flowItem->id;
|
||||
} elseif ($completed !== null) {
|
||||
$updateData['complete_at'] = $completed ? Carbon::now()->toDateTimeString() : false;
|
||||
}
|
||||
$updateMarking = [];
|
||||
$copy->updateTask($updateData, $updateMarking);
|
||||
$copy->addLog('复制{任务}', [
|
||||
'copy_from' => $task->id,
|
||||
]);
|
||||
return $copy;
|
||||
});
|
||||
//
|
||||
$data = ProjectTask::oneTask($newTask->id)->toArray();
|
||||
$data['column_name'] = $column->name;
|
||||
$data['project_name'] = $project->name;
|
||||
//
|
||||
return Base::retSuccess('复制成功', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/project/task/ai_generate 41. 使用 AI 助手生成任务
|
||||
*
|
||||
* @apiDescription 需要token身份,使用AI根据用户输入和上下文信息生成任务标题和详细描述
|
||||
* @apiVersion 1.0.0
|
||||
|
||||
@ -1143,9 +1143,14 @@ class ProjectTask extends AbstractModel
|
||||
*/
|
||||
public function copyTask()
|
||||
{
|
||||
return AbstractModel::transaction(function() {
|
||||
// 复制任务
|
||||
$task = $this->replicate();
|
||||
$source = $this->fresh(['content', 'taskFile', 'taskUser']);
|
||||
if (!$source) {
|
||||
throw new ApiException('任务不存在');
|
||||
}
|
||||
|
||||
return AbstractModel::transaction(function () use ($source) {
|
||||
// 复制任务(使用最新数据,避免复制临时字段)
|
||||
$task = $source->replicate();
|
||||
$task->dialog_id = 0;
|
||||
$task->archived_at = null;
|
||||
$task->archived_userid = 0;
|
||||
@ -1154,21 +1159,21 @@ class ProjectTask extends AbstractModel
|
||||
$task->created_at = Carbon::now();
|
||||
$task->save();
|
||||
// 复制任务内容
|
||||
if ($this->content) {
|
||||
$tmp = $this->content->replicate();
|
||||
if ($source->content) {
|
||||
$tmp = $source->content->replicate();
|
||||
$tmp->task_id = $task->id;
|
||||
$tmp->created_at = Carbon::now();
|
||||
$tmp->save();
|
||||
}
|
||||
// 复制任务附件
|
||||
foreach ($this->taskFile as $taskFile) {
|
||||
foreach ($source->taskFile as $taskFile) {
|
||||
$tmp = $taskFile->replicate();
|
||||
$tmp->task_id = $task->id;
|
||||
$tmp->created_at = Carbon::now();
|
||||
$tmp->save();
|
||||
}
|
||||
// 复制任务成员
|
||||
foreach ($this->taskUser as $taskUser) {
|
||||
foreach ($source->taskUser as $taskUser) {
|
||||
$tmp = $taskUser->replicate();
|
||||
$tmp->task_id = $task->id;
|
||||
$tmp->task_pid = $task->id;
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
<div class="task-move-content">
|
||||
<div class="task-move-content-old">
|
||||
<div class="task-move-title">{{ $L('移动前') }}</div>
|
||||
<div class="task-move-title">{{ beforeTitle }}</div>
|
||||
<div class="task-move-row">
|
||||
<span class="label">{{$L('状态')}}:</span>
|
||||
<div class="flow">
|
||||
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-move-content-new">
|
||||
<div class="task-move-title">{{ $L('移动后') }}</div>
|
||||
<div class="task-move-title">{{ afterTitle }}</div>
|
||||
<div class="task-move-row">
|
||||
<span class="label">{{$L('状态')}}:</span>
|
||||
<TaskMenu
|
||||
@ -91,7 +91,7 @@
|
||||
<div class="ivu-modal-footer">
|
||||
<div class="adaption">
|
||||
<Button type="default" @click="close">{{$L('取消')}}</Button>
|
||||
<Button type="primary" :loading="loadIng > 0" @click="onConfirm">{{$L('确定')}}</Button>
|
||||
<Button type="primary" :loading="loadIng > 0" @click="onConfirm">{{confirmText}}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -117,6 +117,11 @@ export default {
|
||||
type: Object,
|
||||
default: false
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "move",
|
||||
validator: value => ["move", "copy"].includes(value)
|
||||
},
|
||||
|
||||
},
|
||||
data() {
|
||||
@ -148,6 +153,18 @@ export default {
|
||||
|
||||
computed: {
|
||||
...mapState(['cacheProjects', 'cacheColumns']),
|
||||
isCopy() {
|
||||
return this.type === "copy";
|
||||
},
|
||||
beforeTitle() {
|
||||
return this.$L(this.isCopy ? '复制前' : '移动前');
|
||||
},
|
||||
afterTitle() {
|
||||
return this.$L(this.isCopy ? '复制后' : '移动后');
|
||||
},
|
||||
confirmText() {
|
||||
return this.$L(this.isCopy ? '复制' : '确定');
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@ -251,8 +268,8 @@ export default {
|
||||
},
|
||||
|
||||
async onConfirm() {
|
||||
if (this.task.project_id == this.cascader[0] && this.task.column_id == this.cascader[1]) {
|
||||
$A.messageError("未变更移动项");
|
||||
if (!this.isCopy && this.task.project_id == this.cascader[0] && this.task.column_id == this.cascader[1]) {
|
||||
$A.messageError(this.$L('未变更移动项'));
|
||||
return;
|
||||
}
|
||||
this.loadIng++;
|
||||
@ -269,7 +286,7 @@ export default {
|
||||
callData.completed = this.updateData.flow.complete_at ? 1 : 0;
|
||||
}
|
||||
this.$store.dispatch("call", {
|
||||
url: "project/task/move",
|
||||
url: this.isCopy ? "project/task/copy" : "project/task/move",
|
||||
data: callData
|
||||
}).then(({data, msg}) => {
|
||||
this.loadIng--;
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
|
||||
<template v-if="task.parent_id === 0">
|
||||
<template v-if="operationShow">
|
||||
<EDropdownItem command="favorite" :divided="turns.length > 0">
|
||||
<EDropdownItem command="favorite" divided>
|
||||
<div class="item" :class="{favorited: isFavorited}">
|
||||
<i class="taskfont movefont"></i>{{$L(isFavorited ? '取消收藏' : '收藏')}}
|
||||
</div>
|
||||
@ -67,6 +67,11 @@
|
||||
<i class="taskfont movefont"></i>{{$L('移动')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<EDropdownItem command="copy">
|
||||
<div class="item">
|
||||
<Icon type="ios-copy" />{{$L('复制')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<EDropdownItem command="remove">
|
||||
<div class="item hover-del">
|
||||
<Icon type="md-trash" />{{$L('删除')}}
|
||||
@ -104,6 +109,19 @@
|
||||
<TaskMove ref="addTask" v-model="moveTaskShow" :task="task"/>
|
||||
</Modal>
|
||||
|
||||
<!--复制任务-->
|
||||
<Modal
|
||||
v-model="copyTaskShow"
|
||||
:title="$L('复制任务')"
|
||||
:mask-closable="false"
|
||||
:styles="{
|
||||
width: '90%',
|
||||
maxWidth: '540px'
|
||||
}"
|
||||
footer-hide>
|
||||
<TaskMove v-model="copyTaskShow" :task="task" type="copy"/>
|
||||
</Modal>
|
||||
|
||||
<!-- 发送任务 -->
|
||||
<Forwarder
|
||||
ref="forwarder"
|
||||
@ -145,6 +163,7 @@ export default {
|
||||
styles: {},
|
||||
|
||||
moveTaskShow: false,
|
||||
copyTaskShow: false,
|
||||
isFavorited: false,
|
||||
}
|
||||
},
|
||||
@ -331,6 +350,9 @@ export default {
|
||||
case 'move':
|
||||
this.moveTaskShow = true;
|
||||
break;
|
||||
case 'copy':
|
||||
this.copyTaskShow = true;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user