Просмотр исходного кода

Merge branch 'master' of http://60.205.254.193:3000/GCLS/eep_page

zq 3 месяцев назад
Родитель
Сommit
c6b29acdef
26 измененных файлов с 440 добавлено и 260 удалено
  1. 1 1
      .env
  2. 1 1
      packages/package.json
  3. 13 8
      src/api/app.js
  4. 19 0
      src/api/project.js
  5. 7 1
      src/components/ColorPicker.vue
  6. 4 0
      src/components/CoursewareList.vue
  7. BIN
      src/styles/font/pinyin.ttf
  8. 42 27
      src/views/book/courseware/create/components/CreateCanvas.vue
  9. 7 1
      src/views/book/courseware/create/components/question/article/Article.vue
  10. 6 2
      src/views/book/courseware/create/components/question/dialogue_article/Article.vue
  11. 1 1
      src/views/book/courseware/create/components/question/fill/Fill.vue
  12. 1 1
      src/views/book/courseware/create/components/question/image_text/ImageText.vue
  13. 205 194
      src/views/book/courseware/create/components/question/table/Table.vue
  14. 1 1
      src/views/book/courseware/preview/components/article/NormalModelChs.vue
  15. 8 0
      src/views/book/courseware/preview/components/dialogue_article/NormalModelChs.vue
  16. 8 0
      src/views/book/courseware/preview/components/dialogue_article/PhraseModelChs.vue
  17. 4 0
      src/views/book/courseware/preview/components/dialogue_article/Practicechs.vue
  18. 4 0
      src/views/book/courseware/preview/components/dialogue_article/WordModelChs.vue
  19. 13 7
      src/views/home/index.vue
  20. 1 1
      src/views/personal_workbench/courseware_check/index.vue
  21. 1 1
      src/views/personal_workbench/courseware_edit/index.vue
  22. 1 1
      src/views/personal_workbench/courseware_project/index.vue
  23. 20 6
      src/views/personal_workbench/project/ProductionEditorialManage.vue
  24. 2 1
      src/views/personal_workbench/project/components/AddChapter.vue
  25. 6 1
      src/views/project_manage/org/book/index.vue
  26. 64 4
      src/views/project_manage/org/project/index.vue

+ 1 - 1
.env

@@ -11,4 +11,4 @@ VUE_APP_BookWebSI = '/GCLSBookWebSI/ServiceInterface'
 VUE_APP_EepServer = '/EEPServer/SI'
 
 #version
-VUE_APP_VERSION = '2026.07.11'
+VUE_APP_VERSION = '2026.07.14'

+ 1 - 1
packages/package.json

