Bladeren bron

Merge branch 'master' into lhd

liuhaidi123 3 weken geleden
bovenliggende
commit
0cfa1395a9

+ 12 - 9
src/api/offline.js

@@ -5,9 +5,9 @@ import { http } from '@/utils/http';
  * @param {Object} data
  * @param {string} data.book_id 教材ID
  */
-export function CreateBookChapterStructFileList(data) {
+export function CreateBookOfflinePackChapterStructFileList(data) {
   return http.post(
-    `${process.env.VUE_APP_EepServer}?MethodName=offline_pack_manager-CreateBookChapterStructFileList`,
+    `${process.env.VUE_APP_EepServer}?MethodName=offline_pack_manager-CreateBookOfflinePackChapterStructFileList`,
     data,
   );
 }
@@ -17,35 +17,38 @@ export function CreateBookChapterStructFileList(data) {
  * @param {Object} data
  * @param {string} data.courseware_id 课节ID
  */
-export function CreateCoursewareFileList(data) {
-  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=offline_pack_manager-CreateCoursewareFileList`, data);
+export function CreateOfflinePackCoursewareFileList(data) {
+  return http.post(
+    `${process.env.VUE_APP_EepServer}?MethodName=offline_pack_manager-CreateOfflinePackCoursewareFileList`,
+    data,
+  );
 }
 
 /**
  * 添加教材离线包授权
- * @param {} data 
+ * @param {} data
  */
 export function AddBookOfflinePackAuth(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=offline_pack_manager-AddBookOfflinePackAuth`, data);
 }
 /**
  * 删除教材离线包授权
- * @param {} data 
+ * @param {} data
  */
 export function DeleteBookOfflinePackAuth(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=offline_pack_manager-DeleteBookOfflinePackAuth`, data);
 }
 /**
  * 废弃教材离线包授权
- * @param {} data 
+ * @param {} data
  */
 export function DisableBookOfflinePackAuth(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=offline_pack_manager-DisableBookOfflinePackAuth`, data);
 }
 /**
  * 分页查询教材离线包授权列表
- * @param {} data 
+ * @param {} data
  */
 export function PageQueryBookOfflinePackAuthList(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=page_query-PageQueryBookOfflinePackAuthList`, data);
-}
+}

+ 24 - 6
src/utils/auth.js

@@ -83,23 +83,41 @@ export function removeConfig() {
 /**
  * 设置本地存储
  * @param {string} key - 存储的键
- * @param {*} value - 存储的值
+ * @param {any} value - 存储的值
  */
 export function setLocalStore(key, value) {
-  localStorage.setItem(key, JSON.stringify(value));
+  try {
+    if (value === undefined) {
+      localStorage.removeItem(key);
+      return;
+    }
+    const toStore = typeof value === 'string' ? value : JSON.stringify(value);
+    localStorage.setItem(key, toStore);
+  } catch (e) {
+    // 序列化或存储失败时退回到字符串存储,若仍失败则忽略
+    try {
+      localStorage.setItem(key, String(value));
+    } catch (_) {
+      /* noop */
+    }
+  }
 }
 
 /**
  * 获取本地存储
  * @param {string} key - 存储的键
- * @returns {*} 返回存储的值
+ * @returns {any} 返回存储的值
  */
 export function getLocalStore(key) {
   const value = localStorage.getItem(key);
-  if (value) {
-    return JSON.parse(value);
+  try {
+    if (value) {
+      return JSON.parse(value);
+    }
+    return null;
+  } catch (e) {
+    return value;
   }
-  return null;
 }
 
 /**

+ 1 - 1
src/views/book/courseware/create/components/CreateCanvas.vue

@@ -1375,7 +1375,7 @@ export default {
       }
 
       if (['left', 'right'].includes(position)) {
-        let rowNum = grid[position === 'left' ? k : k - 1].row;
+        let rowNum = grid[k].row;
         let _k = position === 'left' ? k : k + 1;
         grid.splice(_k, 0, {
           id,

+ 0 - 188
src/views/project_manage/org/book/index.vue

@@ -26,7 +26,6 @@
 
         <el-table-column label="操作" fixed="right" width="260" align="center" header-align="center">
           <template slot-scope="{ row }">
-            <span class="link" @click="selectDirectory(row.id)">下载离线包</span>
             <span class="link">查看信息</span>
             <span class="link" @click="previewBook(row.id)">预览信息</span>
           </template>
@@ -44,7 +43,6 @@ import PaginationPage from '@/components/PaginationPage.vue';
 
 import { PageQueryYSJBookList_OrgManager } from '@/api/list';
 import { isTrue } from '@/utils/validate';
-import { CreateBookChapterStructFileList, CreateCoursewareFileList } from '@/api/offline';
 
 export default {
   name: 'OrgBookPage',
@@ -58,41 +56,8 @@ export default {
       total: 0,
       cur_page_begin_index: 0,
       isTrue,
-      loadingInstance: null, // 加载中实例
-      tempDir: '', // 临时目录,用于存放下载的文件
-      savePath: '', // 保存离线包的路径
-      packageName: 'EEP 离线包', // 离线包名称
-      file_info_list: [], // 章节结构文件列表
-      downloadTotal: 0, // 下载总数
-      downloadCompleted: 0, // 已下载数量
-      downloadWatcher: null, // 下载进度监听器
     };
   },
-  computed: {
-    downloadProgress() {
-      if (this.downloadTotal === 0) return 0;
-      return (this.downloadCompleted / this.downloadTotal) * 100;
-    },
-  },
-  beforeDestroy() {
-    // 删除临时目录及其内容
-    if (this.tempDir && this.tempDir.length > 0) {
-      window.fileAPI.deleteTempDir(this.tempDir);
-      this.tempDir = '';
-    }
-
-    // 删除 watch 监听器
-    if (this.downloadWatcher) {
-      this.downloadWatcher();
-      this.downloadWatcher = null;
-    }
-
-    // 关闭加载中实例
-    if (this.loadingInstance) {
-      this.loadingInstance.close();
-      this.loadingInstance = null;
-    }
-  },
   methods: {
     pageQueryYSJBookList_OrgManager(data) {
       PageQueryYSJBookList_OrgManager(data).then(({ total_count, book_list, cur_page_begin_index }) => {
@@ -104,159 +69,6 @@ export default {
     previewBook(id) {
       this.$router.push({ path: `/project_manage/org/book/preview/${id}` });
     },
-
-    /**
-     * 选择保存离线包的目录
-     * @param {String} book_id 教材ID
-     */
-    async selectDirectory(book_id) {
-      const result = await window.fileAPI.openFileDialog({
-        title: '选择保存离线包的文件夹',
-        properties: ['openDirectory', 'createDirectory'],
-      });
-      if (result.canceled) {
-        this.$message.warning('未选择文件夹,操作已取消');
-        return;
-      }
-      this.savePath = result.filePaths[0];
-      this.downloadOfflinePackage(book_id);
-    },
-
-    /**
-     * 下载离线包
-     * @param {String} book_id 教材ID
-     */
-    async downloadOfflinePackage(book_id) {
-      const { file_info_list, courseware_id_list } = await CreateBookChapterStructFileList({
-        book_id,
-      });
-
-      this.file_info_list = file_info_list;
-      if (file_info_list.length === 0) {
-        this.$message.error('章节结构文件生成失败,无法下载离线包');
-        return;
-      }
-
-      if (this.tempDir.length === 0) {
-        this.tempDir = window.fileAPI.createTempDir(); // 创建临时保存目录
-      }
-
-      this.loadingInstance = this.$loading({
-        lock: true,
-        text: '正在下载离线包,请稍候...',
-        spinner: 'el-icon-loading',
-        background: 'rgba(255, 255, 255, 0.7)',
-      });
-
-      for (const { dir_name, file_name, file_url } of this.file_info_list) {
-        const dirPath = dir_name.length > 0 ? `${this.tempDir}\\${dir_name}` : this.tempDir;
-        await window.fileAPI.downloadFile(file_url, `${dirPath}\\${file_name}`);
-      }
-
-      const struct = await this.readFileContent('struct.json'); // 读取章节结构文件内容
-
-      if (struct.node_list && struct.node_list.length > 0) {
-        this.packageName = struct.node_list[0].name; // 设置离线包名称为课件名称
-      }
-
-      if (courseware_id_list && courseware_id_list.length > 0) {
-        this.downloadWatcher = this.$watch(
-          'downloadProgress',
-          (newVal) => {
-            if (newVal >= 100) {
-              this.downloadWatcher();
-              this.startCompress();
-            } else {
-              // 显示下载进度,预留 10% 的压缩进度
-              this.loadingInstance.text = `正在下载离线包,进度:${((this.downloadCompleted / (this.downloadTotal + this.downloadTotal * 0.1)) * 100).toFixed(2)}%`;
-            }
-          },
-          { immediate: true },
-        );
-
-        Promise.all(courseware_id_list.map((courseware_id) => this.createCoursewareFileList(courseware_id)));
-      }
-    },
-
-    /**
-     * 下载课件文件列表
-     * @param {Number} courseware_id 课件ID
-     * @return {Promise} Promise对象
-     */
-    async createCoursewareFileList(courseware_id) {
-      const { file_info_list } = await CreateCoursewareFileList({ courseware_id });
-      this.downloadTotal += file_info_list.length;
-
-      file_info_list.forEach((fileInfo) => {
-        const { file_name, file_url, dir_name } = fileInfo;
-        window.fileAPI.downloadFile(file_url, `${this.tempDir}\\${dir_name}\\${file_name}`).then(() => {
-          this.downloadCompleted += 1;
-        });
-      });
-    },
-
-    /**
-     * 开始压缩
-     */
-    async startCompress() {
-      if (this.downloadWatcher) {
-        this.downloadWatcher = null;
-      }
-      const sources = [`${this.tempDir}\\resource`, `${this.tempDir}\\courseware`]; // 要压缩的文件或文件夹路径数组
-      this.file_info_list.forEach(({ dir_name, file_name }) => {
-        const dirPath = dir_name.length > 0 ? `${this.tempDir}\\${dir_name}` : this.tempDir;
-        sources.push(`${dirPath}\\${file_name}`);
-      });
-      const dest = `${this.savePath}\\${this.packageName}.eep`; // 压缩包保存路径
-      try {
-        const offErr = window.fileAPI.onStderr((text) => console.warn('7z stderr:', text));
-
-        await window.fileAPI.compress({
-          sources,
-          dest,
-          format: 'zip',
-          level: 5,
-          password: '1234567a',
-          recurse: true,
-        });
-        offErr();
-      } catch (e) {
-        console.error('压缩失败:', e);
-        this.$message.error('离线包下载失败,请重试!');
-        this.loadingInstance.close();
-        this.loadingInstance = null;
-        this.tempDir = '';
-        return;
-      }
-
-      this.loadingInstance.text = `正在下载离线包,进度:100%`;
-
-      // 删除临时目录及其内容
-      try {
-        await window.fileAPI.deleteTempDir(this.tempDir);
-        this.tempDir = '';
-      } catch (e) {
-        console.error('删除临时目录失败:', e);
-        this.tempDir = '';
-      }
-
-      this.loadingInstance.close();
-      this.loadingInstance = null;
-
-      this.$message.success('离线包下载并保存成功!');
-    },
-
-    /**
-     * 读取文件内容
-     * @param entryName 文件条目名称
-     * @param subdirectory 子目录
-     */
-    async readFileContent(entryName, subdirectory = '') {
-      const content = await window.fileAPI.readZipFileSync(`${this.tempDir}/${subdirectory}`, entryName);
-      const text = new TextDecoder().decode(content);
-      const obj = JSON.parse(text);
-      return obj;
-    },
   },
 };
 </script>

+ 198 - 10
src/views/project_manage/org/offlinepackauth/index.vue

@@ -51,8 +51,9 @@
         </el-table-column>
         <el-table-column prop="creator_name" label="创建人" width="120" align="center" />
         <el-table-column prop="create_time" label="创建时间" width="160" align="center" />
-        <el-table-column label="操作" fixed="right" width="150" align="center">
+        <el-table-column label="操作" fixed="right" width="220" align="center">
           <template slot-scope="{ row }">
+            <span class="link" @click="selectDirectory(row.id)">导出离线包</span>
             <span class="link" @click="handleDisable(row.id)">废弃</span>
             <el-divider direction="vertical" />
             <span class="link" @click="handleDel(row.id)">删除</span>
@@ -74,15 +75,15 @@
           </el-input>
         </el-form-item>
         <el-form-item label="有效次数" prop="effective_count">
-          <el-input v-model.number="editForm.effective_count" placeholder="请输入有效次数"></el-input>
+          <el-input v-model.number="editForm.effective_count" placeholder="请输入有效次数" />
         </el-form-item>
         <el-form-item label="有效截止日期" prop="effective_end_date">
           <el-date-picker
+            v-model="editForm.effective_end_date"
             type="date"
             placeholder="选择日期"
-            v-model="editForm.effective_end_date"
             value-format="yyyy-MM-dd"
-          ></el-date-picker>
+          />
         </el-form-item>
         <el-form-item label="备注" prop="memo">
           <el-input v-model="editForm.memo" type="textarea" />
@@ -127,6 +128,8 @@ import {
   DisableBookOfflinePackAuth,
   BookOfflinePackActivate,
   PageQueryBookOfflinePackAuthList,
+  CreateBookOfflinePackChapterStructFileList,
+  CreateOfflinePackCoursewareFileList,
 } from '@/api/offline';
 import { isTrue } from '@/utils/validate';
 
@@ -169,9 +172,22 @@ export default {
         ],
         effective_end_date: [{ required: true, message: '请选择有效截止日期', trigger: 'blur' }],
       },
+      loadingInstance: null, // 加载中实例
+      tempDir: '', // 临时目录,用于存放下载的文件
+      savePath: '', // 保存离线包的路径
+      packageName: 'EEP 离线包', // 离线包名称
+      file_info_list: [], // 章节结构文件列表
+      downloadTotal: 0, // 下载总数
+      downloadCompleted: 0, // 已下载数量
+      downloadWatcher: null, // 下载进度监听器
     };
   },
-  computed: {},
+  computed: {
+    downloadProgress() {
+      if (this.downloadTotal === 0) return 0;
+      return (this.downloadCompleted / this.downloadTotal) * 100;
+    },
+  },
   watch: {
     dialogFormEdit: {
       handler(val) {
@@ -182,6 +198,25 @@ export default {
       deep: true,
     },
   },
+  beforeDestroy() {
+    // 删除临时目录及其内容
+    if (this.tempDir && this.tempDir.length > 0) {
+      window.fileAPI.deleteTempDir(this.tempDir);
+      this.tempDir = '';
+    }
+
+    // 删除 watch 监听器
+    if (this.downloadWatcher) {
+      this.downloadWatcher();
+      this.downloadWatcher = null;
+    }
+
+    // 关闭加载中实例
+    if (this.loadingInstance) {
+      this.loadingInstance.close();
+      this.loadingInstance = null;
+    }
+  },
   methods: {
     queryList() {
       this.pageList(this.queryForm);
@@ -194,7 +229,7 @@ export default {
           this.list = offline_pack_auth_list;
           this.total = total_count;
           this.cur_page_begin_index = cur_page_begin_index;
-        }
+        },
       );
     },
     pageBookList(data) {
@@ -223,7 +258,7 @@ export default {
           let data = this.editForm;
           this.dialogSearchBook = false;
           AddBookOfflinePackAuth(data).then((res) => {
-            if (res && res.status == 1) {
+            if (res && res.status === 1) {
               this.dialogFormEdit = false;
               this.queryList();
             }
@@ -241,7 +276,7 @@ export default {
         type: 'warning',
       })
         .then(() => {
-          DisableBookOfflinePackAuth({ id: id }).then(() => {
+          DisableBookOfflinePackAuth({ id }).then(() => {
             this.queryList();
           });
         })
@@ -254,17 +289,170 @@ export default {
         type: 'warning',
       })
         .then(() => {
-          DeleteBookOfflinePackAuth({ id: id }).then(() => {
+          DeleteBookOfflinePackAuth({ id }).then(() => {
             this.queryList();
           });
         })
         .catch(() => {});
     },
     handleActive(id) {
-      BookOfflinePackActivate({ id: id }).then(() => {
+      BookOfflinePackActivate({ id }).then(() => {
         this.queryList();
       });
     },
+
+    /**
+     * 选择保存离线包的目录
+     * @param {String} book_id 教材ID
+     */
+    async selectDirectory(book_id) {
+      const result = await window.fileAPI.openFileDialog({
+        title: '选择保存离线包的文件夹',
+        properties: ['openDirectory', 'createDirectory'],
+      });
+      if (result.canceled) {
+        this.$message.warning('未选择文件夹,操作已取消');
+        return;
+      }
+      this.savePath = result.filePaths[0];
+      this.downloadOfflinePackage(book_id);
+    },
+
+    /**
+     * 下载离线包
+     * @param {String} offline_pack_auth_id 离线包授权ID
+     */
+    async downloadOfflinePackage(offline_pack_auth_id) {
+      const { file_info_list, courseware_id_list } = await CreateBookOfflinePackChapterStructFileList({
+        offline_pack_auth_id,
+      });
+
+      this.file_info_list = file_info_list;
+      if (file_info_list.length === 0) {
+        this.$message.error('章节结构文件生成失败,无法下载离线包');
+        return;
+      }
+
+      if (this.tempDir.length === 0) {
+        this.tempDir = window.fileAPI.createTempDir(); // 创建临时保存目录
+      }
+
+      this.loadingInstance = this.$loading({
+        lock: true,
+        text: '正在下载离线包,请稍候...',
+        spinner: 'el-icon-loading',
+        background: 'rgba(255, 255, 255, 0.7)',
+      });
+
+      for (const { dir_name, file_name, file_url } of this.file_info_list) {
+        const dirPath = dir_name.length > 0 ? `${this.tempDir}\\${dir_name}` : this.tempDir;
+        await window.fileAPI.downloadFile(file_url, `${dirPath}\\${file_name}`);
+      }
+
+      const struct = await this.readFileContent('struct.json'); // 读取章节结构文件内容
+
+      if (struct.node_list && struct.node_list.length > 0) {
+        this.packageName = struct.node_list[0].name; // 设置离线包名称为课件名称
+      }
+
+      if (courseware_id_list && courseware_id_list.length > 0) {
+        this.downloadWatcher = this.$watch(
+          'downloadProgress',
+          (newVal) => {
+            if (newVal >= 100) {
+              this.downloadWatcher();
+              this.startCompress();
+            } else {
+              // 显示下载进度,预留 10% 的压缩进度
+              this.loadingInstance.text = `正在下载离线包,进度:${((this.downloadCompleted / (this.downloadTotal + this.downloadTotal * 0.1)) * 100).toFixed(2)}%`;
+            }
+          },
+          { immediate: true },
+        );
+
+        Promise.all(courseware_id_list.map((courseware_id) => this.CreateOfflinePackCoursewareFileList(courseware_id)));
+      }
+    },
+
+    /**
+     * 下载课件文件列表
+     * @param {Number} courseware_id 课件ID
+     * @return {Promise} Promise对象
+     */
+    async CreateOfflinePackCoursewareFileList(courseware_id) {
+      const { file_info_list } = await CreateOfflinePackCoursewareFileList({ courseware_id });
+      this.downloadTotal += file_info_list.length;
+
+      file_info_list.forEach((fileInfo) => {
+        const { file_name, file_url, dir_name } = fileInfo;
+        window.fileAPI.downloadFile(file_url, `${this.tempDir}\\${dir_name}\\${file_name}`).then(() => {
+          this.downloadCompleted += 1;
+        });
+      });
+    },
+
+    /**
+     * 开始压缩
+     */
+    async startCompress() {
+      if (this.downloadWatcher) {
+        this.downloadWatcher = null;
+      }
+      const sources = [`${this.tempDir}\\resource`, `${this.tempDir}\\courseware`]; // 要压缩的文件或文件夹路径数组
+      this.file_info_list.forEach(({ dir_name, file_name }) => {
+        const dirPath = dir_name.length > 0 ? `${this.tempDir}\\${dir_name}` : this.tempDir;
+        sources.push(`${dirPath}\\${file_name}`);
+      });
+      const dest = `${this.savePath}\\${this.packageName}.eep`; // 压缩包保存路径
+      try {
+        const offErr = window.fileAPI.onStderr((text) => console.warn('7z stderr:', text));
+
+        await window.fileAPI.compress({
+          sources,
+          dest,
+          format: 'zip',
+          level: 5,
+          password: '1234567a',
+          recurse: true,
+        });
+        offErr();
+      } catch (e) {
+        console.error('压缩失败:', e);
+        this.$message.error('离线包下载失败,请重试!');
+        this.loadingInstance.close();
+        this.loadingInstance = null;
+        this.tempDir = '';
+        return;
+      }
+
+      this.loadingInstance.text = `正在下载离线包,进度:100%`;
+
+      // 删除临时目录及其内容
+      try {
+        await window.fileAPI.deleteTempDir(this.tempDir);
+        this.tempDir = '';
+      } catch (e) {
+        console.error('删除临时目录失败:', e);
+        this.tempDir = '';
+      }
+
+      this.loadingInstance.close();
+      this.loadingInstance = null;
+
+      this.$message.success('离线包下载并保存成功!');
+    },
+
+    /**
+     * 读取文件内容
+     * @param entryName 文件条目名称
+     * @param subdirectory 子目录
+     */
+    async readFileContent(entryName, subdirectory = '') {
+      const content = await window.fileAPI.readZipFileSync(`${this.tempDir}/${subdirectory}`, entryName);
+      const text = new TextDecoder().decode(content);
+      const obj = JSON.parse(text);
+      return obj;
+    },
   },
 };
 </script>