diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index fa3b9d170..44f126793 100755 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -39,6 +39,7 @@ use App\Module\BillMultipleExport; use Illuminate\Support\Facades\DB; use App\Models\ProjectTaskFlowChange; use App\Models\ProjectTaskVisibilityUser; +use App\Models\ProjectTaskTemplate; /** * @apiDefine project @@ -2693,4 +2694,170 @@ class ProjectController extends AbstractController $projectPermission = ProjectPermission::updatePermissions($projectId, Base::newArrayRecursive('intval', $permissions)); return Base::retSuccess("success", $projectPermission); } + + /** + * @api {get} api/project/task/template_list 47. 任务模板列表 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup project + * @apiName task__template_list + * + * @apiParam {Number} project_id 项目ID + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function task__template_list() + { + $user = User::auth(); + $projectId = intval(Request::input('project_id')); + if (!$projectId) { + return Base::retError('缺少参数project_id'); + } + $project = Project::userProject($projectId); + if (!$project) { + return Base::retError('项目不存在或已被删除'); + } + $templates = ProjectTaskTemplate::where('project_id', $projectId) + ->orderBy('sort') + ->orderByDesc('id') + ->get(); + return Base::retSuccess('success', $templates); + } + + /** + * @api {post} api/project/task/template_save 48. 保存任务模板 + * + * @apiDescription 需要token身份(限:项目负责人) + * @apiVersion 1.0.0 + * @apiGroup project + * @apiName task__template_save + * + * @apiParam {Number} project_id 项目ID + * @apiParam {Number} [id] 模板ID + * @apiParam {String} name 模板名称 + * @apiParam {String} title 任务标题 + * @apiParam {String} content 任务内容 + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function task__template_save() + { + $user = User::auth(); + // + $projectId = intval(Request::input('project_id')); + if (!$projectId) { + return Base::retError('缺少参数project_id'); + } + Project::userProject($projectId, true, true); + // + $id = intval(Request::input('id', 0)); + $name = trim(Request::input('name', '')); + $title = trim(Request::input('title', '')); + $content = trim(Request::input('content', '')); + if (empty($name)) { + return Base::retError('请输入模板名称'); + } + if (empty($title)) { + return Base::retError('请输入任务标题'); + } + $data = [ + 'project_id' => $projectId, + 'name' => $name, + 'title' => $title, + 'content' => $content, + 'userid' => $user->userid + ]; + if ($id > 0) { + $template = ProjectTaskTemplate::where('id', $id) + ->where('project_id', $projectId) + ->first(); + if (!$template) { + return Base::retError('模板不存在或已被删除'); + } + $template->update($data); + } else { + $templateCount = ProjectTaskTemplate::where('project_id', $projectId)->count(); + if ($templateCount >= 20) { + return Base::retError('每个项目最多添加10个模板'); + } + $data['sort'] = ProjectTaskTemplate::where('project_id', $projectId)->max('sort') + 1; + $template = ProjectTaskTemplate::create($data); + } + return Base::retSuccess('保存成功', $template); + } + + /** + * @api {get} api/project/task/template_delete 49. 删除任务模板 + * + * @apiDescription 需要token身份(限:项目负责人) + * @apiVersion 1.0.0 + * @apiGroup project + * @apiName task__template_delete + * + * @apiParam {Number} id 模板ID + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function task__template_delete() + { + User::auth(); + // + $id = intval(Request::input('id')); + if (!$id) { + return Base::retError('缺少参数id'); + } + $template = ProjectTaskTemplate::find($id); + if (!$template) { + return Base::retError('模板不存在或已被删除'); + } + Project::userProject($template->project_id, true, true); + $template->delete(); + return Base::retSuccess('删除成功'); + } + + /** + * @api {get} api/project/task/template_default 50. 设置任务模板为默认 + * + * @apiDescription 需要token身份(限:项目负责人) + * @apiVersion 1.0.0 + * @apiGroup project + * @apiName task__template_default + * + * @apiParam {Number} id 模板ID + * @apiParam {Number} project_id 项目ID + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function task__template_default() + { + User::auth(); + // + $id = intval(Request::input('id')); + $projectId = intval(Request::input('project_id')); + if (!$id || !$projectId) { + return Base::retError('参数错误'); + } + Project::userProject($projectId, true, true); + // + $template = ProjectTaskTemplate::where('id', $id) + ->where('project_id', $projectId) + ->first(); + if (!$template) { + return Base::retError('模板不存在或已被删除'); + } + // 先将所有模板设为非默认 + ProjectTaskTemplate::where('project_id', $projectId)->update(['is_default' => false]); + // 设置当前模板为默认 + $template->update(['is_default' => true]); + return Base::retSuccess('设置成功'); + } } diff --git a/app/Models/ProjectTaskTemplate.php b/app/Models/ProjectTaskTemplate.php new file mode 100644 index 000000000..6e4e119ac --- /dev/null +++ b/app/Models/ProjectTaskTemplate.php @@ -0,0 +1,77 @@ +belongsTo(Project::class); + } + + /** + * 关联创建者 + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo(User::class, 'userid'); + } +} diff --git a/database/migrations/2024_12_01_115600_create_project_task_templates_table.php b/database/migrations/2024_12_01_115600_create_project_task_templates_table.php new file mode 100644 index 000000000..07aad707e --- /dev/null +++ b/database/migrations/2024_12_01_115600_create_project_task_templates_table.php @@ -0,0 +1,44 @@ +id(); + $table->unsignedBigInteger('project_id')->index()->comment('项目ID'); + $table->string('name', 100)->comment('模板名称'); + $table->string('title', 255)->nullable()->comment('任务标题'); + $table->text('content')->nullable()->comment('任务内容'); + $table->unsignedTinyInteger('sort')->default(0)->comment('排序'); + $table->boolean('is_default')->default(false)->comment('是否默认模板'); + $table->unsignedBigInteger('userid')->index()->comment('创建人'); + $table->timestamps(); + + // 外键约束 + $table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade'); + $table->foreign('userid')->references('userid')->on('users'); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('project_task_templates'); + } +} diff --git a/resources/assets/js/pages/manage/components/ProjectPanel.vue b/resources/assets/js/pages/manage/components/ProjectPanel.vue index b4256e50b..2209adf24 100644 --- a/resources/assets/js/pages/manage/components/ProjectPanel.vue +++ b/resources/assets/js/pages/manage/components/ProjectPanel.vue @@ -53,6 +53,7 @@ {{$L('项目设置')}} {{$L('权限设置')}} + {{$L('任务模板')}} {{$L('工作流设置')}} {{$L('成员管理')}} {{$L('邀请链接')}} @@ -452,6 +453,14 @@ + + + + + +
+
+
{{$L('任务模板')}}
+
+ +
+
+ +
+
+
{{$L('暂无任务模板')}}
+
+
+
+
+ {{ item.name }} + {{$L('默认')}} +
+
+
{{ item.title }}
+
+ +
+
+
+ + + +
+
+
+
+ + + +
+ + + + + + + + + +
+
+ + +
+
+
+ + + diff --git a/resources/assets/sass/pages/components/_.scss b/resources/assets/sass/pages/components/_.scss index 5de0c1572..a502c5fb2 100755 --- a/resources/assets/sass/pages/components/_.scss +++ b/resources/assets/sass/pages/components/_.scss @@ -15,6 +15,7 @@ @import "project-panel"; @import "project-workflow"; @import "project-permission"; +@import "project-task-template"; @import "task-add"; @import "task-add-simple"; @import "task-archived"; diff --git a/resources/assets/sass/pages/components/project-task-template.scss b/resources/assets/sass/pages/components/project-task-template.scss new file mode 100644 index 000000000..e2c6937e7 --- /dev/null +++ b/resources/assets/sass/pages/components/project-task-template.scss @@ -0,0 +1,100 @@ +.project-task-template { + height: 100%; + display: flex; + flex-direction: column; + + .header { + padding: 20px 20px 10px; + display: flex; + align-items: center; + justify-content: space-between; + + .title { + color: $primary-title-color; + font-size: 20px; + font-weight: 500; + line-height: 1; + padding-right: 24px; + } + } + + .content { + flex: 1; + padding: 10px 20px 20px; + overflow-y: auto; + position: relative; + + .empty { + text-align: center; + padding: 40px 0; + color: $primary-text-color; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + + .empty-text { + font-size: 14px; + } + } + } + + .template-list { + .template-item { + margin-bottom: 16px; + padding: 16px; + border: 1px solid #F4F4F5; + border-radius: 4px; + + &:hover { + border-color: #84C56A; + } + + .template-title { + font-weight: 500; + margin-bottom: 8px; + display: flex; + align-items: center; + color: $primary-title-color; + + .default-tag { + font-weight: normal; + margin-left: 8px; + font-size: 12px; + padding: 2px 8px; + border-radius: 3px; + background: #84C56A; + } + } + + .template-content { + color: $primary-text-color; + font-size: 13px; + + .task-title { + margin-bottom: 4px; + } + + .task-content { + color: $primary-desc-color; + } + } + + .template-actions { + margin-top: 12px; + text-align: right; + + > button { + margin-right: 12px; + height: 28px; + padding: 0 12px; + font-size: 13px; + + > i { + margin: 0 -2px; + } + } + } + } + } +}