|
|
@@ -0,0 +1,453 @@
|
|
|
+<template>
|
|
|
+ <div class="production-editorial">
|
|
|
+ <div class="top-operate">
|
|
|
+ <span class="name">{{ project_info.name }}</span>
|
|
|
+ <span class="button" @click="$router.push({ path: `/personal_workbench/project` })">返回项目列表</span>
|
|
|
+ <div class="audit-step">
|
|
|
+ <span>审核步骤:</span>
|
|
|
+ <span v-for="(node, index) in audit_node_list" :key="index" class="audit-node">
|
|
|
+ {{ node }}
|
|
|
+ <span v-if="index !== audit_node_list.length - 1">→</span>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <span class="button">设置审校步骤</span>
|
|
|
+ <div class="operator">
|
|
|
+ <span class="button" @click="addChapterDialog">添加章节节点</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="chapters-container">
|
|
|
+ <div class="list-title">
|
|
|
+ <span class="title-cell">教材章节结构</span>
|
|
|
+ <span class="title-cell">制作人</span>
|
|
|
+ <span class="title-cell">审校人</span>
|
|
|
+ <span class="title-cell">状态</span>
|
|
|
+ <span class="title-cell">操作</span>
|
|
|
+ </div>
|
|
|
+ <div v-for="{ id, name, nodes: children, producer_list } in nodes" :key="id" class="catalogue">
|
|
|
+ <!-- 一级目录 -->
|
|
|
+ <div
|
|
|
+ :class="['first-level', { active: curSelectId === id }]"
|
|
|
+ @click="selectActiveChapter(id, '', 'false', 'chapter')"
|
|
|
+ >
|
|
|
+ <div class="chapter-title">{{ name }}</div>
|
|
|
+ <div class="producer">
|
|
|
+ <span>{{ producer_list.map((producer) => producer.name).join(';') }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="audit"></div>
|
|
|
+ <div class="status"></div>
|
|
|
+ <div class="operator">
|
|
|
+ <span class="link">修改</span>
|
|
|
+ <span class="link" @click="openSetProducer(id)">设置制作人</span>
|
|
|
+ <span class="link danger" @click="deleteChapter(id)">删除</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template v-for="item in children">
|
|
|
+ <!-- 二级目录或内容 -->
|
|
|
+ <div
|
|
|
+ :key="item.id"
|
|
|
+ :class="[
|
|
|
+ 'catalogue-item',
|
|
|
+ { active: curSelectId === item.id },
|
|
|
+ item.is_leaf_chapter === 'true' ? 'content' : 'subdirectory',
|
|
|
+ ]"
|
|
|
+ @click="selectActiveChapter(item.id, id, 'false', 'courseware')"
|
|
|
+ >
|
|
|
+ <div class="name">{{ item.name }}</div>
|
|
|
+ <div class="producer">
|
|
|
+ <span>{{ item.producer_list.map((producer) => producer.name).join(';') }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="audit"></div>
|
|
|
+ <div class="status"></div>
|
|
|
+ <div class="operator">
|
|
|
+ <span class="link">修改</span>
|
|
|
+ <span class="link" @click="openSetProducer(id)">设置制作人</span>
|
|
|
+ <span class="link danger" @click="deleteChapter(id)">删除</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 二级目录的内容 -->
|
|
|
+ <template v-if="item.is_leaf_chapter === 'false' && item.nodes?.length > 0">
|
|
|
+ <div v-for="li in item.nodes" :key="li.id">
|
|
|
+ <div :class="['catalogue-item', 'children']">
|
|
|
+ <div class="name">{{ li.name }}</div>
|
|
|
+ <div class="producer">
|
|
|
+ <span>{{ li.producer_list.map((producer) => producer.name).join(';') }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="audit"></div>
|
|
|
+ <div class="status"></div>
|
|
|
+ <div class="operator">
|
|
|
+ <span class="link">修改</span>
|
|
|
+ <span class="link" @click="openSetProducer(li.id)">设置制作人</span>
|
|
|
+ <span class="link">设置审校人</span>
|
|
|
+ <span class="link danger" @click="deleteChapter(li.id)">删除</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <AddChapter
|
|
|
+ :id="book_id"
|
|
|
+ :visible="visible"
|
|
|
+ :parent-id="parent_id"
|
|
|
+ :cur-select-id="curSelectId"
|
|
|
+ :is-leaf="is_leaf"
|
|
|
+ :add-type="addType"
|
|
|
+ @close="closeAddChapter"
|
|
|
+ @addChapterNode="addChapterNode"
|
|
|
+ @addCourseware="chapterAddCoursewareToBook"
|
|
|
+ />
|
|
|
+
|
|
|
+ <SetProducer
|
|
|
+ :id="producer.id"
|
|
|
+ :visible="producer.visible"
|
|
|
+ :member-list="member_list"
|
|
|
+ @close="producer.visible = false"
|
|
|
+ @chapterSetProducer="chapterSetProducer"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import AddChapter from './AddChapter.vue';
|
|
|
+import SetProducer from './SetProducer.vue';
|
|
|
+
|
|
|
+import { GetProjectBaseInfo } from '@/api/project';
|
|
|
+import {
|
|
|
+ ChapterGetBookChapterStruct,
|
|
|
+ ChapterAddChapterToBook,
|
|
|
+ ChapterAddCoursewareToBook,
|
|
|
+ ChapterDeleteChapter,
|
|
|
+ ChapterSetProducer,
|
|
|
+} from '@/api/book';
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'ProductionEditorialManage',
|
|
|
+ components: {
|
|
|
+ AddChapter,
|
|
|
+ SetProducer,
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ book_id: this.$route.params.id,
|
|
|
+ visible: false,
|
|
|
+ curSelectId: '', // 当前选中的章节ID
|
|
|
+ parent_id: '', // 父节点ID
|
|
|
+ is_leaf: false, // 是否是叶子节点
|
|
|
+ addType: 'chapter', // 添加类型
|
|
|
+ project_info: {}, // 项目基本信息
|
|
|
+ member_list: [], // 项目成员列表
|
|
|
+ book_info_PBE: {}, // 与项目绑定的的教材实体(project_bind_entity)信息
|
|
|
+ audit_node_list: [], // 审校流程节点列表
|
|
|
+ nodes: [], // 教材章节结构
|
|
|
+ producer: {
|
|
|
+ visible: false, // 设置制作人弹窗
|
|
|
+ id: '', // 章节ID
|
|
|
+ },
|
|
|
+ };
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.getProjectBaseInfo();
|
|
|
+ this.getBookChapterStruct();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /**
|
|
|
+ * 获取项目基本信息
|
|
|
+ * @param {string} id - 项目ID
|
|
|
+ */
|
|
|
+ getProjectBaseInfo() {
|
|
|
+ GetProjectBaseInfo({ id: this.book_id }).then(({ project_info, book_info_PBE, audit_node_list, member_list }) => {
|
|
|
+ this.project_info = project_info;
|
|
|
+ this.member_list = member_list;
|
|
|
+ this.book_info_PBE = book_info_PBE;
|
|
|
+ this.audit_node_list = audit_node_list;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 获取教材章节结构
|
|
|
+ * @param {string} book_id - 教材ID
|
|
|
+ * @param {number} node_deep_mode - 节点深度模式
|
|
|
+ * @param {boolean} is_contain_producer - 是否包含制作人
|
|
|
+ */
|
|
|
+ getBookChapterStruct() {
|
|
|
+ ChapterGetBookChapterStruct({ book_id: this.book_id, node_deep_mode: 0, is_contain_producer: 'true' }).then(
|
|
|
+ ({ nodes }) => {
|
|
|
+ this.nodes = nodes;
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 选择当前章节
|
|
|
+ * @param id - 父章节ID
|
|
|
+ * @param is_leaf - 是否是叶子节点
|
|
|
+ */
|
|
|
+ selectActiveChapter(id, parentId = '', is_leaf = 'false', type = 'chapter') {
|
|
|
+ this.curSelectId = id;
|
|
|
+ this.parent_id = parentId;
|
|
|
+ this.is_leaf = is_leaf;
|
|
|
+ this.addType = type;
|
|
|
+ },
|
|
|
+ chapterAddCoursewareToBook(data) {
|
|
|
+ ChapterAddCoursewareToBook(data).then(() => {
|
|
|
+ this.getBookChapterStruct();
|
|
|
+ this.visible = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ addChapterDialog() {
|
|
|
+ this.visible = true;
|
|
|
+ },
|
|
|
+ closeAddChapter() {
|
|
|
+ this.visible = false;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 添加章节
|
|
|
+ */
|
|
|
+ addChapterNode(data) {
|
|
|
+ ChapterAddChapterToBook(data).then(() => {
|
|
|
+ this.getBookChapterStruct();
|
|
|
+ this.visible = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 删除章节
|
|
|
+ * @param {string} id - 章节ID
|
|
|
+ */
|
|
|
+ deleteChapter(id) {
|
|
|
+ ChapterDeleteChapter({ id, is_force_delete: 'true' }).then(() => {
|
|
|
+ this.getBookChapterStruct();
|
|
|
+ this.$message.success('删除成功');
|
|
|
+ });
|
|
|
+ },
|
|
|
+ openSetProducer(id) {
|
|
|
+ this.producer.visible = true;
|
|
|
+ this.producer.id = id;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 设置制作人
|
|
|
+ * @param {Object} data - 章节制作人数据
|
|
|
+ * @param {string} data.node_id - 章节ID
|
|
|
+ * @param {string} data.producer_id_list - 制作人ID列表
|
|
|
+ */
|
|
|
+ chapterSetProducer(data) {
|
|
|
+ ChapterSetProducer({ book_id: this.book_id, ...data })
|
|
|
+ .then(() => {
|
|
|
+ this.getBookChapterStruct();
|
|
|
+ this.producer.visible = false;
|
|
|
+ this.$message.success('制作人设置成功');
|
|
|
+ })
|
|
|
+ .catch(({ error }) => {
|
|
|
+ this.$message.error(`设置制作人失败: ${error}`);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+@use '@/styles/mixin.scss' as *;
|
|
|
+
|
|
|
+.production-editorial {
|
|
|
+ @include page-base;
|
|
|
+
|
|
|
+ .top-operate {
|
|
|
+ display: flex;
|
|
|
+ padding: 6px 4px;
|
|
|
+ border-top: $border;
|
|
|
+ border-bottom: $border;
|
|
|
+
|
|
|
+ .name {
|
|
|
+ width: 240px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+
|
|
|
+ .button {
|
|
|
+ padding: 0 8px;
|
|
|
+ color: $main-color;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ .audit-step {
|
|
|
+ padding: 0 12px;
|
|
|
+ border-left: $border;
|
|
|
+
|
|
|
+ .audit-node {
|
|
|
+ &:last-child {
|
|
|
+ margin-right: 18px;
|
|
|
+ color: $danger-color;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .operator {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ justify-content: flex-end;
|
|
|
+ border-left: $border;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .chapters-container {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .list-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .title-cell {
|
|
|
+ padding: 8px 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: $main-color;
|
|
|
+ text-align: center;
|
|
|
+ background-color: $main-background-color;
|
|
|
+ border: $border;
|
|
|
+
|
|
|
+ &:not(:last-child) {
|
|
|
+ border-right: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:first-child {
|
|
|
+ width: 550px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:nth-child(2) {
|
|
|
+ width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:nth-child(3) {
|
|
|
+ width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:nth-child(4) {
|
|
|
+ width: 140px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 200px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .catalogue {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ border: $border;
|
|
|
+ border-top: none;
|
|
|
+
|
|
|
+ .active {
|
|
|
+ background-color: $main-active-color;
|
|
|
+ }
|
|
|
+
|
|
|
+ .first-level {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ > div {
|
|
|
+ height: 40px;
|
|
|
+ padding: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .chapter-title {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #000;
|
|
|
+ }
|
|
|
+
|
|
|
+ .operator {
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :first-child {
|
|
|
+ width: 550px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :nth-child(2) {
|
|
|
+ width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :nth-child(3) {
|
|
|
+ width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :nth-child(4) {
|
|
|
+ width: 140px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :last-child {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :not(:last-child) {
|
|
|
+ border-right: $border;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &-item {
|
|
|
+ display: flex;
|
|
|
+ font-size: 14px;
|
|
|
+ border-top: $border;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: #f3f3f3;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.subdirectory {
|
|
|
+ .name {
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.children {
|
|
|
+ padding-left: 32px;
|
|
|
+
|
|
|
+ > :first-child {
|
|
|
+ width: 518px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .name {
|
|
|
+ padding-left: 24px;
|
|
|
+ color: #000;
|
|
|
+ }
|
|
|
+
|
|
|
+ > div {
|
|
|
+ height: 40px;
|
|
|
+ padding: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .operator {
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :first-child {
|
|
|
+ width: 550px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :nth-child(2) {
|
|
|
+ width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :nth-child(3) {
|
|
|
+ width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :nth-child(4) {
|
|
|
+ width: 140px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :last-child {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ > :not(:last-child) {
|
|
|
+ border-right: $border;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|