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

1、语音矩阵增加字体、字号、文字颜色 2、审核按钮控制 3、删除添加章节按钮

dsy 2 дней назад
Родитель
Сommit
6db80b6324

+ 1 - 1
.env

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

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "eep_page",
-  "version": "2026.04.13",
+  "version": "2026.04.14",
   "private": true,
   "main": "main.js",
   "description": "智慧梧桐数字教材编辑器",

+ 9 - 8
src/components/CommonPreview.vue

@@ -71,7 +71,7 @@
         <!-- 教材章节树 -->
         <div class="courseware-tree">
           <div
-            v-for="{ id: nodeId, name, status_name, deep, is_leaf_chapter } in node_list"
+            v-for="{ id: nodeId, name, status, status_name, deep, is_leaf_chapter, is_my_edit_task } in node_list"
             :key="nodeId"
             :class="['menu-item', { active: curSelectId === nodeId }, { courseware: isTrue(is_leaf_chapter) }]"
             :style="computedNameStyle(deep, isTrue(is_leaf_chapter))"
@@ -80,7 +80,7 @@
             <span
               class="name nowrap-ellipsis"
               :title="name"
-              :style="{ color: computedNameColor(status_name, is_leaf_chapter, nodeId) }"
+              :style="{ color: computedNameColor(status, is_leaf_chapter, is_my_edit_task, nodeId) }"
             >
               {{ name }}
             </span>
@@ -1257,26 +1257,27 @@ export default {
     },
     /**
      * 计算章节名称颜色
-     * @param {string} statusName - 章节状态名称
+     * @param {string} status - 章节状态
      * @param {boolean} is_leaf_chapter - 是否是叶子章节
+     * @param {string} is_my_edit_task - 是否是我的编辑任务
      * @param {string} nodeId - 节点ID
      * @returns {string} - 颜色值
      */
