2 Commits 92c70f0593 ... 71307a61af

Autore SHA1 Messaggio Data
  dsy 71307a61af Merge branch 'master' of http://60.205.254.193:3000/GCLS/eep_page 2 settimane fa
  dsy 9feec35f2f 1、富文本换行 2、制作与审核管理的拖拽 2 settimane fa

+ 1 - 1
.env

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

+ 5 - 6
src/components/PinyinText.vue

@@ -570,8 +570,8 @@ export default {
     }
 
     .pinyin-sentence {
-      display: inline-flex;
-      flex-wrap: wrap;
+      display: inline;
+      white-space: normal;
     }
 
     .image-container {
@@ -587,15 +587,14 @@ export default {
 
   .pinyin-paragraph {
     .pinyin-sentence {
-      display: inline-flex;
-      flex-wrap: wrap;
+      display: inline;
+      white-space: normal;
     }
   }
 
   .pinyin-text {
+    display: inline-flex;
     padding: 0 2px;
-
-    // font-size: 16px;
     text-wrap: pretty;
     hanging-punctuation: allow-end;
 

+ 1 - 1
src/views/personal_workbench/edit_task/edit/UseTemplate.vue

@@ -37,7 +37,7 @@
               {{ scope.$index + 1 }}
             </template>
           </el-table-column>
-          <el-table-column prop="id" label="编号" />
+          <el-table-column prop="sn" label="编号" />
           <el-table-column prop="name" label="名称" />
         </el-table>
         <PaginationPage ref="pagination" :total="total" :pager-count="5" @getList="getTemplateList" />

+ 184 - 94
src/views/personal_workbench/project/ProductionEditorialManage.vue

@@ -33,104 +33,109 @@
         <span class="title-cell">操作</span>
       </div>
       <div class="chapters-container">
-        <div
-          v-for="(
-            {
-              id,
-              name,
-              deep,
-              producer_list,
-              is_leaf_chapter,
-              is_root,
-              is_inherited_producer,
-              is_inherited_auditor,
-              auditor_desc,
-              status_name,
-              last_editor_name,
-              last_edit_time,
-              edit_end_date,
-              is_show_gc_button,
-              status,
-            },
-            i
-          ) in node_list"
-          :key="id"
-          :class="['catalogue', { active: curSelectId === id }]"
-          @click="selectActiveChapter(id, is_leaf_chapter === 'true')"
-        >
-          <div :class="['chapter-title', { courseware: isEnable(is_leaf_chapter) }]" :style="computedNameStyle(deep)">
-            <span class="nowrap-ellipsis" :title="name">{{ name }}</span>
-            <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 @click.native="addCoursewareDialog(id)">
-                  <span>添加教材内容</span>
-                </el-dropdown-item>
-                <el-dropdown-item @click.native="addChapterDialog(id)">
-                  <span>添加章节</span>
-                </el-dropdown-item>
-              </el-dropdown-menu>
-            </el-dropdown>
-
-            <div v-if="isEnable(is_leaf_chapter) && isEnable(is_show_gc_button)">
-              <span v-if="status === 2" class="link" @click="setCoursewareCanGC(id, 'true')">开始改错</span>
-              <span v-if="status === 4" class="link" @click="setCoursewareCanGC(id, 'false')">取消改错</span>
-            </div>
-          </div>
-          <div
-            class="producer nowrap-ellipsis"
-            :style="{ color: !isEnable(is_root) && isEnable(is_inherited_producer) ? '#ff4757' : '' }"
-            :title="producer_list.map((producer) => producer.name).join(';')"
-          >
-            <span>{{ producer_list.map((producer) => producer.name).join(';') }}</span>
-          </div>
-          <div
-            class="edit-end-date"
-            :style="{ color: !isEnable(is_root) && isEnable(is_inherited_producer) ? '#ff4757' : '' }"
-          >
-            {{ edit_end_date }}
-          </div>
+        <VueDraggable v-model="node_list" :animation="150" :move="canMove" @start="onStart" @end="onEnd">
           <div
-            class="audit nowrap-ellipsis"
-            :style="{ color: !isEnable(is_root) && isEnable(is_inherited_auditor) ? '#ff4757' : '' }"
-            :title="auditor_desc"
+            v-for="(
+              {
+                id,
+                name: nodeName,
+                deep,
+                producer_list,
+                is_leaf_chapter,
+                is_root,
+                is_inherited_producer,
+                is_inherited_auditor,
+                auditor_desc,
+                status_name,
+                last_editor_name,
+                last_edit_time,
+                edit_end_date,
+                is_show_gc_button,
+                status,
+              },
+              i
+            ) in node_list"
+            :key="id"
+            :class="[
+              'catalogue',
+              { active: curSelectId === id, 'drag-target': dragCtx.dragging && dragCtx.targetId === id },
+            ]"
+            @click="selectActiveChapter(id, is_leaf_chapter === 'true')"
           >
