-
模板详情
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/left-tree/components/import-bubble.vue b/apps/admin/src/views/template/template-detail/modules/left-tree/components/import-bubble.vue
new file mode 100644
index 0000000..2841ff3
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/left-tree/components/import-bubble.vue
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 用于排除部分书籍(封面/目录/引言)等没有页码的页面
+
+ 第1页开始于:
+
+
+
+ 第
+ 张PDF
+
+
+
+
+
+
+
+
+
+ 开始导入
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/left-tree/components/menu-bubble.vue b/apps/admin/src/views/template/template-detail/modules/left-tree/components/menu-bubble.vue
new file mode 100644
index 0000000..77bf932
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/left-tree/components/menu-bubble.vue
@@ -0,0 +1,123 @@
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/left-tree/left-tree.vue b/apps/admin/src/views/template/template-detail/modules/left-tree/left-tree.vue
new file mode 100644
index 0000000..02c86db
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/left-tree/left-tree.vue
@@ -0,0 +1,207 @@
+
+
+
+
目录
+
+
+
+
+ 导入书籍
+
+
+
+
+
+ 重置起始页
+
+
+
+
+
+
+
+
+
{{ option.name }}
+
+ 校验成功
+ 校验失败
+
+
+
+
+
+
+
+
+
+ 重新加载
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/middle-book/components/menu-bubble.vue b/apps/admin/src/views/template/template-detail/modules/middle-book/components/menu-bubble.vue
new file mode 100644
index 0000000..f1f3d3c
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/middle-book/components/menu-bubble.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/middle-book/components/topic-item-module.vue b/apps/admin/src/views/template/template-detail/modules/middle-book/components/topic-item-module.vue
new file mode 100644
index 0000000..57439d7
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/middle-book/components/topic-item-module.vue
@@ -0,0 +1,312 @@
+
+
+
+
+
+ {{ questions?.no }}
+
+ {{ showType }}
+
+
+ 配
+
+
+ 答
+ 解
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ questions?.no }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/middle-book/middle-book.vue b/apps/admin/src/views/template/template-detail/modules/middle-book/middle-book.vue
new file mode 100644
index 0000000..09399c0
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/middle-book/middle-book.vue
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/add-analysis-bubble.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/add-analysis-bubble.vue
new file mode 100644
index 0000000..8334399
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/add-analysis-bubble.vue
@@ -0,0 +1,200 @@
+
+
+
+
+ 添加解析
+
+
+
+
+ 添加自定义选项
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 确定
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/add-topic-form-bubble.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/add-topic-form-bubble.vue
new file mode 100644
index 0000000..7d5d20a
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/add-topic-form-bubble.vue
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+ 第
+ 页
+
+
+
+
+ 第
+ 大题
+
+
+
+
+ 第
+ 小题
+
+
+
+
+ 第
+ 部分
+
+
+
+
+
+
+
+
+
+ handleSubjectSearch('')"
+ />
+
+
+
+ 分
+
+
+
+
+ 秒
+
+
+
+
+
+ 取消
+ 提交
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/edit-info-bubble.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/edit-info-bubble.vue
new file mode 100644
index 0000000..780bacf
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/edit-info-bubble.vue
@@ -0,0 +1,458 @@
+
+
+
+
+
+
+ 编辑信息
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+
+
+
+
基本信息
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 正确
+ 错误
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 秒
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 重试
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/release-task-bubble.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/release-task-bubble.vue
new file mode 100644
index 0000000..8bed023
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/release-task-bubble.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+ 取消
+ 提交
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/right-topic-item.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/right-topic-item.vue
new file mode 100644
index 0000000..7b96d1a
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/components/right-topic-item.vue
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+ {{ topicInfo.question.no }}
+
+
+ {{ showType }}
+
+
+
+
+
+
+
+
+
+
+
+ 单项解析
+
+
+
+
+
+
+
+
+
+ 发布任务(录制讲解音视频)
+
+
+
+ emit('edit-topic', topicInfo)"
+ >
+
+
+
+
+
+ 编辑或修改信息
+
+
+
+
+
+
+
+
+
+ 点击添加答题区域
+
+
+
+
+
+
+
+
+
+ 删除题目
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/right-topic-list-normal.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/right-topic-list-normal.vue
new file mode 100644
index 0000000..ab5b2b7
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-normal/right-topic-list-normal.vue
@@ -0,0 +1,139 @@
+
+
+
+
+ 普通题目
+
+
+
+
+ 添加题目
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/components/add-topic-form-bubble.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/components/add-topic-form-bubble.vue
new file mode 100644
index 0000000..41113e9
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/components/add-topic-form-bubble.vue
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+ 第
+
+ 页
+
+
+
+
+ 第
+
+ 题
+
+
+
+
+ 第
+
+ 部分
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 提交
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/components/right-topic-item.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/components/right-topic-item.vue
new file mode 100644
index 0000000..3458fa5
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/components/right-topic-item.vue
@@ -0,0 +1,139 @@
+
+
+
+
+
+ {{ topicInfo.question.no }}
+
+
+ {{ showType }}
+
+
+
+
+
+ emit('edit-topic', $event, topicInfo)"
+ >
+
+
+
+ 编辑或修改信息
+
+
+
+
+
+
+
+ 点击添加区域
+
+
+
+
+
+
+
+ 删除题目
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/right-topic-list-special.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/right-topic-list-special.vue
new file mode 100644
index 0000000..7acf226
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/components/right-topic-list-special/right-topic-list-special.vue
@@ -0,0 +1,110 @@
+
+
+
+ 特殊区域
+
+
+ 添加区域
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/modules/right-topic/right-topic-list.vue b/apps/admin/src/views/template/template-detail/modules/right-topic/right-topic-list.vue
new file mode 100644
index 0000000..dab107c
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/modules/right-topic/right-topic-list.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/admin/src/views/template/template-detail/pdf-util.ts b/apps/admin/src/views/template/template-detail/pdf-util.ts
new file mode 100644
index 0000000..f78137a
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/pdf-util.ts
@@ -0,0 +1,11 @@
+import type { ImageInfo } from './types';
+
+export async function convertPDFToImageFiles(file: File): Promise<{ list: ImageInfo[], pdf: File }> {
+ console.warn('PDF conversion requires pdfjs-dist, which is missing. Returning empty list.');
+ return { list: [], pdf: file };
+}
+
+export async function getPDFPageWidth(file: File): Promise
{
+ console.warn('PDF conversion requires pdfjs-dist, which is missing. Returning 0.');
+ return 0;
+}
diff --git a/apps/admin/src/views/template/template-detail/types.ts b/apps/admin/src/views/template/template-detail/types.ts
new file mode 100644
index 0000000..947a9bf
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/types.ts
@@ -0,0 +1,100 @@
+
+export interface BookBaseData {
+ width?: number;
+ height?: number;
+}
+
+export interface QuestionInfo {
+ type?: number;
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ no: string;
+}
+
+export interface LayoutItem {
+ questionId: number;
+ question: QuestionInfo;
+ answerList?: any[]; // Added based on topic-item-module usage
+}
+
+export interface BookPageQuestion {
+ id: number;
+ type?: number;
+ [key: string]: any;
+}
+
+export interface BookPageAreas {
+ id: number;
+ [key: string]: any;
+}
+
+export interface CurrBookPageData {
+ url?: string;
+ bookPageId?: number;
+ layout: LayoutItem[];
+ questions: BookPageQuestion[];
+ areas: BookPageAreas[];
+}
+
+export interface CurrBookPageAllInfo {
+ bookBaseData: BookBaseData;
+ currBookPageData: CurrBookPageData;
+}
+
+export type QuestionType = number;
+
+export interface QuestionsImages {
+ // Add properties as needed based on usage in util.ts
+ [key: string]: any;
+}
+
+export interface CropImageOptions {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+}
+
+export interface AddBookTopicForm {
+ type?: 100 | 101 | 102;
+ pageNumb?: number;
+ bigTpicNumb?: number;
+ subTopicNumb?: number;
+}
+
+export interface BookPageOtherAddInput {
+ bookId: number;
+ bookPageId: number;
+ no: string;
+ type: number;
+}
+
+export interface BookPageOtherUpdateInput {
+ bookId: number;
+ bookPageId: number;
+ no: string;
+ type: number;
+ id: number;
+}
+
+export interface TopicInfo {
+ question: QuestionInfo;
+ answerList?: any[];
+ questionId: number;
+}
+
+export interface AnswerInfo {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+}
+
+export interface ImageInfo {
+ file: File;
+ width: number;
+ height: number;
+ url?: string;
+}
diff --git a/apps/admin/src/views/template/template-detail/util.ts b/apps/admin/src/views/template/template-detail/util.ts
new file mode 100644
index 0000000..75e8909
--- /dev/null
+++ b/apps/admin/src/views/template/template-detail/util.ts
@@ -0,0 +1,138 @@
+
+import { ref } from 'vue';
+import pLimit from 'p-limit';
+import { type BookPageQuestion, type QuestionsImages, updateBookPageLayoutAxios } from '@/service/api/book';
+import { getAliOssTokenAxios } from '@/service/api/upload';
+import { initOSSClient } from '@/utils/oss';
+import type { CropImageOptions, CurrBookPageAllInfo, QuestionInfo } from './types';
+import { compareVersion } from '@/utils/common';
+
+// Crop image function implementation
+const cropImage = async (url: string, client: any, options: CropImageOptions, id: number): Promise => {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ img.crossOrigin = 'anonymous';
+ img.src = url;
+ img.onload = async () => {
+ try {
+ const canvas = document.createElement('canvas');
+ canvas.width = options.width;
+ canvas.height = options.height;
+ const ctx = canvas.getContext('2d');
+ if (!ctx) {
+ reject(new Error('Canvas context not available'));
+ return;
+ }
+
+ // Draw cropped image
+ ctx.drawImage(
+ img,
+ options.x,
+ options.y,
+ options.width,
+ options.height,
+ 0,
+ 0,
+ options.width,
+ options.height
+ );
+
+ // Convert to Blob
+ canvas.toBlob(async (blob) => {
+ if (!blob) {
+ reject(new Error('Canvas to Blob failed'));
+ return;
+ }
+
+ const file = new File([blob], `question_${id}_${Date.now()}.png`, { type: 'image/png' });
+ const path = `book/questions/${id}/${file.name}`;
+
+ try {
+ // Upload to OSS
+ // Assuming client.put or multipartUpload.
+ // Since initOSSClient returns an OSS client, we can use it.
+ // Using put for small files (cropped images are usually small)
+ const result = await client.put(path, file);
+
+ // Return the result format expected by the API
+ // The API expects a map of ID to URL? Or array of objects?
+ // Based on usage: `tasks.push(...)` and `imageFiles.push(...list)`
+ // And `updateBookPageLayoutAxios` takes `questionsImages: imageFiles`
+ // Let's return an object with the ID as key and URL as value, or just the URL object if QuestionsImages is a type.
+ // Looking at previous usage `urlObj[item.id!] = item`, it seems `QuestionsImages` might be `{ [id]: url }`.
+
+ resolve({ [id]: result.url });
+ } catch (err) {
+ reject(err);
+ }
+ }, 'image/png');
+ } catch (err) {
+ reject(err);
+ }
+ };
+ img.onerror = (err) => {
+ reject(new Error('Image load failed'));
+ };
+ });
+};
+
+/** 提交 */
+export function usePageLayoutSubmit() {
+ const submmitLoading = ref(false);
+
+ async function pageLayoutSubmit(currBookPage: CurrBookPageAllInfo) {
+ try {
+ submmitLoading.value = true;
+ const urlObj: Record = {};
+ currBookPage!.currBookPageData.questions.forEach((item) => {
+ if (item.id) {
+ urlObj[item.id!] = item;
+ }
+ });
+
+ // 获取阿里云OSS STS
+ const aliOssSTS = await getAliOssTokenAxios();
+ const client = initOSSClient(aliOssSTS);
+
+ // 截取图片并上传 (5个并发)
+ const imageFiles: QuestionsImages[] = [];
+ const limit = pLimit(5);
+ const tasks: Promise[] = [];
+ currBookPage!.currBookPageData.layout.forEach((item) => {
+ const question: QuestionInfo = item.question;
+ if (currBookPage!.currBookPageData.url) {
+ const cropImageOptions: CropImageOptions = { x: question.x, y: question.y, width: question.w, height: question.h };
+ const url = currBookPage!.currBookPageData.url;
+ //截取图片并添加到上传队列
+ tasks.push(limit(() => cropImage(url, client, cropImageOptions, item.questionId!)));
+ }
+ });
+
+ const list = await Promise.all(tasks);
+ imageFiles.push(...list);
+
+ currBookPage!.currBookPageData.layout = currBookPage!.currBookPageData.layout.sort((a, b) => {
+ return compareVersion(a.question.no, b.question.no, '-');
+ });
+ await updateBookPageLayoutAxios({
+ id: currBookPage!.currBookPageData!.bookPageId!,
+ layout: JSON.stringify(currBookPage!.currBookPageData.layout),
+ questionsImages: imageFiles,
+ });
+ submmitLoading.value = false;
+ return Promise.resolve();
+ } catch (error) {
+ console.log('error====', error);
+ submmitLoading.value = false;
+ return Promise.reject(error);
+ }
+ }
+ return {
+ pageLayoutSubmit,
+ submmitLoading,
+ };
+}
+
+export function getPDFPageWidth(width: number) {
+ return width;
+}