-    computedNameColor(statusName, is_leaf_chapter, nodeId) {
+    computedNameColor(status, is_leaf_chapter, is_my_edit_task, nodeId) {
       if (!isTrue(is_leaf_chapter)) {
         return '';
       }
       // 如果当前选中节点且不可编辑,显示默认色
-      if (this.curSelectId === nodeId && this.courseware_info.is_can_start_edit === 'false') {
+      if (is_my_edit_task === 'false') {
         return '';
       }
-      // 当前选中节点,显示高亮色
+      // 当前选中节点,显示高亮
       if (this.curSelectId === nodeId) {
         return '#4095e5';
       }
-      // 其他节点,根据状态显示不同颜
+      // 审核通过显示绿色,其他显示默认蓝
       let color = '#74b9ff';
-      if (statusName === '审核通过') {
+      if (status === 2) {
         color = '#27ae60';
       }
       return color;

+ 14 - 5
src/components/RichText.vue

@@ -1,7 +1,16 @@
 <template>
   <div ref="richArea" class="rich-wrapper">
-    <Editor v-bind="$attrs" :id="id" ref="richText" model-events="change keyup undo redo setContent" :value="value"
-      :class="['rich-text', isBorder ? 'is-border' : '']" :init="init" v-on="$listeners" @onBlur="handleRichTextBlur" />
+    <Editor
+      v-bind="$attrs"
+      :id="id"
+      ref="richText"
+      model-events="change keyup undo redo setContent"
+      :value="value"
+      :class="['rich-text', isBorder ? 'is-border' : '']"
+      :init="init"
+      v-on="$listeners"
+      @onBlur="handleRichTextBlur"
+    />
     <div v-show="isShow" :style="contentmenu" class="contentmenu">
       <div v-if="isViewNote" @click="openExplanatoryNoteDialog">
         <SvgIcon icon-class="mark" size="14" />
@@ -1049,7 +1058,7 @@ export default {
       let content = this.getRichContent();
 
       if (this.isViewPinyin) {
-        //updated by 2026-3-24 失去焦点时,不再生成拼音,改成手动点击生成拼音
+        // updated by 2026-3-24 失去焦点时,不再生成拼音,改成手动点击生成拼音
         // let styles = this.getFirstCharStyles();
         //  let text = content.replace(/<[^>]+>/g, '');
         //  this.$emit('createParsedTextInfoPinyin', text, styles);
@@ -1320,7 +1329,7 @@ export default {
 
 <style lang="scss" scoped>
 .rich-text {
-  :deep+.tox {
+  :deep + .tox {
     .tox-sidebar-wrap {
       border: 1px solid $fill-color;
       border-radius: 4px;
@@ -1345,7 +1354,7 @@ export default {
   }
 
   &.is-border {
-    :deep+.tox.tox-tinymce {
+    :deep + .tox.tox-tinymce {
       border: $border;
       border-radius: 4px;
     }

+ 4 - 24
src/views/book/courseware/create/components/FullTextSettings.vue

@@ -18,12 +18,7 @@
       </el-form-item>
       <el-form-item label="字体">
         <el-select v-model="unified_attrib.font" placeholder="请选择字体">
-          <el-option label="楷体" value="楷体,微软雅黑" />
-          <el-option label="黑体" value="黑体,微软雅黑" />
-          <el-option label="宋体" value="宋体,微软雅黑" />
-          <el-option label="Arial" value="Arial,Helvetica,sans-serif" />
-          <el-option label="Times New Roman" value="Times New Roman,times,serif" />
-          <el-option label="拼音" value="League" />
+          <el-option v-for="{ label, value } in fontList" :key="value" :label="label" :value="value" />
         </el-select>
       </el-form-item>
       <el-form-item label="字号">
@@ -70,7 +65,7 @@
 </template>
 
 <script>
-import { pinyinPositionList, isEnable } from '@/views/book/courseware/data/common';
+import { pinyinPositionList, fontList, fontSizeList, isEnable } from '@/views/book/courseware/data/common';
 import { GetBookUnifiedAttrib } from '@/api/book';
 import { unified_attrib } from '@/common/data';
 import { ToAuxiliaryColor } from '@/api/app';
@@ -94,24 +89,9 @@ export default {
   data() {
     return {
       unified_attrib,
-      fontSizeList: [
-        '8pt',
-        '10pt',
-        '12pt',
-        '14pt',
-        '16pt',
-        '18pt',
-        '20pt',
-        '22pt',
-        '24pt',
-        '26pt',
-        '28pt',
-        '30pt',
-        '32pt',
-        '34pt',
-        '36pt',
-      ],
+      fontSizeList,
       pinyinPositionList,
+      fontList,
       isEnable,
     };
   },

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

@@ -16,7 +16,7 @@
             </span>
           </div>
           <SvgIcon icon-class="delete-black" size="12" @click="removeFile" />
-          <ResourcesOperate :data="data.mp3_list[0]"></ResourcesOperate>
+          <ResourcesOperate :data="data.mp3_list[0]" />
         </div>
         <span v-if="data.content" class="tips">说明:需先点击“生成分词”按钮后,再进行文章校对或生成字幕节点</span>
         <div v-if="data.content" class="btn-box">
@@ -141,7 +141,7 @@
           :data="compareData"
           :type="compareType"
           :changewords-result-list="changewordsResultList"
-          :openCheckSubtitles="openCheckSubtitles"
+          :open-check-subtitles="openCheckSubtitles"
         />
         <span slot="footer" class="dialog-footer">
           <el-button @click="handleClose">取 消</el-button>
@@ -159,22 +159,22 @@
           v-if="editWordIndex === 0"
           key="new_word"
           :data-new-word="data.new_word_list"
-          @sureNewWords="sureNewWords"
           :unified-attrib="data.unified_attrib ? data.unified_attrib : null"
+          @sureNewWords="sureNewWords"
         />
 
         <Notes
           v-if="editWordIndex === 1"
           key="notes"
           :data-notes="data.notes_list"
-          @sureNotes="sureNotes"
           :unified-attrib="data.unified_attrib ? data.unified_attrib : null"
+          @sureNotes="sureNotes"
         />
         <NewWord
           v-if="editWordIndex === 2"
           :data-new-word="data.other_word_list"
-          @sureNewWords="sureOtherNewWords"
           :unified-attrib="data.unified_attrib ? data.unified_attrib : null"
+          @sureNewWords="sureOtherNewWords"
         />
       </template>
       <!-- </el-dialog> -->
@@ -193,11 +193,11 @@
         <CheckPic :data="data" />
       </el-dialog>
       <CheckSubtitles
+        v-if="visible"
         :visible.sync="visible"
         :audio-id="data.mp3_list[0] && data.mp3_list[0].file_id ? data.mp3_list[0].file_id : ''"
         :option-list="subtitleList"
         @saveSubtitles="saveSubtitles"
-        v-if="visible"
       />
     </template>
   </ModuleBase>
@@ -281,7 +281,6 @@ export default {
         ['n', 'ń', 'ň', 'ǹ', 'n'],
         ['m̄', 'ḿ', 'm', 'm̀', 'm'],
       ],
-      toneList: [' ', 'ˉ', 'ˊ', 'ˇ', 'ˋ'],
       pinyinList: [], // 拼音校正列表
     };
   },

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

@@ -26,7 +26,7 @@
                 <div v-for="(ritem, rindex) in data.property.role_list" :key="rindex" @click="changeRole(item, ritem)">
                   {{ ritem.fullName || ritem.name }}
                 </div>
-                <div class="avatar-box" slot="reference">
+                <div slot="reference" class="avatar-box">
                   <span
                     v-if="
                       data.property.role_img_type === 'upload' &&
@@ -85,7 +85,7 @@
                       }"
                       @click="selectItem(wItem)"
                     >
-                      <span class="pinyin" v-if="isEnable(data.property.view_pinyin)">{{
+                      <span v-if="isEnable(data.property.view_pinyin)" class="pinyin">{{
                         windex === 0 &&
                         wIndex === 0 &&
                         data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case === 'true'
@@ -172,7 +172,7 @@
             </span>
           </div>
           <SvgIcon icon-class="delete-black" size="12" @click="removeFile" />
-          <ResourcesOperate :data="data.mp3_list[0]"></ResourcesOperate>
+          <ResourcesOperate :data="data.mp3_list[0]" />
         </div>
         <span class="tips">说明:需先点击“生成分词”按钮后,再进行文章校对或生成字幕节点</span>
         <div v-if="data.detail.length > 0" class="btn-box">
@@ -246,12 +246,12 @@
         </div>
       </el-dialog>
       <el-dialog
+        v-if="remarkVisible"
         title="气泡"
         :visible.sync="remarkVisible"
         width="50%"
         :close-on-click-modal="false"
         :append-to-body="true"
-        v-if="remarkVisible"
       >
         <div v-if="remark" class="remark">
           <div class="adult-book-input-item">
@@ -277,7 +277,7 @@
           </div>
           <div class="adult-book-input-item">
             <span class="adult-book-lable">标题背景色:</span>
-            <el-color-picker v-model="remark.chsBg"></el-color-picker>
+            <el-color-picker v-model="remark.chsBg" />
           </div>
           <div class="adult-book-input-item">
             <span class="adult-book-lable">正文:</span>
@@ -302,7 +302,7 @@
           </div>
           <div class="adult-book-input-item">
             <span class="adult-book-lable">正文背景色:</span>
-            <el-color-picker v-model="remark.enBg"></el-color-picker>
+            <el-color-picker v-model="remark.enBg" />
           </div>
           <div v-if="remark.img_list && remark.img_list.length == 0" class="adult-book-input-item">
             <el-upload
@@ -367,7 +367,7 @@
           :data="compareData"
           :type="compareType"
           :changewords-result-list="changewordsResultList"
-          :openCheckSubtitles="openCheckSubtitles"
+          :open-check-subtitles="openCheckSubtitles"
         />
         <span slot="footer" class="dialog-footer">
           <el-button @click="handleClose">取 消</el-button>
@@ -385,31 +385,31 @@
           v-if="editWordIndex === 0"
           key="new_word"
           :data-new-word="data.new_word_list"
-          @sureNewWords="sureNewWords"
           :unified-attrib="data.unified_attrib ? data.unified_attrib : null"
+          @sureNewWords="sureNewWords"
         />
 
         <Notes
           v-if="editWordIndex === 1"
           key="notes"
           :data-notes="data.notes_list"
-          @sureNotes="sureNotes"
           :unified-attrib="data.unified_attrib ? data.unified_attrib : null"
+          @sureNotes="sureNotes"
         />
         <NewWord
           v-if="editWordIndex === 2"
           :data-new-word="data.other_word_list"
-          @sureNewWords="sureOtherNewWords"
           :unified-attrib="data.unified_attrib ? data.unified_attrib : null"
+          @sureNewWords="sureOtherNewWords"
         />
       </template>
       <!-- </el-dialog> -->
       <CheckSubtitles
+        v-if="visible"
         :visible.sync="visible"
         :audio-id="data.mp3_list[0] && data.mp3_list[0].file_id ? data.mp3_list[0].file_id : ''"
         :option-list="subtitleList"
         @saveSubtitles="saveSubtitles"
-        v-if="visible"
       />
     </template>
   </ModuleBase>

+ 6 - 6
src/views/book/courseware/create/components/question/new_word/NewWord.vue

@@ -364,7 +364,7 @@
           </span>
         </div>
         <SvgIcon icon-class="delete-black" size="12" @click="removeFile('audio')" />
-        <ResourcesOperate :data="data.audio_data"></ResourcesOperate>
+        <ResourcesOperate :data="data.audio_data" />
       </div>
       <SelectUpload label="音频字幕(lrc)文件" :limit="1" type="lrc" @uploadSuccess="uploadLrcSuccess" />
       <div v-if="data.lrc_data.url.length > 0" class="upload-file">
@@ -375,7 +375,7 @@
           </span>
         </div>
         <SvgIcon icon-class="delete-black" size="12" @click="removeFile('lrc')" />
-        <ResourcesOperate :data="data.lrc_data"></ResourcesOperate>
+        <ResourcesOperate :data="data.lrc_data" />
       </div>
       <div class="lrc-box" style="margin: 5px 0">
         <div v-if="data.lrc_arr && data.lrc_arr.length > 0" class="lrc-box">
@@ -640,7 +640,7 @@ export default {
         this.data.new_word_list.length > 0 ? this.data.new_word_list[this.data.new_word_list.length - 1].number : '0';
       this.data.new_word_list.push(getOption());
       if (!isNaN(number) && !isNaN(parseFloat(number))) {
-        this.data.new_word_list[this.data.new_word_list.length - 1].number = number * 1 + 1;
+        this.data.new_word_list[this.data.new_word_list.length - 1].number = Number(number) + 1;
       }
     },
     // 获取数据
@@ -685,7 +685,7 @@ export default {
     // 自动生成拼音
     handlePinyin(text) {
       let data = {
-        text: text,
+        text,
         is_rich_text: 'false',
         is_first_sentence_first_hz_pinyin_first_char_upper_case: 'false',
         is_fill_space: 'false',
@@ -698,7 +698,7 @@ export default {
             res.parsed_text.paragraph_list.map((outerArr, i) =>
               outerArr.map((innerArr, j) =>
                 innerArr.map((newItem, k) => {
-                  mergedData += newItem.pinyin + ' ';
+                  mergedData += `${newItem.pinyin} `;
                 }),
               ),
             );
@@ -895,7 +895,7 @@ export default {
         // });
       });
       TextToAudioFile({
-        text: text,
+        text,
         voice_type: this.data.property.voice_type,
         emotion: this.data.property.emotion,
         speed_ratio: this.data.property.speed_ratio,

+ 41 - 4
src/views/book/courseware/create/components/question/voice_matrix/VoiceMatrix.vue

@@ -7,6 +7,23 @@
           <SvgIcon icon-class="font-italic" size="16" @click="BatchSetFormat('italic')" />
           <SvgIcon icon-class="font-underline" size="16" @click="BatchSetFormat('strikethrough')" />
           <SvgIcon icon-class="font-strikethrough" size="16" @click="BatchSetFormat('underline')" />
+          <el-select
+            v-model="font"
+            style="width: 120px"
+            placeholder="请选择字体"
+            @change="BatchSetFormat('fontFamily', font)"
+          >
+            <el-option v-for="{ label, value } in fontList" :key="value" :label="label" :value="value" />
+          </el-select>
+          <el-select
+            v-model="fontSize"
+            style="width: 100px"
+            placeholder="请选择字号"
+            @change="BatchSetFormat('fontSize', fontSize)"
+          >
+            <el-option v-for="size in fontSizeList" :key="size" :label="size" :value="size" />
+          </el-select>
+          <el-color-picker v-model="color" @change="BatchSetFormat('color', color)" />
         </div>
 
         <div class="option-list">
@@ -38,7 +55,7 @@
             </span>
           </div>
           <SvgIcon icon-class="delete-black" size="12" @click="removeFile('audio')" />
-          <ResourcesOperate :data="data.audio_data"></ResourcesOperate>
+          <ResourcesOperate :data="data.audio_data" />
         </div>
 
         <div class="upload-lrc">
@@ -60,7 +77,7 @@
             </span>
           </div>
           <SvgIcon icon-class="delete-black" size="12" @click="removeFile('lrc')" />
-          <ResourcesOperate :data="data.lrc_data"></ResourcesOperate>
+          <ResourcesOperate :data="data.lrc_data" />
         </div>
       </div>
 
@@ -99,6 +116,7 @@ import ResourcesOperate from '../../common/ResourcesOperate.vue';
 
 import { getVoiceMatrixData, getOption } from '@/views/book/courseware/data/voiceMatrix';
 import { GetStaticResources } from '@/api/app';
+import { fontList, fontSizeList } from '@/views/book/courseware/data/common';
 
 export default {
   name: 'VoiceMatrix',
@@ -114,9 +132,23 @@ export default {
       visible: false,
       curSelectRow: -1,
       curSelectColumn: -1,
+      fontList,
+      fontSizeList,
+      font: '楷体,微软雅黑',
+      fontSize: '12pt',
+      color: '#1d2129',
     };
   },
   watch: {
+    'property.isGetContent': {
+      handler(val) {
+        if (val) {
+          this.font = this.data?.unified_attrib?.font || '楷体,微软雅黑';
+          this.fontSize = this.data?.unified_attrib?.font_size || '12pt';
+          this.color = this.data?.unified_attrib?.text_color || '#1d2129';
+        }
+      },
+    },
     'data.property.row_count': {
       handler(val) {
         if (val < this.data.option_list.length) {
@@ -232,9 +264,14 @@ export default {
         })
         .catch(() => {});
     },
-    BatchSetFormat(text) {
+    /**
+     * 批量设置格式
+     * @param {string} text 格式类型
+     * @param {string} val 格式值(如果是字体或字号则传入对应的值)
+     */
+    BatchSetFormat(text, val = '') {
       this.$refs.richText.forEach((richText) => {
-        richText.setRichFormat(text);
+        richText.setRichFormat(text, val);
       });
     },
     /**

+ 29 - 0
src/views/book/courseware/data/common.js

@@ -128,6 +128,35 @@ export const audioGenerationMethodList = [
   },
 ];
 
+// 字体样式列表
+export const fontList = [
+  { label: '楷体', value: '楷体,微软雅黑' },
+  { label: '黑体', value: '黑体,微软雅黑' },
+  { label: '宋体', value: '宋体,微软雅黑' },
+  { label: 'Arial', value: 'Arial,Helvetica,sans-serif' },
+  { label: 'Times New Roman', value: 'Times New Roman,times,serif' },
+  { label: '拼音', value: 'League' },
+];
+
+// 字体大小列表
+export const fontSizeList = [
+  '8pt',
+  '10pt',
+  '12pt',
+  '14pt',
+  '16pt',
+  '18pt',
+  '20pt',
+  '22pt',
+  '24pt',
+  '26pt',
+  '28pt',
+  '30pt',
+  '32pt',
+  '34pt',
+  '36pt',
+];
+
 // 参考答案数据模板
 export const answerData = {
   answer_rich_text: '', // 参考答案富文本

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

@@ -16,8 +16,6 @@
         <div class="operator flex">
           <span class="link" @click="openBookUnifiedTitle()">教材标题设置</span>
           <span class="link" @click="openBookUnifiedAttrib()">教材样式设置</span>
-          <span class="link" @click="addChapterDialog()">添加章节节点</span>
-          <span class="link" @click="addCoursewareDialog()">添加教材内容节点</span>
           <span class="link" @click="$router.push({ path: `/personal_workbench/project` })">返回项目列表</span>
         </div>
       </div>
@@ -67,11 +65,11 @@
             <el-dropdown v-if="!isEnable(is_leaf_chapter)" trigger="click">
               <span class="el-dropdown-link" style="cursor: pointer"><i class="el-icon-plus"></i> </span>
               <el-dropdown-menu slot="dropdown">
-                <el-dropdown-item>
-                  <span @click="addCoursewareDialog(id)">添加教材内容</span>
+                <el-dropdown-item @click.native="addCoursewareDialog(id)">
+                  <span>添加教材内容</span>
                 </el-dropdown-item>
-                <el-dropdown-item>
-                  <span @click="addChapterDialog(id)">添加章节</span>
+                <el-dropdown-item @click.native="addChapterDialog(id)">
+                  <span>添加章节</span>
                 </el-dropdown-item>
               </el-dropdown-menu>
             </el-dropdown>

+ 45 - 11
src/views/project_manage/org/project/index.vue

@@ -32,8 +32,14 @@
               >查看信息</span
             >
             <span class="link" @click="previewProject(row.id)">预览项目</span>
-            <span class="link" @click="projectAuditOperate(row.id, 'true')">审核通过</span>
-            <span class="link" @click="projectAuditOperate(row.id, 'false')">审核拒绝</span>
+            <span
+              v-if="isTrue(row.is_can_audit)"
+              class="link"
+              @click="projectAuditOperate(row.id, 'true', row.request_status)"
+            >
+              审核通过
+            </span>
+            <span v-else class="link" @click="projectAuditOperate(row.id, 'false', row.request_status)">审核拒绝</span>
           </template>
         </el-table-column>
       </el-table>
@@ -66,20 +72,48 @@ export default {
     };
   },
   methods: {
-    projectAuditOperate(project_id, is_pass) {
-      ProjectAuditOperate({ project_id, is_pass }).then(() => {
-        this.$message.success('操作成功');
-        this.$refs.pagination.getList();
-      });
+    /**
+     * 项目审核操作
+     * @param {number} project_id 项目id
+     * @param {string} is_pass 是否审核通过,true-审核通过,false-审核拒绝
+     * @param {string} request_status 申请状态
+     */
+    projectAuditOperate(project_id, is_pass, request_status) {
+      console.log(request_status);
+
+      this.$confirm(`确定要执行${is_pass === 'true' ? '审核通过' : '审核拒绝'}吗?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+      })
+        .then(() => {
+          let loading = null;
+          if (request_status === 3) {
+            loading = this.$loading({
+              text: '正在生成新的修订项目,请等待...',
+            });
+          }
+          ProjectAuditOperate({ project_id, is_pass })
+            .then(() => {
+              this.$message.success('操作成功');
+              this.$refs.pagination.getList();
+            })
+            .catch(() => {})
+            .finally(() => {
+              if (loading) {
+                loading.close();
+              }
+            });
+        })
+        .catch(() => {});
     },
     pageQueryProjectList_OrgManager(data) {
-      PageQueryProjectList_OrgManager({ ...data, status: -1 }).then(
-        ({ total_count, cur_page_begin_index, project_list }) => {
+      PageQueryProjectList_OrgManager({ ...data, status: -1 })
+        .then(({ total_count, cur_page_begin_index, project_list }) => {
           this.total = total_count;
           this.cur_page_begin_index = cur_page_begin_index;
           this.list = project_list;
-        },
-      );
+        })
+        .catch(() => {});
     },
     previewProject(projectId) {
       this.$router.push({ path: `/project_manage/org/project/preview/${projectId}` });