-            {{ auditor_desc }}
-          </div>
-          <div class="status">{{ status_name }}</div>
-          <div class="last-editor">{{ isEnable(is_leaf_chapter) ? last_editor_name : '' }}</div>
-          <div class="last-edit-time">{{ isEnable(is_leaf_chapter) ? last_edit_time : '' }}</div>
-          <div class="operator">
-            <template v-if="isEnable(is_leaf_chapter)">
-              <span v-if="i > 1 && computedIsRootFirst(i)" class="link" @click="moveChapterTreeNode(i, 0)">上移</span>
-              <span v-if="i < node_list.length - 1" class="link" @click="moveChapterTreeNode(i, 1)">下移</span>
-            </template>
-
-            <template v-else>
-              <span v-if="i > 1" class="link" @click="moveChapterNode(i, deep, 0)">上移</span>
-              <span v-if="i > 0 && computedIsLast(id, deep)" class="link" @click="moveChapterNode(i, deep, 1)">
-                下移
-              </span>
-            </template>
-
-            <span
-              v-if="is_root !== 'true'"
-              class="link"
-              @click="openUpdateNameDialog(id, name, is_leaf_chapter === 'true')"
+            <div :class="['chapter-title', { courseware: isEnable(is_leaf_chapter) }]" :style="computedNameStyle(deep)">
+              <span class="nowrap-ellipsis" :title="nodeName">{{ nodeName }}</span>
+              <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 @click.native="addCoursewareDialog(id)">
+                    <span>添加教材内容</span>
+                  </el-dropdown-item>
+                  <el-dropdown-item @click.native="addChapterDialog(id)">
+                    <span>添加章节</span>
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+
+              <div v-if="isEnable(is_leaf_chapter) && isEnable(is_show_gc_button)">
+                <span v-if="status === 2" class="link" @click="setCoursewareCanGC(id, 'true')">开始改错</span>
+                <span v-if="status === 4" class="link" @click="setCoursewareCanGC(id, 'false')">取消改错</span>
+              </div>
+            </div>
+            <div
+              class="producer nowrap-ellipsis"
+              :style="{ color: !isEnable(is_root) && isEnable(is_inherited_producer) ? '#ff4757' : '' }"
+              :title="producer_list.map((producer) => producer.name).join(';')"
             >
-              修改
-            </span>
-            <span class="link" @click="openSetProducer(id, producer_list)">设置制作人</span>
-            <span class="link" @click="openSetAuditor(id)">设置审核人</span>
-            <span
-              v-if="is_root !== 'true'"
-              class="link danger"
-              @click="is_leaf_chapter === 'true' ? deleteCourseware(id) : deleteChapter(id)"
+              <span>{{ producer_list.map((producer) => producer.name).join(';') }}</span>
+            </div>
+            <div
+              class="edit-end-date"
+              :style="{ color: !isEnable(is_root) && isEnable(is_inherited_producer) ? '#ff4757' : '' }"
+            >
+              {{ edit_end_date }}
+            </div>
+            <div
+              class="audit nowrap-ellipsis"
+              :style="{ color: !isEnable(is_root) && isEnable(is_inherited_auditor) ? '#ff4757' : '' }"
+              :title="auditor_desc"
             >
-              删除
-            </span>
+              {{ auditor_desc }}
+            </div>
+            <div class="status">{{ status_name }}</div>
+            <div class="last-editor">{{ isEnable(is_leaf_chapter) ? last_editor_name : '' }}</div>
+            <div class="last-edit-time">{{ isEnable(is_leaf_chapter) ? last_edit_time : '' }}</div>
+            <div class="operator">
+              <template v-if="isEnable(is_leaf_chapter)">
+                <span v-if="i > 1 && computedIsRootFirst(i)" class="link" @click="moveChapterTreeNode(i, 0)">上移</span>
+                <span v-if="i < node_list.length - 1" class="link" @click="moveChapterTreeNode(i, 1)">下移</span>
+              </template>
+
+              <template v-else>
+                <span v-if="i > 1" class="link" @click="moveChapterNode(i, deep, 0)">上移</span>
+                <span v-if="i > 0 && computedIsLast(id, deep)" class="link" @click="moveChapterNode(i, deep, 1)">
+                  下移
+                </span>
+              </template>
+
+              <span
+                v-if="is_root !== 'true'"
+                class="link"
+                @click="openUpdateNameDialog(id, nodeName, is_leaf_chapter === 'true')"
+              >
+                修改
+              </span>
+              <span class="link" @click="openSetProducer(id, producer_list)">设置制作人</span>
+              <span class="link" @click="openSetAuditor(id)">设置审核人</span>
+              <span
+                v-if="is_root !== 'true'"
+                class="link danger"
+                @click="is_leaf_chapter === 'true' ? deleteCourseware(id) : deleteChapter(id)"
+              >
+                删除
+              </span>
+            </div>
           </div>
