From dbdb8052691f533ba36f195e09fbbdd1b7439e69 Mon Sep 17 00:00:00 2001 From: Pang Date: Mon, 22 Apr 2024 09:13:55 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=E6=9F=A5=E7=9C=8B?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E6=8F=8F=E8=BF=B0=E4=BF=AE=E6=94=B9=E5=8E=86?= =?UTF-8?q?=E5=8F=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Api/ProjectController.php | 46 ++++- app/Models/ProjectTask.php | 18 +- app/Models/ProjectTaskContent.php | 1 - ...64219_add_project_task_contents_userid.php | 38 ++++ resources/assets/js/components/TEditor.vue | 3 + .../assets/js/components/TEditorTask.vue | 24 ++- .../js/pages/manage/components/ProjectLog.vue | 10 + .../manage/components/TaskContentHistory.vue | 190 ++++++++++++++++++ .../js/pages/manage/components/TaskDetail.vue | 25 ++- .../assets/js/pages/single/taskContent.vue | 87 ++++++++ resources/assets/js/routes.js | 11 +- 11 files changed, 442 insertions(+), 11 deletions(-) create mode 100644 database/migrations/2024_04_22_064219_add_project_task_contents_userid.php create mode 100644 resources/assets/js/pages/manage/components/TaskContentHistory.vue create mode 100644 resources/assets/js/pages/single/taskContent.vue diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index 4c17846da..245d38821 100755 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -32,6 +32,7 @@ use App\Models\ProjectTaskUser; use App\Models\WebSocketDialog; use App\Exceptions\ApiException; use App\Models\ProjectPermission; +use App\Models\ProjectTaskContent; use App\Models\WebSocketDialogMsg; use App\Module\BillMultipleExport; use Illuminate\Support\Facades\DB; @@ -1576,7 +1577,8 @@ class ProjectController extends AbstractController * @apiGroup project * @apiName task__content * - * @apiParam {Number} task_id 任务ID + * @apiParam {Number} task_id 任务ID + * @apiParam {Number} [history_id] 历史ID(获取历史版本) * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) @@ -1587,15 +1589,57 @@ class ProjectController extends AbstractController User::auth(); // $task_id = intval(Request::input('task_id')); + $history_id = intval(Request::input('history_id')); // $task = ProjectTask::userTask($task_id, null); // + if ($history_id > 0) { + $taskContent = ProjectTaskContent::whereTaskId($task->id)->whereId($history_id)->first(); + if (empty($taskContent)) { + return Base::retError('历史版本不存在'); + } + return Base::retSuccess('success', array_merge($taskContent->getContentInfo(), [ + 'name' => $task->name, + ])); + } if (empty($task->content)) { return Base::retSuccess('success', json_decode('{}')); } return Base::retSuccess('success', $task->content->getContentInfo()); } + /** + * @api {get} api/project/task/content_history 25. 获取任务详细历史描述 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup project + * @apiName task__content_history + * + * @apiParam {Number} task_id 任务ID + * + * @apiParam {Number} [page] 当前页,默认:1 + * @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:100 + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function task__content_history() + { + User::auth(); + // + $task_id = intval(Request::input('task_id')); + // + $task = ProjectTask::userTask($task_id, null); + // + $data = ProjectTaskContent::select(['id', 'task_id', 'desc', 'userid', 'created_at']) + ->whereTaskId($task->id) + ->orderByDesc('id') + ->paginate(Base::getPaginate(100, 20)); + return Base::retSuccess('success', $data); + } + /** * @api {get} api/project/task/files 26. 获取任务文件列表 * diff --git a/app/Models/ProjectTask.php b/app/Models/ProjectTask.php index 4c0c29306..1f9aeba25 100644 --- a/app/Models/ProjectTask.php +++ b/app/Models/ProjectTask.php @@ -393,7 +393,7 @@ class ProjectTask extends AbstractModel $p_color = $data['p_color']; $top = intval($data['top']); $userid = User::userid(); - $visibility = isset($data['visibility_appoint']) ? $data['visibility_appoint'] : $data['visibility']; + $visibility = $data['visibility_appoint'] ?? $data['visibility']; $visibility_userids = $data['visibility_appointor'] ?: []; // if (ProjectTask::whereProjectId($project_id) @@ -527,6 +527,8 @@ class ProjectTask extends AbstractModel ProjectTaskContent::createInstance([ 'project_id' => $task->project_id, 'task_id' => $task->id, + 'userid' => $task->userid, + 'desc' => $task->desc, 'content' => [ 'url' => ProjectTaskContent::saveContent($task->id, $content) ], @@ -913,15 +915,25 @@ class ProjectTask extends AbstractModel } // 内容 if (Arr::exists($data, 'content')) { + $logRecord = []; + $logContent = ProjectTaskContent::whereTaskId($this->id)->orderByDesc('id')->first(); + if ($logContent) { + $logRecord['link'] = [ + 'title' => '查看历史', + 'url' => 'single/task/content/' . $this->id . '?history_id=' . $logContent->id, + ]; + } + $this->desc = self::generateDesc($data['content']); ProjectTaskContent::createInstance([ 'project_id' => $this->project_id, 'task_id' => $this->id, + 'userid' => User::userid(), + 'desc' => $this->desc, 'content' => [ 'url' => ProjectTaskContent::saveContent($this->id, $data['content']) ], ])->save(); - $this->desc = self::generateDesc($data['content']); - $this->addLog("修改{任务}详细描述"); + $this->addLog("修改{任务}详细描述", $logRecord); $updateMarking['is_update_content'] = true; } // 优先级 diff --git a/app/Models/ProjectTaskContent.php b/app/Models/ProjectTaskContent.php index 63c34e17d..977d73835 100644 --- a/app/Models/ProjectTaskContent.php +++ b/app/Models/ProjectTaskContent.php @@ -34,7 +34,6 @@ use App\Exceptions\ApiException; class ProjectTaskContent extends AbstractModel { protected $hidden = [ - 'created_at', 'updated_at', ]; diff --git a/database/migrations/2024_04_22_064219_add_project_task_contents_userid.php b/database/migrations/2024_04_22_064219_add_project_task_contents_userid.php new file mode 100644 index 000000000..a7489d951 --- /dev/null +++ b/database/migrations/2024_04_22_064219_add_project_task_contents_userid.php @@ -0,0 +1,38 @@ +string('desc', 500)->nullable()->default('')->after('task_id')->comment('内容描述'); + $table->bigInteger('userid')->nullable()->default(0)->after('task_id')->comment('用户ID'); + } + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::table('project_task_contents', function (Blueprint $table) { + $table->dropColumn("desc"); + $table->dropColumn("userid"); + }); + } +} diff --git a/resources/assets/js/components/TEditor.vue b/resources/assets/js/components/TEditor.vue index 5debbd32c..7ac5d6f63 100755 --- a/resources/assets/js/components/TEditor.vue +++ b/resources/assets/js/components/TEditor.vue @@ -354,6 +354,7 @@ export default { } }); editor.ui.registry.addMenuItem('imagePreview', { + icon: 'preview', text: this.$L('预览图片'), onAction: () => { this.operateImg = null @@ -394,6 +395,7 @@ export default { } }); editor.ui.registry.addMenuItem('screenload', { + icon: 'fullscreen', text: this.$L('退出全屏'), onAction: () => { this.closeFull(); @@ -418,6 +420,7 @@ export default { } }); editor.ui.registry.addMenuItem('screenload', { + icon: 'fullscreen', text: this.$L('全屏'), onAction: () => { this.onFull(); diff --git a/resources/assets/js/components/TEditorTask.vue b/resources/assets/js/components/TEditorTask.vue index aef094fac..04e9fdfa3 100755 --- a/resources/assets/js/components/TEditorTask.vue +++ b/resources/assets/js/components/TEditorTask.vue @@ -23,6 +23,7 @@
{{ $L('编辑描述') }} + {{ $L('历史记录') }} {{ $L('打开链接') }} {{ $L('查看图片') }} @@ -81,7 +82,7 @@ export default { autoresize_bottom_margin: 2, min_height: 200, max_height: 380, - contextmenu: 'checklist | bold italic underline forecolor backcolor | link | uploadImages imagePreview | screenload', + contextmenu: 'checklist | bold italic underline forecolor backcolor | link | uploadImages imagePreview | history screenload', valid_elements: 'a[href|title|target=_blank],em,strong/b,div[align],span[style],a,br,p,img[src|alt|witdh|height],pre[class],code,ol[class],ul[class],li[class]', extended_valid_elements: 'a[href|title|target=_blank]', toolbar: false @@ -89,9 +90,13 @@ export default { optionFull: { menubar: 'file edit view', removed_menuitems: 'preview,print', + contextmenu: 'checklist | bold italic underline forecolor backcolor | link | uploadImages imagePreview | screenload', valid_elements: 'a[href|title|target=_blank],em,strong/b,div[align],span[style],a,br,p,img[src|alt|witdh|height],pre[class],code,ol[class],ul[class],li[class]', extended_valid_elements: 'a[href|title|target=_blank]', - toolbar: 'uploadImages | checklist | bold italic underline | forecolor backcolor' + toolbar: 'uploadImages | checklist | bold italic underline | forecolor backcolor', + mobile: { + menubar: 'file edit view', + }, }, operateStyles: {}, @@ -147,12 +152,17 @@ export default { this.$refs.desc.onFull() }, + onHistory() { + this.$emit('on-history'); + }, + onBlur() { this.$emit('on-blur'); }, onEditorInit(editor) { this.updateTouchContent(); + this.updateHistoryContent(editor); this.$emit('on-editor-init', editor); }, @@ -237,6 +247,16 @@ export default { }, timeout) }, + updateHistoryContent(editor) { + editor.ui.registry.addMenuItem('history', { + icon: 'insert-time', + text: this.$L('历史记录'), + onAction: () => { + this.onHistory(); + } + }); + }, + onLinkPreview() { if (this.operateLink) { window.open(this.operateLink); diff --git a/resources/assets/js/pages/manage/components/ProjectLog.vue b/resources/assets/js/pages/manage/components/ProjectLog.vue index 4ee92d59a..2d4765d1b 100644 --- a/resources/assets/js/pages/manage/components/ProjectLog.vue +++ b/resources/assets/js/pages/manage/components/ProjectLog.vue @@ -180,6 +180,16 @@ export default { vNode.push(h('span', {class:'change-value'}, now || '-')) } } + if ($A.isJson(record.link)) { + let {title, url} = record.link + vNode.push(h('span', ': ')) + vNode.push(h('a', { + attrs: { + href: $A.baseUrl(url), + target: '_blank' + } + }, this.$L(title))) + } if (record.userid) { let userids = $A.isArray(record.userid) ? record.userid : [record.userid] let userNode = []; diff --git a/resources/assets/js/pages/manage/components/TaskContentHistory.vue b/resources/assets/js/pages/manage/components/TaskContentHistory.vue new file mode 100644 index 000000000..680ea187b --- /dev/null +++ b/resources/assets/js/pages/manage/components/TaskContentHistory.vue @@ -0,0 +1,190 @@ + + + + diff --git a/resources/assets/js/pages/manage/components/TaskDetail.vue b/resources/assets/js/pages/manage/components/TaskDetail.vue index ef8d359f8..b8b78811f 100755 --- a/resources/assets/js/pages/manage/components/TaskDetail.vue +++ b/resources/assets/js/pages/manage/components/TaskDetail.vue @@ -140,6 +140,7 @@ class="desc" :value="taskContent" :placeholder="$L('详细描述...')" + @on-history="onHistory" @on-blur="updateBlur('content')"/>
@@ -496,6 +497,20 @@ + + + +
+ +
+
@@ -511,10 +526,12 @@ import ChatInput from "./ChatInput"; import UserSelect from "../../../components/UserSelect.vue"; import TaskExistTips from "./TaskExistTips.vue"; import TEditorTask from "../../../components/TEditorTask.vue"; +import TaskContentHistory from "./TaskContentHistory.vue"; export default { name: "TaskDetail", components: { + TaskContentHistory, TEditorTask, UserSelect, TaskExistTips, @@ -621,7 +638,9 @@ export default { remark: [ { required: true, message: this.$L('请输入备注'), trigger: 'blur' }, ], - } + }, + + historyShow: false, } }, @@ -993,6 +1012,10 @@ export default { return isModify; }, + onHistory() { + this.historyShow = true; + }, + updateBlur(action, params) { if (this.canUpdateBlur) { this.updateData(action, params) diff --git a/resources/assets/js/pages/single/taskContent.vue b/resources/assets/js/pages/single/taskContent.vue new file mode 100644 index 000000000..5c9388a56 --- /dev/null +++ b/resources/assets/js/pages/single/taskContent.vue @@ -0,0 +1,87 @@ + + + diff --git a/resources/assets/js/routes.js b/resources/assets/js/routes.js index b1e54fb24..a79406319 100755 --- a/resources/assets/js/routes.js +++ b/resources/assets/js/routes.js @@ -143,6 +143,11 @@ export default [ path: '/single/file/:codeOrFileId', component: () => import('./pages/single/file.vue'), }, + { + name: 'single-task-content', + path: '/single/task/content/:taskId', + component: () => import('./pages/single/taskContent.vue'), + }, { name: 'single-task', path: '/single/task/:taskId', @@ -154,17 +159,17 @@ export default [ component: () => import('./pages/single/apps.vue') }, { - name: 'valid-email', + name: 'single-valid-email', path: '/single/valid/email', component: () => import('./pages/single/validEmail.vue') }, { - name: 'report-edit', + name: 'single-report-edit', path: '/single/report/edit/:reportEditId', component: () => import('./pages/single/reportEdit.vue') }, { - name: 'report-detail', + name: 'single-report-detail', path: '/single/report/detail/:reportDetailId', component: () => import('./pages/single/reportDetail.vue') },