From 311aa5948224d20eccf58297c60f3ccfcac0044e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E5=AF=92?= <2596194220@qq.com> Date: Sun, 11 Jan 2026 01:04:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(desktop):=20=E2=9C=A8=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=B5=B7=E6=8A=A5=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db.sqlite | Bin 135168 -> 135168 bytes package.json | 1 + pnpm-lock.yaml | 39 +++ src/renderer/components.d.ts | 2 + src/renderer/src/pages/reflection/index.vue | 143 ++++++++++ .../pages/reflection/views/poster/index.vue | 247 ++++++++++++++++++ src/renderer/src/pages/task/detail.vue | 145 ---------- src/renderer/src/pages/task/index.vue | 16 +- src/renderer/src/router/index.ts | 12 +- src/rpc/router/task.router.ts | 12 + 10 files changed, 467 insertions(+), 150 deletions(-) create mode 100644 src/renderer/src/pages/reflection/index.vue create mode 100644 src/renderer/src/pages/reflection/views/poster/index.vue delete mode 100644 src/renderer/src/pages/task/detail.vue diff --git a/db.sqlite b/db.sqlite index 8e23c179ce60a71c63bfc798d444e444b61047b5..1e4bd58a7f35807198929e8575bcb936d078f806 100644 GIT binary patch delta 190 zcmZozz|pXPV}dlV8Uq7^A{4Vs)G%jM+nBKAKO^&2R>tLgj4QXV dapOT=W}r%GAeI7Rxy^zOb2zqd;9v@12LLcpDC7VD delta 207 zcmZozz|pXPV}dlV3IhX!A`~-E)G%jM*_g28KO@UlR>tLgjKSO2@-c=pa=-v5;O;nE+PN` diff --git a/package.json b/package.json index 4a2824c..f495788 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "electron-store": "^6.0.1", "electron-trpc": "^0.7.1", "electron-updater": "^6.3.9", + "html2canvas": "^1.4.1", "langchain": "^1.2.4", "mitt": "^3.0.1", "p-limit": "^2.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b30c42a..26101f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: electron-updater: specifier: ^6.3.9 version: 6.6.2 + html2canvas: + specifier: ^1.4.1 + version: 1.4.1 langchain: specifier: ^1.2.4 version: 1.2.4(@langchain/core@1.1.11(openai@6.15.0(zod@4.3.5)))(openai@6.15.0(zod@4.3.5)) @@ -1457,6 +1460,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1764,6 +1771,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-line-break@2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + css-tree@3.1.0: resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -2418,6 +2428,10 @@ packages: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} + html2canvas@1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} + http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -3616,6 +3630,9 @@ packages: resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} engines: {node: '>=8'} + text-segmentation@1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -3854,6 +3871,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utrie@1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + uuid@10.0.0: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true @@ -5417,6 +5437,8 @@ snapshots: balanced-match@1.0.2: {} + base64-arraybuffer@1.0.2: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.9.11: {} @@ -5784,6 +5806,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-line-break@2.1.0: + dependencies: + utrie: 1.0.2 + css-tree@3.1.0: dependencies: mdn-data: 2.12.2 @@ -6599,6 +6625,11 @@ snapshots: dependencies: lru-cache: 6.0.0 + html2canvas@1.4.1: + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + http-cache-semantics@4.2.0: {} http-proxy-agent@5.0.0: @@ -7765,6 +7796,10 @@ snapshots: text-extensions@2.4.0: {} + text-segmentation@1.0.3: + dependencies: + utrie: 1.0.2 + through@2.3.8: {} tiny-async-pool@1.3.0: @@ -7995,6 +8030,10 @@ snapshots: util-deprecate@1.0.2: {} + utrie@1.0.2: + dependencies: + base64-arraybuffer: 1.0.2 + uuid@10.0.0: {} uuid@11.1.0: {} diff --git a/src/renderer/components.d.ts b/src/renderer/components.d.ts index 5bf6f5d..00a94c3 100644 --- a/src/renderer/components.d.ts +++ b/src/renderer/components.d.ts @@ -21,6 +21,7 @@ declare module 'vue' { AInput: typeof import('@arco-design/web-vue')['Input'] AInputNumber: typeof import('@arco-design/web-vue')['InputNumber'] AInputPassword: typeof import('@arco-design/web-vue')['InputPassword'] + AModal: typeof import('@arco-design/web-vue')['Modal'] AOption: typeof import('@arco-design/web-vue')['Option'] ASelect: typeof import('@arco-design/web-vue')['Select'] ASlider: typeof import('@arco-design/web-vue')['Slider'] @@ -29,6 +30,7 @@ declare module 'vue' { ATabs: typeof import('@arco-design/web-vue')['Tabs'] ATag: typeof import('@arco-design/web-vue')['Tag'] ATextarea: typeof import('@arco-design/web-vue')['Textarea'] + ATooltip: typeof import('@arco-design/web-vue')['Tooltip'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] } diff --git a/src/renderer/src/pages/reflection/index.vue b/src/renderer/src/pages/reflection/index.vue new file mode 100644 index 0000000..ca334a6 --- /dev/null +++ b/src/renderer/src/pages/reflection/index.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/src/renderer/src/pages/reflection/views/poster/index.vue b/src/renderer/src/pages/reflection/views/poster/index.vue new file mode 100644 index 0000000..3740d12 --- /dev/null +++ b/src/renderer/src/pages/reflection/views/poster/index.vue @@ -0,0 +1,247 @@ + + + + + diff --git a/src/renderer/src/pages/task/detail.vue b/src/renderer/src/pages/task/detail.vue deleted file mode 100644 index 4eb1922..0000000 --- a/src/renderer/src/pages/task/detail.vue +++ /dev/null @@ -1,145 +0,0 @@ - - - - - diff --git a/src/renderer/src/pages/task/index.vue b/src/renderer/src/pages/task/index.vue index 8d40aba..c592356 100644 --- a/src/renderer/src/pages/task/index.vue +++ b/src/renderer/src/pages/task/index.vue @@ -54,13 +54,19 @@ const handleDeleteTask = () => { }) } -// 预览功能 +/** + * 预览功能 + * @param subTaskId 子任务ID + * */ const handlePreview = (subTaskId: string) => { - go('/task/detail', { + go('/reflection', { id: subTaskId, batchId: activeTaskId.value }) } +/** + * 监听任务进度 + * */ const statusSub = trpc.task.onReadingReflectionStatusUpdate.subscribe(undefined, { onData(data) { const index = bookSubTasks.value.findIndex((item) => item.id === data.taskId) @@ -92,6 +98,9 @@ const statusSub = trpc.task.onReadingReflectionStatusUpdate.subscribe(undefined, console.error('订阅流异常:', err) } }) +/** + * 监听任务进度 + * */ const batchSub = trpc.task.onBatchProgressUpdate.subscribe(undefined, { onData(data) { if (data.batchId === activeTaskId.value) { @@ -101,6 +110,9 @@ const batchSub = trpc.task.onBatchProgressUpdate.subscribe(undefined, { } }) +/** + * 获取当亲任务的详情 + * */ const fetchCurrentTaskDetail = async () => { if (!activeTaskId.value) return const data = await trpc.task.getBatchDetail.query({ batchId: activeTaskId.value }) diff --git a/src/renderer/src/router/index.ts b/src/renderer/src/router/index.ts index 4b85cc2..bd60030 100644 --- a/src/renderer/src/router/index.ts +++ b/src/renderer/src/router/index.ts @@ -16,10 +16,16 @@ const routes: RouteRecordRaw[] = [ component: () => import('@renderer/pages/task/create.vue') }, { - path: '/task/detail', - name: 'TaskDetail', - component: () => import('@renderer/pages/task/detail.vue') + path: '/reflection', + name: 'Reflection', + component: () => import('@renderer/pages/reflection/index.vue') }, + { + path: '/reflection/poster', + name: 'ReflectionPoster', + component: () => import('@renderer/pages/reflection/views/poster/index.vue') + }, + { path: '/setting', name: 'Setting', diff --git a/src/rpc/router/task.router.ts b/src/rpc/router/task.router.ts index 8c4a323..3bb3c34 100644 --- a/src/rpc/router/task.router.ts +++ b/src/rpc/router/task.router.ts @@ -96,6 +96,18 @@ export const taskRouter = router({ return items as IReadingReflectionTaskItem[] }), + /** + * 获取子任务详情 + * */ + getItemDetail: publicProcedure + .input(z.object({ itemId: z.string() })) + .query(async ({ input }) => { + const repo = AppDataSource.getRepository(ReadingReflectionTaskItem) + const item = await repo.findOne({ + where: { id: input.itemId } + }) + return item as IReadingReflectionTaskItem + }), /** * 获取批次详情及其关联的子任务 * 适用于点击左侧列表后,一次性初始化右侧所有内容