-        </div>
+        </VueDraggable>
       </div>
     </main>
 
@@ -178,6 +183,7 @@ import SetAuditor from './components/SetAuditor.vue';
 import UpdateName from './components/UpdateName.vue';
 import BookUnifiedAttr from './components/BookUnifiedAttr.vue';
 import BookUnifiedTitle from './components/BookUnifiedTitle.vue';
+import VueDraggable from 'vuedraggable';
 
 import { GetProjectBaseInfo } from '@/api/project';
 import {
@@ -206,6 +212,7 @@ export default {
     UpdateName,
     BookUnifiedAttr,
     BookUnifiedTitle,
+    VueDraggable,
   },
   data() {
     return {
@@ -237,6 +244,13 @@ export default {
       },
       visibleAttr: false, // 教材属性设置弹窗
       visibleTitle: false, // 教材标题设置弹窗
+      // 拖拽相关数据
+      dragCtx: {
+        movedId: '',
+        oldIndex: -1,
+        targetId: '',
+        dragging: false,
+      },
     };
   },
   created() {
@@ -457,7 +471,7 @@ export default {
     },
     /**
      * 设置制作人
-     * @param {Object} data - 章节制作人数据
+     * @param {object} data - 章节制作人数据
      * @param {string} data.node_id - 章节ID
      * @param {string} data.producer_id_list - 制作人ID列表
      * @param {string} data.edit_end_date - 交稿日期
@@ -567,6 +581,78 @@ export default {
           // 忽略用户取消确认的情况
         });
     },
+    /**
+     * 拖拽过程中判断是否可以移动
+     * @param {object} evt - 拖拽事件对象
+     * @returns {boolean} - 是否可以移动
+     */
+    canMove(evt) {
+      // 当前被拖拽的数据项
+      const dragged = evt.draggedContext && evt.draggedContext.element;
+      const related = evt.relatedContext && evt.relatedContext.element;
+      this.dragCtx.targetId = related ? related.id : '';
+      if (dragged && dragged.is_root === 'true') {
+        return false;
+      }
+      return true;
+    },
+    /**
+     * 拖拽开始时记录被移动节点的信息
+     * @param {object} evt - 拖拽事件对象
+     * @param {number} evt.oldIndex - 原始索引
+     */
+    onStart(evt) {
+      const { oldIndex } = evt;
+      this.dragCtx.dragging = true;
+      this.dragCtx.oldIndex = oldIndex;
+      this.dragCtx.movedId = this.node_list[oldIndex]?.id || '';
+      this.dragCtx.targetId = '';
+    },
+    /**
+     * 拖拽结束后处理章节移动
+     * @param {object} evt - 拖拽事件对象
+     * @param {number} evt.oldIndex - 原始索引
+     * @param {number} evt.newIndex - 新索引
+     */
+    onEnd(evt) {
+      const { oldIndex, newIndex } = evt;
+
+      if (oldIndex === newIndex) {
+        this.dragCtx.dragging = false;
+        this.dragCtx.targetId = '';
+        return;
+      }
+
+      // 被移动节点 id
+      const id = this.dragCtx.movedId || this.node_list[newIndex]?.id;
+
+      // 按拖拽方向计算目标位置:上移放到目标前,下移放到目标后
+      const isMoveDown = newIndex > oldIndex;
+      const dest_position = isMoveDown ? 1 : 0;
+      const targetIndex = isMoveDown ? newIndex - 1 : newIndex + 1;
+      const dest_id = this.node_list[targetIndex]?.id || '';
+
+      if (!id || !dest_id) {
+        this.dragCtx.dragging = false;
+        this.dragCtx.targetId = '';
+        return;
+      }
+
+      ChapterMoveTreeNode({ id, dest_id, dest_position })
+        .then(() => {
+          this.getBookChapterStructExpandList();
+          this.$message.success('章节移动成功');
+        })
+        .catch(() => {
+          // 请求失败时,恢复原始位置
+          const movedNode = this.node_list.splice(newIndex, 1)[0];
+          this.node_list.splice(oldIndex, 0, movedNode);
+        })
+        .finally(() => {
+          this.dragCtx.dragging = false;
+          this.dragCtx.targetId = '';
+        });
+    },
   },
 };
 </script>
@@ -683,6 +769,10 @@ export default {
         background-color: $main-active-color;
       }
 
+      &.drag-target {
+        background-color: rgba($main-hover-color, 0.15);
+      }
+
       > div {
         height: 40px;
         padding: 8px;