@@ -1,6 +1,6 @@
 {
   "name": "eep-ui",
-  "version": "0.0.18",
+  "version": "0.0.21",
   "main": "eep/eep-ui.umd.js",
   "style": "eep/eep-ui.css",
   "exports": {

+ 13 - 8
src/api/app.js

@@ -62,15 +62,20 @@ export async function fileUpload(
   store?.commit(`app/${app.SET_UPLOAD_CONTROLLER}`, controller);
 
   try {
-    const res = await http.postForm('/FileServer/WebFileUpload', formData, {
-      params: {
-        SecurityLevel,
+    const res = await http.postForm(
+      '/FileServer/WebFileUpload',
+      formData,
+      {
+        params: {
+          SecurityLevel,
+        },
+        signal: controller.signal,
+        transformRequest: [(data) => data],
+        onUploadProgress,
+        timeout: 0,
       },
-      signal: controller.signal,
-      transformRequest: [(data) => data],
-      onUploadProgress,
-      timeout: 0,
-    });
+      true,
+    );
     store?.commit(`app/${app.SET_UPLOAD_INFO}`, { progress: 1 });
     return res;
   } finally {

+ 19 - 0
src/api/project.js

@@ -261,6 +261,15 @@ export function ShangjiaBook(data) {
 }
 
 /**
+ * @description 下架教材
+ * @param {object} data
+ * @param {string} data.project_id 项目ID
+ */
+export function XiajiaBook(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-XiajiaBook`, data);
+}
+
+/**
  * @description 驳回上架教材申请
  * @param {object} data
  * @param {string} data.project_id 项目ID
@@ -401,3 +410,13 @@ export function GetColorList_RecentlyUsed(data) {
     data,
   );
 }
+
+/**
+ * @description 保存颜色(最近使用)
+ * @param {object} data
+ * @param {number} data.type 类型 1:背景色 2:文字颜色 3.主题色
+ * @param {string} data.color 颜色值 "FAF9DE"
+ */
+export function SaveColor_RecentlyUsed(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_resource_manager-SaveColor_RecentlyUsed`, data);
+}

+ 7 - 1
src/components/ColorPicker.vue

@@ -4,11 +4,12 @@
     popper-class="color-picker has-history"
     v-bind="$attrs"
     v-on="$listeners"
+    @change="saveColorRecentlyUsed($event)"
   />
 </template>
 
 <script>
-import { GetColorList_Common, GetColorList_RecentlyUsed } from '@/api/project';
+import { GetColorList_Common, GetColorList_RecentlyUsed, SaveColor_RecentlyUsed } from '@/api/project';
 
 export default {
   name: 'ColorPicker',
@@ -49,6 +50,11 @@ export default {
         this.recentlyUsedList = color_list;
       });
     },
+    saveColorRecentlyUsed(color) {
+      SaveColor_RecentlyUsed({ type: this.type, color: color.replace('#', '') }).then(() => {
+        this.getColorList_RecentlyUsed();
+      });
+    },
   },
 };
 </script>

+ 4 - 0
src/components/CoursewareList.vue

@@ -254,10 +254,14 @@ export default {
       }
 
       .card-name {
+        width: 245px;
         margin-bottom: 12px;
+        overflow: hidden;
         font-size: 15px;
         font-weight: 600;
         color: #1f2937;
+        text-overflow: ellipsis;
+        white-space: nowrap;
       }
 
       .card-meta {

BIN
src/styles/font/pinyin.ttf


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

@@ -1246,16 +1246,15 @@ export default {
           this.calculateGridObject();
         }
       }
-      this.curType = '';
-      this.curParams = {};
-      this.enterCanvas = false;
+      this.resetMoveData();
     },
     /**
      * 处理组件移动
      * 组件拖拽移动后,先删除组件,再根据拖拽位置计算插入行、列、格子
      */
     async handleComponentMove() {
-      const component = this.findChildComponentByKey(`grid-${this.curParams.id}`);
+      const oldID = this.curParams?.id;
+      const component = this.findChildComponentByKey(`grid-${oldID}`);
       // 获取组件内容,保存后再插入到新的位置
       const requestData = component.getComponentRequestContent();
 
@@ -1276,7 +1275,9 @@ export default {
         grid,
         rowNum,
       };
-      this.deleteComponent(this.curParams.id);
+
+      // 根据 oldID 改变 data.row_list 中,将对应ID 改为新ID,并返回新ID,在删除时使用
+      const deleteID = this.replaceGridIdByOldId(oldID);
 
       await ContentSaveCoursewareComponentContent(requestData);
 
@@ -1285,38 +1286,49 @@ export default {
       }
 
       if (this.curRow >= -1 && this.curCol > -1 && this.curGrid <= -1) {
-        // 当拖拽组件和放置位置在同一列内,且行内组件数量为 1,将 curRow 减 1,才能正确插入
-        if ('col' in this.curParams) {
-          const { row, rowNum } = this.curParams;
-          if (row === this.curRow && rowNum === 1) {
-            this.curRow = Math.max(0, this.curRow - 1);
-            this.calculateRowInsertedObject();
-            return;
-          }
-        }
         this.calculateColObject();
       }
 
       if (this.curRow >= -1 && this.curCol > -1 && this.curGrid > -1) {
-        if ('col' in this.curParams) {
-          const { col, row, grid } = this.curParams;
+        this.calculateGridObject();
+      }
 
-          // 当拖拽组件和放置位置在同一列内,将 curGrid 减 1,对应上面的删除组件,才能正确插入
-          if (col === this.curCol && row === this.curRow && grid < this.curGrid) {
-            this.curGrid = Math.max(0, this.curGrid - 1);
-          }
+      this.$nextTick(() => {
+        this.deleteComponent(deleteID);
+      });
 
-          // 当拖拽组件和放置位置在同一列内,且行内组件数量为 1,将 curRow 减 1,才能正确插入
-          if (col === this.curCol && row === this.curRow && grid === this.curGrid) {
-            this.curRow = Math.max(0, this.curRow - 1);
-            this.calculateRowInsertedObject();
-            return;
+      this.resetMoveData();
+    },
+    /**
+     * 根据旧组件 id 将 row_list 中对应 grid 的 id 替换为新 id,并返回新 id
+     * @param {String} oldID 旧组件 id
+     * @returns {String} 新组件 id
+     */
+    replaceGridIdByOldId(oldID) {
+      if (!oldID) return oldID;
+
+      const allIds = this.data.row_list.flatMap((row) =>
+        row.col_list.flatMap((col) => col.grid_list.map((grid) => grid.id)),
+      );
+
+      let newID = `ID-${getRandomNumber(12, true)}`;
+      while (allIds.includes(newID)) {
+        newID = `ID-${getRandomNumber(12, true)}`;
+      }
+
+      for (const row of this.data.row_list) {
+        for (const col of row.col_list) {
+          const targetGrid = col.grid_list.find((grid) => grid.id === oldID);
+          if (targetGrid) {
+            targetGrid.id = newID;
+            return newID;
           }
         }
-
-        this.calculateGridObject();
       }
 
+      return oldID;
+    },
+    resetMoveData() {
       this.curType = '';
       this.curParams = {};
       this.enterCanvas = false;
@@ -1401,6 +1413,7 @@ export default {
         });
         grid[this.curGrid].width = `${w}fr`;
       }
+      this.resetMoveData();
     },
     /**
      * 计算列插入的对象
@@ -1438,6 +1451,7 @@ export default {
           },
         ],
       });
+      this.resetMoveData();
     },
     /**
      * 计算行插入的对象
@@ -1470,6 +1484,7 @@ export default {
           },
         ],
       });
+      this.resetMoveData();
     },
     /**
      * 获取拖拽元素和画布的边距差值

+ 7 - 1
src/views/book/courseware/create/components/question/article/Article.vue

@@ -568,6 +568,8 @@ export default {
             .then(() => {
               this.handleResetSet();
               this.data.notes_list.option = [];
+              this.isWordTime = false;
+              this.data.wordTime = [];
             })
             .catch(() => {});
         }
@@ -614,6 +616,7 @@ export default {
       this.data.detail = [];
       let contentArr = this.data.content.split('\n');
       let textList = [];
+      let contentStr = '';
       let detailItem = {
         paraIndex: 0,
         para: '',
@@ -654,10 +657,11 @@ export default {
 
           let str = Base64.encode(item);
           textList.push(str);
+          contentStr += item.trim() + '\n';
         }
       });
       BatchSegContent({
-        text: this.data.content,
+        text: contentStr,
         is_build_pinyin: 'true',
         is_custom_fc: 'false',
         is_vocabulary_pinyin_lt: 'true',
@@ -786,6 +790,8 @@ export default {
         .then(() => {
           this.data.mp3_list = [];
           this.data.file_id_list = [];
+          this.isWordTime = false;
+          this.data.wordTime = [];
         })
         .catch(() => {});
     },

+ 6 - 2
src/views/book/courseware/create/components/question/dialogue_article/Article.vue

@@ -976,6 +976,8 @@ export default {
           .then(() => {
             this.handleResetSet();
             this.data.notes_list.option = [];
+            this.isWordTime = false;
+            this.data.wordTime = [];
           })
           .catch(() => {});
       }
@@ -987,9 +989,9 @@ export default {
       this.data.detail.forEach((item, index) => {
         item.paraIndex = index;
         if (item.type === 'text') {
-          textList += `${item.para}\n`;
+          textList += `${item.para.trim()}\n`;
         } else if (item.type === 'notice') {
-          textList += `${item.notice}\n`;
+          textList += `${item.notice.trim()}\n`;
         }
       });
 
@@ -1194,6 +1196,8 @@ export default {
       })
         .then(() => {
           this.data.mp3_list = [];
+          this.isWordTime = false;
+          this.data.wordTime = [];
         })
         .catch(() => {});
     },

+ 1 - 1
src/views/book/courseware/create/components/question/fill/Fill.vue

@@ -9,7 +9,7 @@
           ref="richText"
           v-model="data.content"
           :is-fill="true"
-          toolbar="fontselect fontsizeselect forecolor backcolor | lineheight paragraphSpacing underline | bold italic strikethrough alignleft aligncenter alignright bullist numlist dotEmphasis"
+          toolbar="fontselect fontsizeselect forecolor backcolor | lineheight paragraphSpacing fullwidthspace underline | bold italic strikethrough alignleft aligncenter alignright bullist numlist dotEmphasis"
           :wordlimit-num="false"
           :font-size="data?.unified_attrib?.font_size"
           :font-family="data?.unified_attrib?.font"

+ 1 - 1
src/views/book/courseware/create/components/question/image_text/ImageText.vue

@@ -840,7 +840,7 @@ export default {
       let textList = '';
       this.data.text_list.forEach((item, index) => {
         item.paraIndex = index;
-        textList += `${item.text}\n`;
+        textList += `${item.text.trim()}\n`;
       });
 
       BatchSegContent({

+ 205 - 194
src/views/book/courseware/create/components/question/table/Table.vue

@@ -10,204 +10,211 @@
           >{{ label }}</a
         >
       </div> -->
-      <div class="table-rich-toolbar">
-        <el-select
-          v-model="data.styles.fontFamily"
-          placeholder="请选择"
-          style="width: 130px"
-          @change="BatchSetFormat('fontFamily', data.styles.fontFamily)"
-        >
-          <el-option v-for="{ value, label } in fontFamilyList" :key="value" :label="label" :value="value" />
-        </el-select>
-        <el-select
-          v-model="data.styles.fontSize"
-          placeholder="请选择"
-          style="width: 130px"
-          @change="BatchSetFormat('fontSize', data.styles.fontSize)"
-        >
-          <el-option
-            v-for="(value, index) in 16"
-            :key="index"
-            :label="6 + value * 2 + 'pt'"
-            :value="6 + value * 2 + 'pt'"
-          />
-        </el-select>
-        <el-form :model="property" inline>
-          <el-form-item label="文字颜色">
-            <el-color-picker v-model="data.styles.fontColor" @change="BatchSetFormat('color', data.styles.fontColor)" />
-          </el-form-item>
-          <el-form-item label="背景色">
-            <el-color-picker v-model="data.styles.bgColor" />
-          </el-form-item>
-        </el-form>
-        <span
-          :class="[data.styles.isUnderline ? 'active' : '']"
-          @click="
-            data.styles.isUnderline = !data.styles.isUnderline;
-            BatchSetFormat('underline');
-          "
-        >
-          <SvgIcon icon-class="underline" size="20" />
-        </span>
-        <span
-          :class="[data.styles.isBold ? 'active' : '']"
-          @click="
-            data.styles.isBold = !data.styles.isBold;
-            BatchSetFormat('bold');
-          "
-        >
-          <SvgIcon icon-class="font-bold" size="20" />
-        </span>
-        <span
-          :class="[data.styles.isItalic ? 'active' : '']"
-          @click="
-            data.styles.isItalic = !data.styles.isItalic;
-            BatchSetFormat('italic');
-          "
-        >
-          <SvgIcon icon-class="font-italic" size="20" />
-        </span>
-        <span
-          :class="[data.styles.isStrikethrough ? 'active' : '']"
-          @click="
-            data.styles.isStrikethrough = !data.styles.isStrikethrough;
-            BatchSetFormat('strikethrough');
-          "
-        >
-          <SvgIcon icon-class="strikethrough" size="20" />
-        </span>
-        <span
-          :class="[data.styles.textAlign === 'LEFT' ? 'active' : '']"
-          @click="
-            data.styles.textAlign = 'LEFT';
-            BatchSetFormat('align', 'LEFT');
-          "
-        >
-          <SvgIcon icon-class="align-left" size="20" />
-        </span>
-        <span
-          :class="[data.styles.textAlign === 'MIDDLE' ? 'active' : '']"
-          @click="
-            data.styles.textAlign = 'MIDDLE';
-            BatchSetFormat('align', 'MIDDLE');
-          "
-        >
-          <SvgIcon icon-class="align-center" size="20" />
-        </span>
-        <span
-          :class="[data.styles.textAlign === 'RIGHT' ? 'active' : '']"
-          @click="
-            data.styles.textAlign = 'RIGHT';
-            BatchSetFormat('align', 'RIGHT');
-          "
-        >
-          <SvgIcon icon-class="align-right" size="20" />
-        </span>
-      </div>
-      <div class="option-list">
-        <div v-for="(item, i) in data.option_list" :key="i" class="table-node">
-          <el-color-picker v-model="data.rows_bg_list[i]" />
-          <div v-for="(li, j) in item" :key="li.mark" class="table-item">
-            <!-- eslint-disable max-len -->
-            <RichText
-              v-if="property.isGetContent"
-              ref="richText"
-              v-model="li.content"
-              :font-size="data?.unified_attrib?.font_size"
-              :font-family="data?.unified_attrib?.font"
-              :font-color="data?.unified_attrib?.text_color"
-              :inline="true"
-              :item-index="i + '#' + j"
-              toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright image media link"
-              @handleRichTextBlur="handleBlurCon"
+      <div v-loading="loading">
+        <div class="table-rich-toolbar">
+          <el-select
+            v-model="data.styles.fontFamily"
+            placeholder="请选择"
+            style="width: 130px"
+            @change="BatchSetFormat('fontFamily', data.styles.fontFamily)"
+          >
+            <el-option v-for="{ value, label } in fontFamilyList" :key="value" :label="label" :value="value" />
+          </el-select>
+          <el-select
+            v-model="data.styles.fontSize"
+            placeholder="请选择"
+            style="width: 130px"
+            @change="BatchSetFormat('fontSize', data.styles.fontSize)"
+          >
+            <el-option
+              v-for="(value, index) in 16"
+              :key="index"
+              :label="6 + value * 2 + 'pt'"
+              :value="6 + value * 2 + 'pt'"
             />
-            <el-input v-else v-model="li.content" @blur="handleBlurCon" />
-            <el-button size="mini" @click="editCell(li.cell)">设置</el-button>
-          </div>
-        </div>
-        <div class="table-node">
-          <div style="flex-shrink: 0; width: 32px"></div>
-          <div v-for="(value, index) in data.col_width" :key="index" class="table-item table-items">
-            <el-color-picker v-model="data.cols_bg_list[index]" />
-            <el-input v-model="value.value">
-              <template slot="prepend">列宽</template><template slot="append">%</template></el-input
-            >
-          </div>
+          </el-select>
+          <el-form :model="property" inline>
+            <el-form-item label="文字颜色">
+              <el-color-picker
+                v-model="data.styles.fontColor"
+                @change="BatchSetFormat('color', data.styles.fontColor)"
+              />
+            </el-form-item>
+            <el-form-item label="背景色">
+              <el-color-picker v-model="data.styles.bgColor" />
+            </el-form-item>
+          </el-form>
+          <span
+            :class="[data.styles.isUnderline ? 'active' : '']"
+            @click="
+              data.styles.isUnderline = !data.styles.isUnderline;
+              BatchSetFormat('underline');
+            "
+          >
+            <SvgIcon icon-class="underline" size="20" />
+          </span>
+          <span
+            :class="[data.styles.isBold ? 'active' : '']"
+            @click="
+              data.styles.isBold = !data.styles.isBold;
+              BatchSetFormat('bold');
+            "
+          >
+            <SvgIcon icon-class="font-bold" size="20" />
+          </span>
+          <span
+            :class="[data.styles.isItalic ? 'active' : '']"
+            @click="
+              data.styles.isItalic = !data.styles.isItalic;
+              BatchSetFormat('italic');
+            "
+          >
+            <SvgIcon icon-class="font-italic" size="20" />
+          </span>
+          <span
+            :class="[data.styles.isStrikethrough ? 'active' : '']"
+            @click="
+              data.styles.isStrikethrough = !data.styles.isStrikethrough;
+              BatchSetFormat('strikethrough');
+            "
+          >
+            <SvgIcon icon-class="strikethrough" size="20" />
+          </span>
+          <span
+            :class="[data.styles.textAlign === 'LEFT' ? 'active' : '']"
+            @click="
+              data.styles.textAlign = 'LEFT';
+              BatchSetFormat('align', 'LEFT');
+            "
+          >
+            <SvgIcon icon-class="align-left" size="20" />
+          </span>
+          <span
+            :class="[data.styles.textAlign === 'MIDDLE' ? 'active' : '']"
+            @click="
+              data.styles.textAlign = 'MIDDLE';
+              BatchSetFormat('align', 'MIDDLE');
+            "
+          >
+            <SvgIcon icon-class="align-center" size="20" />
+          </span>
+          <span
+            :class="[data.styles.textAlign === 'RIGHT' ? 'active' : '']"
+            @click="
+              data.styles.textAlign = 'RIGHT';
+              BatchSetFormat('align', 'RIGHT');
+            "
+          >
+            <SvgIcon icon-class="align-right" size="20" />
+          </span>
         </div>
-      </div>
-      <el-input
-        v-if="data.property.fill_type === fillTypeList[1].value"
-        v-model="data.vocabulary"
-        type="textarea"
-        :autosize="{ minRows: 2, maxRows: 4 }"
-        resize="none"
-        placeholder="请输入词汇,用于选词填空"
-      />
-      <p class="tips">在需要作答的单元格内输入三个以上下划线“___”</p>
-      <el-button @click="identifyText()">识别</el-button>
-      <el-button @click="handleMultilingual">多语言</el-button>
-      <template v-if="isEnable(data.has_identify)">
-        <p class="tips">在需要作答的单元格内录入标准答案,多个填空答案用换行录入,同一个填空有多个答案用斜线“/”隔开</p>
         <div class="option-list">
-          <div v-for="(item, i) in data.answer_lists" :key="i" class="table-node">
-            <div v-for="(li, j) in item" :key="i + 'col' + j" class="table-item">
-              <el-input v-model="li.answer" type="textarea" :readonly="!data.option_list[i][j].hasInput" />
-              <span
-                v-if="data.option_list[i][j].cell.isCross"
-                style="display: flex; align-items: center; font-size: 14px"
-                >勾叉答案<i
-                  :class="[
-                    { 'el-icon-check': li.crossAnswer === statusList[1] },
-                    { 'el-icon-close': li.crossAnswer === statusList[2] },
-                  ]"
-                  style="display: block; width: 14px; height: 14px; border: 1px solid #000"
-                  @click="toggle(li)"
-                ></i
-              ></span>
+          <div v-for="(item, i) in data.option_list" :key="i" class="table-node">
+            <el-color-picker v-model="data.rows_bg_list[i]" />
+            <div v-for="(li, j) in item" :key="li.mark" class="table-item">
+              <!-- eslint-disable max-len -->
+              <RichText
+                v-if="property.isGetContent"
+                ref="richText"
+                v-model="li.content"
+                :font-size="data?.unified_attrib?.font_size"
+                :font-family="data?.unified_attrib?.font"
+                :font-color="data?.unified_attrib?.text_color"
+                :inline="true"
+                :item-index="i + '#' + j"
+                toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright image media link"
+                @handleRichTextBlur="handleBlurCon"
+              />
+              <el-input v-else v-model="li.content" @blur="handleBlurCon" />
+              <el-button size="mini" @click="editCell(li.cell)">设置</el-button>
+            </div>
+          </div>
+          <div class="table-node">
+            <div style="flex-shrink: 0; width: 32px"></div>
+            <div v-for="(value, index) in data.col_width" :key="index" class="table-item table-items">
+              <el-color-picker v-model="data.cols_bg_list[index]" />
+              <el-input v-model="value.value">
+                <template slot="prepend">列宽</template><template slot="append">%</template></el-input
+              >
             </div>
           </div>
         </div>
-      </template>
-      <AnswerAnalysisList
-        v-if="data.answer_list?.length > 0 || data.analysis_list?.length > 0"
-        :answer-list="data.answer_list"
-        :analysis-list="data.analysis_list"
-        :unified-attrib="data.unified_attrib"
-        @updateAnswerAnalysisFileList="updateAnswerAnalysisFileList"
-        @deleteAnswerAnalysis="deleteAnswerAnalysis"
-      />
-      <MultilingualFill
-        :visible.sync="multilingualVisible"
-        :text="multilingualText"
-        :translations="data.multilingual"
-        @SubmitTranslation="handleMultilingualTranslation"
-      />
-      <el-divider v-if="isEnable(data.property.view_pinyin)" content-position="left">
-        拼音效果
-        <el-button
-          v-show="isEnable(data.property.view_pinyin)"
-          type="text"
-          icon="el-icon-refresh"
-          title="刷新"
-          class="refresh-pinyin-btn"
-          @click.native="identifyText()"
-      /></el-divider>
-      <template v-if="isEnable(data.property.view_pinyin)">
-        <template v-for="(item, index) in data.option_list">
-          <PinyinText
-            v-for="(items, indexs) in item"
-            :id="'table_pinyin_text_' + index + '_' + indexs"
-            :key="index + '_' + indexs"
-            ref="PinyinText"
-            :rich-text-list="items.rich_text_list"
-            :pinyin-position="data.property.pinyin_position"
-            :pinyin-size="data?.unified_attrib?.pinyin_size"
-            :font-size="data?.unified_attrib?.font_size"
-            :font-family="data?.unified_attrib?.font"
-            @fillCorrectPinyin="fillCorrectPinyin"
-          />
+        <el-input
+          v-if="data.property.fill_type === fillTypeList[1].value"
+          v-model="data.vocabulary"
+          type="textarea"
+          :autosize="{ minRows: 2, maxRows: 4 }"
+          resize="none"
+          placeholder="请输入词汇,用于选词填空"
+        />
+        <p class="tips">在需要作答的单元格内输入三个以上下划线“___”</p>
+        <el-button @click="identifyText()">识别</el-button>
+        <el-button @click="handleMultilingual">多语言</el-button>
+        <template v-if="isEnable(data.has_identify)">
+          <p class="tips">
+            在需要作答的单元格内录入标准答案,多个填空答案用换行录入,同一个填空有多个答案用斜线“/”隔开
+          </p>
+          <div class="option-list">
+            <div v-for="(item, i) in data.answer_lists" :key="i" class="table-node">
+              <div v-for="(li, j) in item" :key="i + 'col' + j" class="table-item">
+                <el-input v-model="li.answer" type="textarea" :readonly="!data.option_list[i][j].hasInput" />
+                <span
+                  v-if="data.option_list[i][j].cell.isCross"
+                  style="display: flex; align-items: center; font-size: 14px"
+                  >勾叉答案<i
+                    :class="[
+                      { 'el-icon-check': li.crossAnswer === statusList[1] },
+                      { 'el-icon-close': li.crossAnswer === statusList[2] },
+                    ]"
+                    style="display: block; width: 14px; height: 14px; border: 1px solid #000"
+                    @click="toggle(li)"
+                  ></i
+                ></span>
+              </div>
+            </div>
+          </div>
         </template>
-      </template>
+        <AnswerAnalysisList
+          v-if="data.answer_list?.length > 0 || data.analysis_list?.length > 0"
+          :answer-list="data.answer_list"
+          :analysis-list="data.analysis_list"
+          :unified-attrib="data.unified_attrib"
+          @updateAnswerAnalysisFileList="updateAnswerAnalysisFileList"
+          @deleteAnswerAnalysis="deleteAnswerAnalysis"
+        />
+        <MultilingualFill
+          :visible.sync="multilingualVisible"
+          :text="multilingualText"
+          :translations="data.multilingual"
+          @SubmitTranslation="handleMultilingualTranslation"
+        />
+        <el-divider v-if="isEnable(data.property.view_pinyin)" content-position="left">
+          拼音效果
+          <el-button
+            v-show="isEnable(data.property.view_pinyin)"
+            type="text"
+            icon="el-icon-refresh"
+            title="刷新"
+            class="refresh-pinyin-btn"
+            @click.native="identifyText()"
+        /></el-divider>
+        <template v-if="isEnable(data.property.view_pinyin)">
+          <template v-for="(item, index) in data.option_list">
+            <PinyinText
+              v-for="(items, indexs) in item"
+              :id="'table_pinyin_text_' + index + '_' + indexs"
+              :key="index + '_' + indexs"
+              ref="PinyinText"
+              :rich-text-list="items.rich_text_list"
+              :pinyin-position="data.property.pinyin_position"
+              :pinyin-size="data?.unified_attrib?.pinyin_size"
+              :font-size="data?.unified_attrib?.font_size"
+              :font-family="data?.unified_attrib?.font"
+              @fillCorrectPinyin="fillCorrectPinyin"
+            />
+          </template>
+        </template>
+      </div>
       <el-dialog
         v-if="editCellFlag"
         title="设置单元格"
@@ -276,6 +283,7 @@ export default {
       editCellFlag: false,
       statusList: ['normal', 'tick', 'cross'],
       editContentIndex: '', // 编辑内容的单元格索引
+      loading: false,
     };
   },
   watch: {
@@ -470,14 +478,14 @@ export default {
         } else {
           this.data.option_list.forEach((item, index) => {
             item.forEach((items, indexs) => {
-              this.createParsedTextInfoPinyin(items.content, `${index}#${indexs}`);
+              this.createParsedTextInfoPinyin(items.content, `${index}#${indexs}`, true);
             });
           });
         }
       }
     },
     // 获取拼音解析文本
-    createParsedTextInfoPinyin(text, editIndex) {
+    createParsedTextInfoPinyin(text, editIndex, flag) {
       if (text === '') {
         this.data.paragraph_list_parameter.pinyin_proofread_word_list = [];
         return;
@@ -487,7 +495,11 @@ export default {
       this.data.paragraph_list_parameter.is_first_sentence_first_hz_pinyin_first_char_upper_case =
         this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case;
       this.data.paragraph_list_parameter.is_rich_text = 'true';
+      if (flag) this.loading = true;
       PinyinBuild_OldFormat(this.data.paragraph_list_parameter).then((res) => {
+        if (flag) {
+          this.loading = false;
+        }
         if (res.parsed_text) {
           const mergedData = res.parsed_text.paragraph_list.map((outerArr, i) =>
             outerArr.map((innerArr, j) =>
@@ -568,7 +580,6 @@ export default {
           }
         }
       });
-      console.log(this.data);
     },
     // 填充校对后的拼音
     fillCorrectPinyin({ selectContent: { text, pinyin, activeTextStyle }, i, j, k }) {

+ 1 - 1
src/views/book/courseware/preview/components/article/NormalModelChs.vue

@@ -1825,7 +1825,7 @@ export default {
       resArr: [],
       resObj: null,
       curTime: 0, // 单位s
-      chsFhList: [',', '。', '”', ':', '》', '?', '!', ';', '#', '、', '('],
+      chsFhList: [',', '。', '”', ':', '》', '?', '!', ';', '#', '、'],
       enFhList: [',', '.', ';', '?', '!', ':', '>', '<'],
       firstFhlist: ['(', '“'],
       NumberList: ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬', '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳'],

+ 8 - 0
src/views/book/courseware/preview/components/dialogue_article/NormalModelChs.vue

@@ -281,6 +281,7 @@
                                           attrib && attrib.font_size
                                             ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                             : '28px',
+                                        whiteSpace: pItem.chs[wIndex].trim() === '' ? 'pre' : '', // 保留空白分词
                                       }"
                                       @click.stop="
                                         viewNotes(
@@ -354,6 +355,7 @@
                                           attrib && attrib.font_size
                                             ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                             : '28px',
+                                        whiteSpace: pItem.chs[wIndex].trim() === '' ? 'pre' : '', // 保留空白分词
                                       }"
                                       @click.stop="
                                         viewNotes(
@@ -486,6 +488,7 @@
                                       attrib && attrib.font_size
                                         ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                         : '28px',
+                                    whiteSpace: item.wordsList[pIndex + 1].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="
                                     viewNotes(
@@ -546,6 +549,7 @@
                                       attrib && attrib.font_size
                                         ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                         : '28px',
+                                    whiteSpace: item.wordsList[pIndex + 1].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="
                                     viewNotes(
@@ -688,6 +692,7 @@
                                       attrib && attrib.font_size
                                         ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                         : '28px',
+                                    whiteSpace: item.wordsList[pIndex + 2].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="
                                     viewNotes(
@@ -748,6 +753,7 @@
                                       attrib && attrib.font_size
                                         ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                         : '28px',
+                                    whiteSpace: item.wordsList[pIndex + 2].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="
                                     viewNotes(
@@ -895,6 +901,7 @@
                                         attrib && attrib.font_size
                                           ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                           : '28px',
+                                      whiteSpace: pItem.chs[wIndex].trim() === '' ? 'pre' : '', // 保留空白分词
                                     }"
                                     @click.stop="
                                       viewNotes(
@@ -966,6 +973,7 @@
                                         attrib && attrib.font_size
                                           ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                           : '28px',
+                                      whiteSpace: pItem.chs[wIndex].trim() === '' ? 'pre' : '', // 保留空白分词
                                     }"
                                     @click.stop="
                                       viewNotes(

+ 8 - 0
src/views/book/courseware/preview/components/dialogue_article/PhraseModelChs.vue

@@ -246,6 +246,7 @@
                                         : pItem.matchNotesObj.con && pItem.matchNotesObj.notesColor
                                           ? pItem.matchNotesObj.notesColor
                                           : pItem.config.color,
+                                    whiteSpace: pItem.chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="viewNotes($event, pItem.words ? pItem.words : pItem.chs, pItem)"
                                   >{{ convertText(pItem.chs) }}</span
@@ -280,6 +281,7 @@
                                         : pItem.matchNotesObj.con && pItem.matchNotesObj.notesColor
                                           ? pItem.matchNotesObj.notesColor
                                           : pItem.config.color,
+                                    whiteSpace: pItem.chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="viewNotes($event, pItem.words ? pItem.words : pItem.chs, pItem)"
                                   >{{ convertText(pItem.chs) }}</span
@@ -372,6 +374,7 @@
                                             item.wordsList[pIndex + 1].matchNotesObj.notesColor
                                           ? item.wordsList[pIndex + 1].matchNotesObj.notesColor
                                           : item.wordsList[pIndex + 1].config.color,
+                                    whiteSpace: item.wordsList[pIndex + 1].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="
                                     viewNotes(
@@ -418,6 +421,7 @@
                                             item.wordsList[pIndex + 1].matchNotesObj.notesColor
                                           ? item.wordsList[pIndex + 1].matchNotesObj.notesColor
                                           : item.wordsList[pIndex + 1].config.color,
+                                    whiteSpace: item.wordsList[pIndex + 1].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="
                                     viewNotes(
@@ -539,6 +543,7 @@
                                             item.wordsList[pIndex + 2].matchNotesObj.notesColor
                                           ? item.wordsList[pIndex + 2].matchNotesObj.notesColor
                                           : item.wordsList[pIndex + 2].config.color,
+                                    whiteSpace: item.wordsList[pIndex + 2].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="
                                     viewNotes(
@@ -592,6 +597,7 @@
                                             item.wordsList[pIndex + 2].matchNotesObj.notesColor
                                           ? item.wordsList[pIndex + 2].matchNotesObj.notesColor
                                           : item.wordsList[pIndex + 2].config.color,
+                                    whiteSpace: item.wordsList[pIndex + 2].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   @click.stop="
                                     viewNotes(
@@ -694,6 +700,7 @@
                                       : pItem.matchNotesObj.con && pItem.matchNotesObj.notesColor
                                         ? pItem.matchNotesObj.notesColor
                                         : pItem.config.color,
+                                  whiteSpace: pItem.chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                 }"
                                 @click.stop="viewNotes($event, pItem.words ? pItem.words : pItem.chs, pItem)"
                                 >{{ convertText(pItem.chs) }}</span
@@ -730,6 +737,7 @@
                                       : pItem.matchNotesObj.con && pItem.matchNotesObj.notesColor
                                         ? pItem.matchNotesObj.notesColor
                                         : pItem.config.color,
+                                  whiteSpace: pItem.chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                 }"
                                 @click.stop="viewNotes($event, pItem.words ? pItem.words : pItem.chs, pItem)"
                                 >{{ convertText(pItem.chs) }}</span

+ 4 - 0
src/views/book/courseware/preview/components/dialogue_article/Practicechs.vue

@@ -278,6 +278,7 @@
                                         attrib
                                           ? attrib.topic_color
                                           : pItem.config.color,
+                                      whiteSpace: pItem.chs[wIndex].trim() === '' ? 'pre' : '', // 保留空白分词
                                     }"
                                     >{{
                                       NumberList.indexOf(pItem.pinyin) == -1 ? convertText(pItem.chs[wIndex]) : ''
@@ -368,6 +369,7 @@
                                       attrib
                                         ? attrib.topic_color
                                         : item.sentArr[pIndex + 1].config.color,
+                                    whiteSpace: item.sentArr[pIndex + 1].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                 >
                                   {{
@@ -471,6 +473,7 @@
                                       attrib
                                         ? attrib.topic_color
                                         : item.sentArr[pIndex + 2].config.color,
+                                    whiteSpace: item.sentArr[pIndex + 2].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                 >
                                   {{
@@ -577,6 +580,7 @@
                                       attrib
                                         ? attrib.topic_color
                                         : pItem.config.color,
+                                    whiteSpace: pItem.chs[wIndex].trim() === '' ? 'pre' : '', // 保留空白分词
                                   }"
                                   >{{
                                     NumberList.indexOf(pItem.chs[wIndex]) == -1 ? convertText(pItem.chs[wIndex]) : ''

+ 4 - 0
src/views/book/courseware/preview/components/dialogue_article/WordModelChs.vue

@@ -213,6 +213,7 @@
                                     attrib
                                       ? attrib.topic_color
                                       : '',
+                                  whiteSpace: pItem.chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                 }"
                                 >{{ NumberList.indexOf(pItem.pinyin) == -1 ? convertText(pItem.chs) : '' }}</span
                               >
@@ -267,6 +268,7 @@
                                     attrib && attrib.font_size
                                       ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                       : '28px',
+                                  whiteSpace: item.wordsList[pIndex + 1].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                 }"
                                 >{{
                                   NumberList.indexOf(item.wordsList[pIndex + 1].pinyin) == -1
@@ -343,6 +345,7 @@
                                     attrib && attrib.font_size
                                       ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                       : '28px',
+                                  whiteSpace: item.wordsList[pIndex + 2].chs.trim() === '' ? 'pre' : '', // 保留空白分词
                                 }"
                                 >{{
                                   NumberList.indexOf(item.wordsList[pIndex + 2].pinyin) == -1
@@ -416,6 +419,7 @@
                                   attrib
                                     ? attrib.topic_color
                                     : '',
+                                whiteSpace: pItem.chs.trim() === '' ? 'pre' : '', // 保留空白分词
                               }"
                               @click="showWordDetail($event, pItem)"
                               >{{ NumberList.indexOf(pItem.pinyin) == -1 ? convertText(pItem.chs) : '' }}</span

+ 13 - 7
src/views/home/index.vue

@@ -33,7 +33,7 @@
         </div>
       </div>
 
-      <div class="list-section">
+      <div v-loading="loading" class="list-section">
         <el-table
           v-if="currentOverview === overviewList[0].key"
           :key="overviewList[0].key"
@@ -366,6 +366,7 @@ export default {
       cur_page: 1,
       projectList: [], // 项目列表
       toolModalVisible: false,
+      loading: false, // 列表加载状态
     };
   },
   created() {
@@ -387,12 +388,17 @@ export default {
         cur_page: this.cur_page,
         type: this.overviewList.find((item) => item.key === this.currentOverview)?.type,
       };
-      PageQueryMyProjectList({ ...params, ...data }).then(({ project_list, total_count, cur_page }) => {
-        this.projectList = project_list;
-        this.total = total_count;
-        this.page_capacity = data?.page_capacity || this.page_capacity;
-        this.cur_page = cur_page;
-      });
+      this.loading = true;
+      PageQueryMyProjectList({ ...params, ...data })
+        .then(({ project_list, total_count, cur_page }) => {
+          this.projectList = project_list;
+          this.total = total_count;
+          this.page_capacity = data?.page_capacity || this.page_capacity;
+          this.cur_page = cur_page;
+        })
+        .finally(() => {
+          this.loading = false;
+        });
     },
     /**
      * @description 切换概览后重置页码并获取列表数据

+ 1 - 1
src/views/personal_workbench/courseware_check/index.vue

@@ -12,7 +12,7 @@
           <img v-if="item.cover_iamge_file_url.length > 0" :src="item.cover_iamge_file_url" alt="封面" />
         </div>
         <div class="card-right" @click="jumpToDetail(item.id)">
-          <div class="card-name">{{ item.name || '-' }}</div>
+          <div class="card-name" :title="item.name">{{ item.name || '-' }}</div>
           <div class="card-meta">
             <div class="meta-row">
               <span class="meta-label">项目组长:</span>

+ 1 - 1
src/views/personal_workbench/courseware_edit/index.vue

@@ -12,7 +12,7 @@
           <img v-if="item.cover_iamge_file_url.length > 0" :src="item.cover_iamge_file_url" alt="封面" />
         </div>
         <div class="card-right" @click="jumpToDetail(item.id)">
-          <div class="card-name">{{ item.name || '-' }}</div>
+          <div class="card-name" :title="item.name">{{ item.name || '-' }}</div>
           <div class="card-meta">
             <div class="meta-row">
               <span class="meta-label">项目组长:</span>

+ 1 - 1
src/views/personal_workbench/courseware_project/index.vue

@@ -12,7 +12,7 @@
           <img v-if="item.cover_iamge_file_url.length > 0" :src="item.cover_iamge_file_url" alt="封面" />
         </div>
         <div class="card-right" @click="jumpToDetail(item.id)">
-          <div class="card-name">{{ item.name || '-' }}</div>
+          <div class="card-name" :title="item.name">{{ item.name || '-' }}</div>
           <div class="card-meta">
             <div class="meta-row">
               <span class="meta-label">项目组长:</span>

+ 20 - 6
src/views/personal_workbench/project/ProductionEditorialManage.vue

@@ -324,10 +324,17 @@ export default {
       this.curSelectId = id;
     },
     chapterAddCoursewareToBook(data) {
-      ChapterAddCoursewareToBook(data).then(() => {
-        this.getBookChapterStructExpandList();
-        this.visible = false;
+      const loading = this.$loading({
+        text: '正在添加课件,请稍候...',
       });
+      ChapterAddCoursewareToBook(data)
+        .then(() => {
+          this.getBookChapterStructExpandList();
+          this.visible = false;
+        })
+        .finally(() => {
+          loading.close();
+        });
     },
     /**
      * 点击目录外部区域,取消选中
@@ -373,10 +380,17 @@ export default {
      * 添加章节
      */
     addChapterNode(data) {
-      ChapterAddChapterToBook(data).then(() => {
-        this.getBookChapterStructExpandList();
-        this.visible = false;
+      const loading = this.$loading({
+        text: '正在添加章节,请稍候...',
       });
+      ChapterAddChapterToBook(data)
+        .then(() => {
+          this.getBookChapterStructExpandList();
+          this.visible = false;
+        })
+        .finally(() => {
+          loading.close();
+        });
     },
     /**
      * 删除章节(二次确认)

+ 2 - 1
src/views/personal_workbench/project/components/AddChapter.vue

@@ -67,6 +67,8 @@ export default {
           this.$refs.form.clearValidate();
           this.$refs.name.focus();
         });
+      } else {
+        this.data.name = '';
       }
     },
   },
@@ -88,7 +90,6 @@ export default {
           chapter_id: this.curSelectId,
         });
       }
-      this.data.name = '';
     },
   },
 };

+ 6 - 1
src/views/project_manage/org/book/index.vue

@@ -12,11 +12,16 @@
         <el-table-column prop="sn" label="编号" width="140" header-align="center" />
         <el-table-column prop="name" label="名称" width="240" header-align="center" />
         <el-table-column prop="project_sn" label="项目编号" width="140" header-align="center" />
-        <el-table-column prop="project_name" label="项目名称" width="240" header-align="center" />
+        <el-table-column prop="project_name" label="教材名称" width="240" header-align="center" />
         <el-table-column prop="org_name" label="所属机构" width="120" header-align="center" />
         <el-table-column prop="content_count" label="教材页面数" width="100" header-align="center" />
         <el-table-column prop="shangjia_time" label="上架时间" width="180" header-align="center" />
         <el-table-column prop="version" label="版本号" width="90" header-align="center" align="center" />
+        <el-table-column prop="revise_count" label="修订版次" align="center" header-align="center">
+          <template slot-scope="{ row }">
+            {{ row.revise_count === 0 ? `首版` : `第${row.revise_count}次修订` }}
+          </template>
+        </el-table-column>
         <el-table-column label="已申请下架" align="center" header-align="center">
           <template slot-scope="{ row }">
             <span style="color: #f53f3f">

+ 64 - 4
src/views/project_manage/org/project/index.vue

@@ -2,6 +2,24 @@
   <div class="project org">
     <ProjectMenu cur-key="org/project" />
 
+    <div id="query-form">
+      <el-form inline>
+        <el-form-item prop="name" label="名称">
+          <el-input v-model="queryForm.name" clearable @change="pageQueryProjectList_OrgManager()" />
+        </el-form-item>
+        <el-form-item prop="status" label="状态">
+          <el-select v-model="queryForm.status" @change="pageQueryProjectList_OrgManager()">
+            <el-option label="全部" :value="-1" />
+            <el-option label="制作中" :value="0" />
+            <el-option label="审核中" :value="1" />
+            <el-option label="已终审" :value="2" />
+            <el-option label="已上架" :value="3" />
+            <el-option label="已下架" :value="4" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+    </div>
+
     <div class="project-list">
       <el-table :data="list">
         <el-table-column label="序号" width="60" align="center" header-align="center" class-name="index-column">
@@ -16,8 +34,8 @@
         <el-table-column prop="leader_name_desc" label="项目组长" width="140" header-align="center" />
         <el-table-column prop="create_time" label="创建时间" width="180" header-align="center" />
         <el-table-column prop="content_count_SJ" label="教材页面数" width="100" header-align="center" />
-        <el-table-column prop="status_name" label="状态" width="120" header-align="center" />
-        <el-table-column prop="request_status_name" label="申请状态" header-align="center" />
+        <el-table-column prop="status_name" label="状态" align="center" width="120" header-align="center" />
+        <el-table-column prop="request_status_name" label="申请状态" align="center" header-align="center" />
 
         <el-table-column fixed="right" label="操作" width="350" align="center" header-align="center">
           <template slot-scope="{ row }">
@@ -36,6 +54,8 @@
               <span class="link" @click="projectAuditOperate(row.id, 'true', row.request_status)"> 审核通过 </span>
               <span class="link" @click="projectAuditOperate(row.id, 'false', row.request_status)">审核拒绝</span>
             </template>
+            <span v-if="isTrue(row.is_can_shangjia)" class="link" @click="shangjiaProject(row.id)">上架</span>
+            <span v-if="isTrue(row.is_can_xiajia)" class="link" @click="xiajiaProject(row.id)">下架</span>
           </template>
         </el-table-column>
       </el-table>
@@ -51,7 +71,7 @@ import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
 
 import { PageQueryProjectList_OrgManager } from '@/api/list';
 import { isTrue } from '@/utils/validate';
-import { ProjectAuditOperate } from '@/api/project';
+import { ProjectAuditOperate, ShangjiaBook, XiajiaBook } from '@/api/project';
 
 export default {
   name: 'OrgProjectPage',
@@ -65,6 +85,10 @@ export default {
       total: 0,
       cur_page_begin_index: 0,
       isTrue,
+      queryForm: {
+        name: '',
+        status: -1,
+      },
     };
   },
   methods: {
@@ -101,7 +125,13 @@ export default {
         .catch(() => {});
     },
     pageQueryProjectList_OrgManager(data) {
-      PageQueryProjectList_OrgManager({ ...data, status: -1 })
+      const _data = {
+        page_capacity: 10,
+        cur_page: 1,
+        ...data,
+        ...this.queryForm,
+      };
+      PageQueryProjectList_OrgManager(_data)
         .then(({ total_count, cur_page_begin_index, project_list }) => {
           this.total = total_count;
           this.cur_page_begin_index = cur_page_begin_index;
@@ -112,6 +142,36 @@ export default {
     previewProject(projectId) {
       this.$router.push({ path: `/project_manage/org/project/preview/${projectId}` });
     },
+    shangjiaProject(project_id) {
+      this.$confirm('确定要上架该教材吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+      })
+        .then(() => {
+          ShangjiaBook({ project_id })
+            .then(() => {
+              this.$message.success('操作成功');
+              this.$refs.pagination.getList();
+            })
+            .catch(() => {});
+        })
+        .catch(() => {});
+    },
+    xiajiaProject(project_id) {
+      this.$confirm('确定要下架该教材吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+      })
+        .then(() => {
+          XiajiaBook({ project_id })
+            .then(() => {
+              this.$message.success('操作成功');
+              this.$refs.pagination.getList();
+            })
+            .catch(() => {});
+        })
+        .catch(() => {});
+    },
   },
 };
 </script>