Browse Source

统一预览页面

dusenyao 3 tuần trước cách đây
mục cha
commit
abaa68aeb2

+ 8 - 0
src/api/list.js

@@ -35,3 +35,11 @@ export function queryOrgUserList(data) {
 export function PageQueryMyProjectList_Auditor(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=page_query-PageQueryMyProjectList_Auditor`, data);
 }
+
+/**
+ * @description 分页查询项目列表(机构管理员)
+ * @param {object} data
+ */
+export function PageQueryProjectList_OrgManager(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=page_query-PageQueryProjectList_OrgManager`, data);
+}

+ 39 - 3
src/api/project.js

@@ -168,7 +168,7 @@ export function SetProjectMember(data) {
  * @param {string} data.courseware_id 课件ID
  * @param {string} data.content 审批内容
  */
-export function addCoursewareAuditRemark(data) {
+export function AddCoursewareAuditRemark(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_audit_manager-AddCoursewareAuditRemark`, data);
 }
 
@@ -177,7 +177,7 @@ export function addCoursewareAuditRemark(data) {
  * @param {object} data
  * @param {string} data.courseware_id 课件ID
  */
-export function getCoursewareAuditRemarkList(data) {
+export function GetCoursewareAuditRemarkList(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_audit_manager-GetCoursewareAuditRemarkList`, data);
 }
 
@@ -186,6 +186,42 @@ export function getCoursewareAuditRemarkList(data) {
  * @param {object} data
  * @param {string} data.id 批注ID
  */
-export function deleteCoursewareAuditRemarkList(data) {
+export function DeleteCoursewareAuditRemarkList(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_audit_manager-DeleteCoursewareAuditRemark`, data);
 }
+
+/**
+ * @description 申请上架教材
+ * @param {object} data
+ * @param {string} data.project_id 项目ID
+ */
+export function RequestShangjiaBook(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-RequestShangjiaBook`, data);
+}
+
+/**
+ * @description 申请回退项目
+ * @param {object} data
+ * @param {string} data.project_id 项目ID
+ */
+export function RequestRollbackProject(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-RequestRollbackProject`, data);
+}
+
+/**
+ * @description 上架教材
+ * @param {object} data
+ * @param {string} data.project_id 项目ID
+ */
+export function ShangjiaBook(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-ShangjiaBook`, data);
+}
+
+/**
+ * @description 退改项目
+ * @param {object} data
+ * @param {string} data.project_id 项目ID
+ */
+export function RollbackProject(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=project_manager-RollbackProject`, data);
+}

+ 422 - 0
src/components/CommonPreview.vue

@@ -0,0 +1,422 @@
+<template>
+  <div class="common-preview">
+    <div class="common-preview__header">
+      <div class="menu-container">
+        <MenuPopover :id="id" :node-list="node_list" :book-name="courseware_info.book_name" @selectNode="selectNode" />
+      </div>
+      <div class="courseware">
+        <span class="name-path">{{ courseware_info.name_path }}</span>
+        <span class="flow-nodename">{{ courseware_info.cur_audit_flow_node_name }}</span>
+        <div class="operator">
+          <slot name="operator" :courseware="courseware_info"></slot>
+        </div>
+      </div>
+    </div>
+
+    <div class="audit-content">
+      <div class="main-container">
+        <main class="preview-main">
+          <span class="title">
+            <SvgIcon icon-class="menu-2" size="24" />
+            <span>{{ courseware_info.name_path }}</span>
+          </span>
+
+          <CoursewarePreview :data="data" :component-list="component_list" :background="background" />
+        </main>
+      </div>
+      <div class="remark-list">
+        <h5>审校批注</h5>
+        <ul v-if="remark_list.length > 0">
+          <li v-for="{ id: remarkId, content, remark_person_name, remark_time } in remark_list" :key="remarkId">
+            <!-- eslint-disable-next-line vue/no-v-html -->
+            <p v-html="content"></p>
+            <div v-if="isAudit" class="remark-bottom">
+              <span>{{ remark_person_name + ':' + remark_time }}</span>
+              <el-button type="text" class="delete-btn" @click="deleteRemarks(remarkId)">删除</el-button>
+            </div>
+          </li>
+        </ul>
+        <p v-else style="text-align: center">暂无批注</p>
+      </div>
+    </div>
+
+    <el-dialog
+      title="添加课件审校批注"
+      :visible="visible"
+      width="680px"
+      :close-on-click-modal="false"
+      class="audit-dialog"
+      @close="dialogClose"
+    >
+      <RichText
+        v-model="remark_content"
+        toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
+        :wordlimit-num="false"
+        :height="240"
+        page-from="audit"
+      />
+      <div slot="footer">
+        <el-button @click="dialogClose">取消</el-button>
+        <el-button type="primary" :loading="submit_loading" @click="addCoursewareAuditRemark(select_node)">
+          确定
+        </el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import CoursewarePreview from '@/views/book/courseware/preview/CoursewarePreview.vue';
+import MenuPopover from '@/views/personal_workbench/common/MenuPopover.vue';
+import RichText from '@/components/RichText.vue';
+
+import {
+  GetBookCoursewareInfo,
+  GetProjectBaseInfo,
+  GetCoursewareAuditRemarkList,
+  AddCoursewareAuditRemark,
+  DeleteCoursewareAuditRemarkList,
+} from '@/api/project';
+import { ContentGetCoursewareContent_View, ChapterGetBookChapterStructExpandList } from '@/api/book';
+
+export default {
+  name: 'CommonPreview',
+  components: {
+    CoursewarePreview,
+    MenuPopover,
+    RichText,
+  },
+  props: {
+    projectId: {
+      type: String,
+      required: true,
+    },
+    id: {
+      type: String,
+      default: '',
+    },
+    // 是否是审校页面
+    isAudit: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      select_node: this.id,
+      courseware_info: {
+        book_name: '',
+        is_can_start_edit: 'false',
+        is_can_submit_audit: 'false',
+        is_can_audit_pass: 'false',
+        is_can_audit_reject: 'false',
+        is_can_add_audit_remark: 'false',
+        is_can_finish_audit: 'false',
+        is_can_request_shangjia_book: 'false',
+        is_can_request_rollback_project: 'false',
+        is_can_shangjia_book: 'false',
+        is_can_rollback_project: 'false',
+      },
+      background: {
+        background_image_url: '',
+        background_position: {
+          left: 0,
+          top: 0,
+        },
+      },
+      node_list: [],
+      data: { row_list: [] },
+      component_list: [],
+      remark_list: [],
+      visible: false,
+      remark_content: '',
+      submit_loading: false,
+    };
+  },
+  created() {
+    if (this.id) {
+      this.getBookCoursewareInfo(this.id);
+      this.getCoursewareComponentContent_View(this.id);
+      this.getCoursewareAuditRemarkList(this.id);
+    } else {
+      this.getProjectBaseInfo();
+    }
+    this.getBookChapterStructExpandList();
+  },
+  methods: {
+    getProjectBaseInfo() {
+      GetProjectBaseInfo({ id: this.projectId }).then(({ project_info }) => {
+        this.courseware_info = { ...project_info, book_name: project_info.name };
+      });
+    },
+    /**
+     * 得到教材课件信息
+     * @param {string} id - 课件ID
+     */
+    getBookCoursewareInfo(id) {
+      GetBookCoursewareInfo({ id, is_contain_producer: 'true', is_contain_auditor: 'true' }).then(
+        ({ courseware_info }) => {
+          this.courseware_info = courseware_info;
+        },
+      );
+    },
+    /**
+     * 得到课件内容(展示内容)
+     * @param {string} id - 课件ID
+     */
+    getCoursewareComponentContent_View(id) {
+      ContentGetCoursewareContent_View({ id }).then(({ content, component_list }) => {
+        if (content) {
+          this.data = JSON.parse(content);
+        } else {
+          this.data = { row_list: [] };
+        }
+
+        if (component_list) this.component_list = component_list;
+      });
+    },
+    /**
+     * 得到教材章节结构展开列表
+     */
+    getBookChapterStructExpandList() {
+      ChapterGetBookChapterStructExpandList({
+        book_id: this.projectId,
+        node_deep_mode: 0,
+        is_contain_producer: 'true',
+        is_contain_auditor: 'true',
+      }).then(({ node_list }) => {
+        this.node_list = node_list;
+      });
+    },
+    /**
+     * 选择节点
+     * @param {string} nodeId - 节点ID
+     */
+    selectNode(nodeId) {
+      this.getCoursewareComponentContent_View(nodeId);
+      this.getBookCoursewareInfo(nodeId);
+      this.getCoursewareAuditRemarkList(nodeId);
+      this.select_node = nodeId;
+    },
+    // 审校批注列表
+    getCoursewareAuditRemarkList(id) {
+      this.remark_list = [];
+      GetCoursewareAuditRemarkList({
+        courseware_id: id,
+      }).then(({ remark_list }) => {
+        this.remark_list = remark_list;
+      });
+    },
+    addRemark() {
+      this.remark_content = '';
+      this.visible = true;
+    },
+    dialogClose() {
+      this.visible = false;
+    },
+    // 添加审校批注
+    addCoursewareAuditRemark(id) {
+      this.submit_loading = true;
+      AddCoursewareAuditRemark({
+        courseware_id: id || this.id,
+        content: this.remark_content,
+      })
+        .then(() => {
+          this.submit_loading = false;
+          this.visible = false;
+          this.getCoursewareAuditRemarkList(id || this.id);
+        })
+        .catch(() => {
+          this.submit_loading = false;
+        });
+    },
+    // 删除批注
+    deleteRemarks(id) {
+      this.$confirm('确定要删除此条批注吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          DeleteCoursewareAuditRemarkList({ id }).then(() => {
+            this.getCoursewareAuditRemarkList(this.select_node ? this.select_node : this.id);
+            this.$message.success('删除成功!');
+          });
+        })
+        .catch(() => {});
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.common-preview {
+  &__header {
+    position: sticky;
+    top: 56px;
+    left: 0;
+    z-index: 9;
+    display: flex;
+    align-items: center;
+    height: 40px;
+    padding: 6px 4px;
+    margin-bottom: 5px;
+    background-color: #fff;
+    border-top: $border;
+    border-bottom: $border;
+
+    > .menu-container {
+      display: flex;
+      justify-content: space-between;
+      width: 360px;
+      padding: 4px 8px;
+      border-right: $border;
+    }
+
+    > .courseware {
+      display: flex;
+      flex-grow: 1;
+      column-gap: 16px;
+      align-items: center;
+      justify-content: space-between;
+      height: 40px;
+
+      .name-path {
+        min-width: 200px;
+        height: 40px;
+        padding: 4px 8px;
+        font-size: 14px;
+        line-height: 32px;
+        border-right: $border;
+      }
+
+      .flow-nodename {
+        flex: 1;
+      }
+
+      .operator {
+        display: flex;
+        column-gap: 8px;
+        align-items: center;
+
+        .link {
+          + .link {
+            margin-left: 0;
+
+            &::before {
+              margin-right: 8px;
+              color: #999;
+              content: '|';
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .main-container {
+    flex: 1;
+    min-width: 1110px;
+    overflow: auto;
+  }
+
+  main.preview-main {
+    display: flex;
+    flex: 1;
+    flex-direction: column;
+    row-gap: 5px;
+    width: 1100px;
+    min-width: 1100px;
+    min-height: 100%;
+    padding: 5px;
+    margin: 0 5px;
+    background-color: #fff;
+    border: 3px solid #f44444;
+    border-radius: 4px;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 10%);
+
+    .title {
+      display: inline-flex;
+      column-gap: 24px;
+      align-items: center;
+      width: 100%;
+      min-width: 280px;
+      height: 64px;
+      padding: 18px 24px;
+      font-size: 20px;
+      color: #fff;
+      background-color: #f44444;
+      border-top-left-radius: 12px;
+      border-bottom-right-radius: 16px;
+    }
+  }
+
+  .audit-content {
+    display: flex;
+    column-gap: 20px;
+    min-width: 1400px;
+    height: calc(100vh - 175px);
+
+    .remark-list {
+      width: 300px;
+      overflow: auto;
+      border: 1px solid #e5e5e5;
+
+      h5 {
+        padding: 0 5px;
+        margin: 0;
+        font-size: 18px;
+        line-height: 40px;
+        background: #f2f3f5;
+      }
+
+      .delete-btn {
+        padding-left: 10px;
+        color: #f44444;
+        border-left: 1px solid #e5e5e5;
+      }
+
+      ul {
+        height: calc(100% - 40px);
+        overflow: auto;
+
+        li {
+          border-bottom: 1px solid #e5e5e5;
+
+          > p {
+            padding: 5px;
+          }
+
+          :deep p {
+            margin: 0;
+          }
+
+          .remark-bottom {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 0 5px;
+            font-size: 14px;
+            color: #555;
+            border-top: 1px solid #e5e5e5;
+          }
+        }
+      }
+    }
+  }
+}
+
+:deep .audit-dialog {
+  .el-dialog__body {
+    padding: 5px 20px;
+  }
+}
+</style>
+
+<style lang="scss">
+.tox-tinymce-aux {
+  z-index: 9999 !important;
+}
+</style>

+ 7 - 1
src/layouts/default/header/index.vue

@@ -53,7 +53,7 @@ export default {
         { key: 'personal_center', name: '个人中心' },
       ],
       ORG_MANAGER: [
-        { key: 'project_manage', name: '项目管理' },
+        { key: 'project_manage/org/project', name: '项目管理' },
         { key: 'user_manage_org', name: '用户管理' },
         { key: 'personal_center', name: '个人中心' },
       ],
@@ -72,6 +72,11 @@ export default {
       projectList: projectListMap[userType] || projectListMap.USER,
     };
   },
+  created() {
+    let path = this.$route.path.split('/')[1] === 'personal_workbench' ? '' : this.$route.path.replace(/^\//, '');
+    this.activePro = path;
+    this.LoginNavIndex = this.projectList.findIndex((item) => item.key === path);
+  },
   methods: {
     logout() {
       this.$store.dispatch('user/signOut');
@@ -82,6 +87,7 @@ export default {
      * @param {string} key - 项目key
      */
     changeProject(key) {
+      if (this.activePro === key) return;
       this.LoginNavIndex = this.projectList.findIndex((item) => item.key === key);
       this.activePro = key;
       this.$router.push({ path: `/${key}` });

+ 1 - 1
src/router/guard/index.js

@@ -9,7 +9,7 @@ const whiteList = ['/login', '/image_change', '/register']; // 重定向白名
 // 用户类型对应的跳转路径
 export const userTypeToJump = {
   USER: '/',
-  ORG_MANAGER: '/project_manage',
+  ORG_MANAGER: '/project_manage/org/project',
   ADMIN: '/org_manage',
 };
 

+ 42 - 3
src/router/modules/project.js

@@ -88,22 +88,61 @@ const projectPage = {
   path: '/project_manage',
   name: 'ProjectManage',
   component: DEFAULT,
-  redirect: '/project_manage',
+  redirect: '/project_manage/project',
   meta: {
     title: '项目管理',
     icon: 'practice',
   },
   children: [
+    // 机构用户 -> 项目管理 -> 项目
     {
-      path: '/project_manage',
+      path: 'project',
       name: 'ProjectManageIndex',
       component: () => import('@/views/project_manage/project/index.vue'),
     },
+    // 机构用户 -> 项目管理 -> 项目 -> 预览项目
     {
-      path: '/project_manage/book',
+      path: 'project/preview/:projectId',
+      name: 'ProjectManagePreview',
+      component: () => import('@/views/project_manage/project/ProjectPreview.vue'),
+    },
+    // 机构用户 -> 项目管理 -> 已上架教材
+    {
+      path: 'book',
       name: 'ProjectManageBook',
       component: () => import('@/views/project_manage/book/index.vue'),
     },
+
+    // 机构管理员 -> 项目管理 -> 已立项
+    {
+      path: 'org/project',
+      name: 'ProjectManageOrgProject',
+      component: () => import('@/views/project_manage/org/project/index.vue'),
+    },
+    // 机构管理员 -> 项目管理 -> 已立项 -> 预览项目
+    {
+      path: 'org/project/preview/:projectId',
+      name: 'ProjectManageOrgProjectPreview',
+      component: () => import('@/views/project_manage/org/project/OrgProjectPreview.vue'),
+    },
+    // 机构管理员 -> 项目管理 -> 已终审
+    {
+      path: 'org/final',
+      name: 'ProjectManageFinal',
+      component: () => import('@/views/project_manage/org/final/index.vue'),
+    },
+    // 机构管理员 -> 项目管理 -> 已终审 -> 预览项目
+    {
+      path: 'org/final/preview/:projectId',
+      name: 'ProjectManageOrgFinalPreview',
+      component: () => import('@/views/project_manage/org/final/OrgFinalPreview.vue'),
+    },
+    // 机构管理员 -> 项目管理 -> 已上架教材
+    {
+      path: 'org/book',
+      name: 'ProjectManageBook',
+      component: () => import('@/views/project_manage/org/book/index.vue'),
+    },
   ],
 };
 

+ 5 - 1
src/styles/element.scss

@@ -9,6 +9,10 @@ div.el-table {
     font-size: 14px;
     color: $font-color;
   }
+
+  td.index-column {
+    background-color: $fill-color;
+  }
 }
 
 .el-pagination {
@@ -31,4 +35,4 @@ label.el-radio {
     background-color: $main-color;
     border-color: $main-color;
   }
-}
+}

+ 19 - 0
src/styles/mixin.scss

@@ -92,6 +92,25 @@
   box-shadow: 0 2px 4px rgba(0, 0, 0, 10%);
 }
 
+// 列表样式
+@mixin table-list {
+  &-list {
+    display: flex;
+    flex: 1;
+    flex-direction: column;
+    padding-bottom: 10px;
+
+    .el-table {
+      display: flex;
+      flex-direction: column;
+
+      :deep &__body-wrapper {
+        flex: 1;
+      }
+    }
+  }
+}
+
 @mixin page-content($isMenu: false) {
   display: flex;
   flex-direction: column;

+ 6 - 6
src/views/create_project/selectProjectMembers.vue

@@ -20,12 +20,12 @@
     </div>
 
     <el-table ref="user" :data="user_list" height="240">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column prop="real_name" label="真实姓名" width="140" />
-      <el-table-column prop="user_name" label="用户名" width="140" />
-      <el-table-column prop="email" label="邮箱" width="200" />
-      <el-table-column prop="phone" label="电话" width="120" />
-      <el-table-column prop="org_name" label="所属机构" />
+      <el-table-column type="selection" width="55" align="center" header-align="center" class-name="index-column" />
+      <el-table-column prop="real_name" label="真实姓名" width="140" header-align="center" />
+      <el-table-column prop="user_name" label="用户名" width="140" header-align="center" />
+      <el-table-column prop="email" label="邮箱" width="200" header-align="center" />
+      <el-table-column prop="phone" label="电话" width="120" header-align="center" />
+      <el-table-column prop="org_name" label="所属机构" header-align="center" />
     </el-table>
     <PaginationPage :total="total" @getList="getUserList" />
 

+ 11 - 9
src/views/org_manage/index.vue

@@ -19,17 +19,19 @@
           width="60"
           align="center"
           :index="(form.cur_page - 1) * form.page_capacity + 1"
+          class-name="index-column"
+          header-align="center"
         />
-        <el-table-column prop="name" label="名称" align="center" />
-        <el-table-column prop="user_count_max" label="最大用户量" align="center" width="120" />
-        <el-table-column prop="user_count" label="已注册用户数" align="center" width="120" />
-        <el-table-column prop="user_count_stop" label="已停用用户数" align="center" width="120" />
-        <el-table-column prop="project_count_max" label="最大项目量" align="center" width="120" />
-        <el-table-column prop="project_count" label="已创建项目数" align="center" width="120" />
-        <el-table-column prop="org_manager_name_desc" label="机构管理员" align="center" />
-        <el-table-column prop="create_time" label="创建时间" align="center" width="180" />
+        <el-table-column prop="name" label="名称" align="center" header-align="center" />
+        <el-table-column prop="user_count_max" label="最大用户量" align="center" width="120" header-align="center" />
+        <el-table-column prop="user_count" label="已注册用户数" align="center" width="120" header-align="center" />
+        <el-table-column prop="user_count_stop" label="已停用用户数" align="center" width="120" header-align="center" />
+        <el-table-column prop="project_count_max" label="最大项目量" align="center" width="120" header-align="center" />
+        <el-table-column prop="project_count" label="已创建项目数" align="center" width="120" header-align="center" />
+        <el-table-column prop="org_manager_name_desc" label="机构管理员" align="center" header-align="center" />
+        <el-table-column prop="create_time" label="创建时间" align="center" width="180" header-align="center" />
 
-        <el-table-column prop="operation" label="操作" fixed="right" width="200" align="center">
+        <el-table-column prop="operation" label="操作" fixed="right" width="200" align="center" header-align="center">
           <template slot-scope="{ row }">
             <span class="link" @click="setOrgManager(row.id)">机构管理信息</span>
             <span class="link danger" @click="deleteOrg(row.id)">删除</span>

+ 31 - 235
src/views/personal_workbench/check_task/audit/index.vue

@@ -2,203 +2,58 @@
   <div class="audit">
     <MenuPage cur-key="check_task" />
 
-    <div class="audit__header">
-      <div class="menu-container">
-        <MenuPopover :id="id" :node-list="node_list" :book-name="courseware_info.book_name" @selectNode="selectNode" />
-      </div>
-      <div class="courseware">
-        <span class="name-path">{{ courseware_info.name_path }}</span>
-        <span class="flow-nodename">{{ courseware_info.cur_audit_flow_node_name }}</span>
-        <div class="operator">
-          <span v-if="isTrue(courseware_info.is_can_add_audit_remark)" class="link" @click="addRemark">
-            添加审校批注
-          </span>
-          <span
-            v-if="isTrue(courseware_info.is_can_finish_audit)"
-            class="link"
-            @click="finishCoursewareCurFlowNodeAudit('false')"
-          >
-            审校完成
-          </span>
-          <span
-            v-if="isTrue(courseware_info.is_can_audit_pass)"
-            class="link"
-            @click="finishCoursewareCurFlowNodeAudit('true')"
-          >
-            审核通过
-          </span>
-          <span
-            v-if="isTrue(courseware_info.is_can_audit_reject)"
-            class="link"
-            @click="finishCoursewareCurFlowNodeAudit('false')"
-          >
-            审核驳回
-          </span>
-          <span class="link" @click="goBackBookList">返回教材列表</span>
-        </div>
-      </div>
-    </div>
-    <div class="audit-content">
-      <div class="main-container">
-        <main class="preview-main">
-          <span class="title">
-            <SvgIcon icon-class="menu-2" size="24" />
-            <span>{{ courseware_info.name_path }}</span>
-          </span>
-
-          <CoursewarePreview :data="data" :component-list="component_list" :background="background" />
-        </main>
-      </div>
-      <div class="remark-list">
-        <h5>审校批注</h5>
-        <ul v-if="remark_list.length > 0">
-          <li v-for="(item, index) in remark_list" :key="index">
-            <p v-html="item.content"></p>
-            <div class="remark-bottom">
-              <span>{{ item.remark_person_name + ':' + item.remark_time }}</span>
-              <el-button type="text" class="delete-btn" @click="deleteRemarks(item.id)">删除</el-button>
-            </div>
-          </li>
-        </ul>
-        <p v-else style="text-align: center">暂无批注</p>
-      </div>
-    </div>
-    <el-dialog
-      title="添加课件审校批注"
-      :visible="visible"
-      width="680px"
-      :close-on-click-modal="false"
-      class="audit-dialog"
-      @close="dialogClose"
-    >
-      <RichText
-        v-model="remark_content"
-        toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
-        :wordlimit-num="false"
-        :height="240"
-        page-from="audit"
-      />
-      <div slot="footer">
-        <el-button @click="dialogClose">取消</el-button>
-        <el-button type="primary" :loading="submit_loading" @click="addCoursewareAuditRemark(select_node)">
-          确定
-        </el-button>
-      </div>
-    </el-dialog>
+    <CommonPreview :id="id" ref="preview" :project-id="project_id" :is-audit="true">
+      <template #operator="{ courseware }">
+        <span v-if="isTrue(courseware.is_can_add_audit_remark)" class="link" @click="addRemark">添加审校批注</span>
+        <span
+          v-if="isTrue(courseware.is_can_finish_audit)"
+          class="link"
+          @click="finishCoursewareCurFlowNodeAudit('false')"
+        >
+          审校完成
+        </span>
+        <span
+          v-if="isTrue(courseware.is_can_audit_pass)"
+          class="link"
+          @click="finishCoursewareCurFlowNodeAudit('true')"
+        >
+          审核通过
+        </span>
+        <span
+          v-if="isTrue(courseware.is_can_audit_reject)"
+          class="link"
+          @click="finishCoursewareCurFlowNodeAudit('false')"
+        >
+          审核驳回
+        </span>
+        <span class="link" @click="goBackBookList">返回教材列表</span>
+      </template>
+    </CommonPreview>
   </div>
 </template>
 
 <script>
 import MenuPage from '@/views/personal_workbench/common/menu.vue';
-import CoursewarePreview from '@/views/book/courseware/preview/CoursewarePreview.vue';
-import MenuPopover from '@/views/personal_workbench/common/MenuPopover.vue';
+import CommonPreview from '@/components/CommonPreview.vue';
 
 import { isTrue } from '@/utils/common';
-import {
-  GetBookCoursewareInfo,
-  FinishCoursewareCurFlowNodeAudit,
-  addCoursewareAuditRemark,
-  getCoursewareAuditRemarkList,
-  deleteCoursewareAuditRemarkList,
-} from '@/api/project';
-import { ContentGetCoursewareContent_View, ChapterGetBookChapterStructExpandList } from '@/api/book';
-
-import RichText from '@/components/RichText.vue';
+import { FinishCoursewareCurFlowNodeAudit } from '@/api/project';
 
 export default {
   name: 'AuditTaskPage',
   components: {
     MenuPage,
-    CoursewarePreview,
-    MenuPopover,
-    RichText,
+    CommonPreview,
   },
   data() {
     return {
       id: this.$route.params.id || '',
       project_id: this.$route.query.project_id || '',
-      courseware_info: {
-        is_can_add_audit_remark: 'false',
-        is_can_finish_audit: 'false',
-        is_can_audit_pass: 'false',
-        is_can_audit_reject: 'false',
-        cur_audit_flow_node_type: 0,
-      },
-      background: {
-        background_image_url: '',
-        background_position: {
-          left: 0,
-          top: 0,
-        },
-      },
-      node_list: [],
-      data: { row_list: [] },
-      component_list: [],
       isTrue,
-      visible: false,
-      remark_content: '',
-      submit_loading: false, // 确定按钮loading
-      select_node: '', // 选中节点课件id
-      remark_list: [], // 批注列表
     };
   },
-  created() {
-    this.getBookCoursewareInfo(this.id);
-    this.getCoursewareComponentContent_View(this.id);
-    this.getBookChapterStructExpandList();
-    this.getCoursewareAuditRemarkList(this.id);
-  },
   methods: {
     /**
-     * 得到教材课件信息
-     * @param {string} id - 课件ID
-     */
-    getBookCoursewareInfo(id) {
-      GetBookCoursewareInfo({ id, is_contain_producer: 'true', is_contain_auditor: 'true' }).then(
-        ({ courseware_info }) => {
-          this.courseware_info = courseware_info;
-        },
-      );
-    },
-    /**
-     * 得到课件内容(展示内容)
-     * @param {string} id - 课件ID
-     */
-    getCoursewareComponentContent_View(id) {
-      ContentGetCoursewareContent_View({ id }).then(({ content, component_list }) => {
-        if (content) {
-          this.data = JSON.parse(content);
-        } else {
-          this.data = { row_list: [] };
-        }
-
-        if (component_list) this.component_list = component_list;
-      });
-    },
-    /**
-     * 得到教材章节结构展开列表
-     */
-    getBookChapterStructExpandList() {
-      ChapterGetBookChapterStructExpandList({
-        book_id: this.project_id,
-        node_deep_mode: 0,
-        is_contain_producer: 'true',
-        is_contain_auditor: 'true',
-      }).then(({ node_list }) => {
-        this.node_list = node_list;
-      });
-    },
-    /**
-     * 选择节点
-     * @param {string} nodeId - 节点ID
-     */
-    selectNode(nodeId) {
-      this.getCoursewareComponentContent_View(nodeId);
-      this.getBookCoursewareInfo(nodeId);
-      this.getCoursewareAuditRemarkList(nodeId);
-      this.select_node = nodeId;
-    },
-    /**
      * 提交课件到审校流程
      * @param {'true'|'false'} is_pass - 是否通过审校
      */
@@ -217,66 +72,7 @@ export default {
 
     // 点击添加审校意见
     addRemark() {
-      this.remark_content = '';
-      this.visible = true;
-    },
-
-    dialogClose() {
-      this.visible = false;
-    },
-
-    // 添加审校批注
-    addCoursewareAuditRemark(id) {
-      this.submit_loading = true;
-      addCoursewareAuditRemark({
-        courseware_id: id || this.id,
-        content: this.remark_content,
-      })
-        .then((res) => {
-          this.submit_loading = false;
-          if (res.status === 1) {
-            this.visible = false;
-            this.getCoursewareAuditRemarkList(id || this.id);
-          }
-        })
-        .catch(() => {
-          this.submit_loading = false;
-        });
-    },
-
-    // 审校批注列表
-    getCoursewareAuditRemarkList(id) {
-      this.remark_list = [];
-      getCoursewareAuditRemarkList({
-        courseware_id: id,
-      }).then((res) => {
-        if (res.status === 1) {
-          this.remark_list = res.remark_list;
-        }
-      });
-    },
-
-    // 删除批注
-    deleteRemarks(id) {
-      let _this = this;
-      _this
-        .$confirm('确定要删除此条批注吗?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'warning',
-        })
-        .then(function () {
-          deleteCoursewareAuditRemarkList({ id }).then((res) => {
-            if (res.status === 1) {
-              _this.getCoursewareAuditRemarkList(_this.select_node ? _this.select_node : _this.id);
-              _this.$message({
-                type: 'success',
-                message: '删除成功!',
-              });
-            }
-          });
-        })
-        .catch(() => {});
+      this.$refs.preview.addRemark();
     },
   },
 };

+ 7 - 0
src/views/personal_workbench/common/MenuPopover.vue

@@ -131,3 +131,10 @@ export default {
   }
 }
 </style>
+
+<style lang="scss">
+.menu-popover {
+  max-height: calc(100vh - 200px);
+  overflow: auto;
+}
+</style>

+ 9 - 12
src/views/personal_workbench/common/menu.vue

@@ -2,10 +2,10 @@
   <div class="menu">
     <ul class="menu-list">
       <li
-        v-for="({ key, name }, i) in subMenuList"
+        v-for="{ key, name } in subMenuList"
         :key="key"
-        :class="['menu-item', { active: currentIndex === i }]"
-        @click="changeSubMenu(i)"
+        :class="['menu-item', { active: key === activeKey }]"
+        @click="changeSubMenu(key)"
       >
         {{ name }}
       </li>
@@ -24,7 +24,7 @@ export default {
   },
   data() {
     return {
-      currentIndex: 0, // 当前选中的菜单索引
+      activeKey: this.curKey,
       // 子菜单列表
       subMenuList: [
         { key: 'edit_task', name: '我的编辑任务' },
@@ -36,18 +36,15 @@ export default {
       ],
     };
   },
-  created() {
-    // 根据当前路由的路径设置当前选中的菜单索引
-    this.currentIndex = this.subMenuList.findIndex((item) => item.key === this.curKey);
-  },
   methods: {
     /**
      * 切换子菜单
-     * @param {number} index - 子菜单索引
+     * @param {number} key - 子菜单键
      */
-    changeSubMenu(index) {
-      this.currentIndex = index;
-      this.$router.push({ path: `/personal_workbench/${this.subMenuList[index].key}` });
+    changeSubMenu(key) {
+      if (key === this.activeKey) return;
+      this.activeKey = key;
+      this.$router.push({ path: `/personal_workbench/${key}` });
     },
   },
 };

+ 13 - 136
src/views/personal_workbench/edit_task/preview/index.vue

@@ -2,79 +2,38 @@
   <div class="task-preview">
     <MenuPage cur-key="edit_task" />
 
-    <div class="task-preview__header">
-      <div class="menu-container">
-        <MenuPopover :id="id" :node-list="node_list" :book-name="courseware_info.book_name" @selectNode="selectNode" />
-      </div>
-      <div class="courseware">
-        <span class="name-path">{{ courseware_info.name_path }}</span>
-        <div class="operator">
-          <span v-if="isTrue(courseware_info.is_can_start_edit)" class="link" @click="editTask">开始编辑</span>
-          <span v-if="isTrue(courseware_info.is_can_submit_audit)" class="link" @click="submitCoursewareToAuditFlow">
-            提交审校
-          </span>
-          <span class="link" @click="goBackBookList">返回教材列表</span>
-        </div>
-      </div>
-    </div>
-
-    <main class="preview-main">
-      <span class="title">
-        <SvgIcon icon-class="menu-2" size="24" />
-        <span>{{ courseware_info.name_path }}</span>
-      </span>
-
-      <CoursewarePreview :data="data" :component-list="component_list" :background="background" />
-    </main>
+    <CommonPreview :id="id" ref="preview" :project-id="project_id">
+      <template #operator="{ courseware }">
+        <span v-if="isTrue(courseware.is_can_start_edit)" class="link" @click="editTask">开始编辑</span>
+        <span v-if="isTrue(courseware.is_can_submit_audit)" class="link" @click="submitCoursewareToAuditFlow">
+          提交审校
+        </span>
+        <span class="link" @click="goBackBookList">返回教材列表</span>
+      </template>
+    </CommonPreview>
   </div>
 </template>
 
 <script>
 import MenuPage from '@/views/personal_workbench/common/menu.vue';
-import CoursewarePreview from '@/views/book/courseware/preview/CoursewarePreview.vue';
-import MenuPopover from '@/views/personal_workbench/common/MenuPopover.vue';
+import CommonPreview from '@/components/CommonPreview.vue';
 
-import { GetBookCoursewareInfo, SubmitBookCoursewareToAuditFlow } from '@/api/project';
-import { ContentGetCoursewareContent_View, ChapterGetBookChapterStructExpandList } from '@/api/book';
+import { SubmitBookCoursewareToAuditFlow } from '@/api/project';
 import { isTrue } from '@/utils/common';
 
 export default {
   name: 'TaskPreviewPage',
   components: {
     MenuPage,
-    CoursewarePreview,
-    MenuPopover,
+    CommonPreview,
   },
   data() {
     return {
       id: this.$route.params.id,
       project_id: this.$route.query.project_id,
-      courseware_info: {
-        is_my_edit_task: 'false',
-        is_my_audit_task: 'false',
-      },
-      background: {
-        background_image_url: '',
-        background_position: {
-          left: 0,
-          top: 0,
-          width: 100,
-          height: 100,
-        },
-      },
-      data: {
-        row_list: [],
-      },
-      component_list: [],
-      node_list: [],
       isTrue,
     };
   },
-  created() {
-    this.getBookCoursewareInfo(this.id);
-    this.getCoursewareComponentContent_View(this.id);
-    this.getBookChapterStructExpandList();
-  },
   methods: {
     goBackBookList() {
       this.$router.push({ path: '/personal_workbench/edit_task' });
@@ -91,68 +50,9 @@ export default {
     submitCoursewareToAuditFlow() {
       SubmitBookCoursewareToAuditFlow({ id: this.id }).then(() => {
         this.$message.success('课件已提交审校');
-        this.getBookCoursewareInfo(this.id);
-      });
-    },
-    /**
-     * 得到课件内容(展示内容)
-     * @param {string} id - 课件ID
-     */
-    getCoursewareComponentContent_View(id) {
-      ContentGetCoursewareContent_View({ id }).then(({ content, component_list }) => {
-        if (content) {
-          this.data = JSON.parse(content);
-        } else {
-          this.data = { row_list: [] };
-        }
-
-        if (component_list) this.component_list = component_list;
+        this.$refs.preview.getBookCoursewareInfo(this.id);
       });
     },
-    /**
-     * 得到教材课件信息
-     * @param {string} id - 课件ID
-     */
-    getBookCoursewareInfo(id) {
-      GetBookCoursewareInfo({ id, is_contain_producer: 'true', is_contain_auditor: 'true' }).then(
-        ({ courseware_info }) => {
-          this.courseware_info = courseware_info;
-        },
-      );
-    },
-    /**
-     * 得到教材章节结构展开列表
-     */
-    getBookChapterStructExpandList() {
-      ChapterGetBookChapterStructExpandList({
-        book_id: this.project_id,
-        node_deep_mode: 0,
-        is_contain_producer: 'true',
-        is_contain_auditor: 'true',
-      }).then(({ node_list }) => {
-        this.node_list = node_list;
-      });
-    },
-    /**
-     * 选择节点
-     * @param {string} nodeId - 节点ID
-     */
-    selectNode(nodeId) {
-      this.getCoursewareComponentContent_View(nodeId);
-      this.getBookCoursewareInfo(nodeId);
-    },
-    /**
-     * 计算章节名称样式
-     * @param {number} deep - 节点深度
-     * @param {boolean} isLeaf - 是否是叶子节点
-     * @returns {Object} - 样式对象
-     */
-    computedNameStyle(deep, isLeaf) {
-      return {
-        'padding-left': `${(deep - 1) * 16}px`,
-        cursor: isLeaf ? 'pointer' : 'auto',
-      };
-    },
   },
 };
 </script>
@@ -162,28 +62,5 @@ export default {
 
 .task-preview {
   @include page-content(true);
-
-  main.preview-main {
-    flex: 1;
-    width: 1100px;
-    margin: 0 auto 10px;
-    background-color: #fff;
-    border: 3px solid #f44444;
-    border-radius: 16px;
-
-    .title {
-      display: inline-flex;
-      column-gap: 24px;
-      align-items: center;
-      min-width: 280px;
-      height: 64px;
-      padding: 18px 24px;
-      font-size: 20px;
-      color: #fff;
-      background-color: #f44444;
-      border-top-left-radius: 12px;
-      border-bottom-right-radius: 16px;
-    }
-  }
 }
 </style>

+ 15 - 17
src/views/personal_workbench/project/index.vue

@@ -4,13 +4,18 @@
 
     <div class="project-list">
       <el-table :data="list">
-        <el-table-column prop="sn" label="编号" width="140" />
-        <el-table-column prop="name" label="名称" width="240" />
-        <el-table-column prop="create_time" label="创建时间" width="180" />
-        <el-table-column prop="leader_name_desc" label="项目组长" width="260" />
-        <el-table-column prop="member_name_desc" label="项目成员" />
+        <el-table-column label="序号" width="80" header-align="center" align="center" class-name="index-column">
+          <template slot-scope="{ $index }">
+            {{ cur_page_begin_index + $index }}
+          </template>
+        </el-table-column>
+        <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="create_time" label="创建时间" width="180" header-align="center" />
+        <el-table-column prop="leader_name_desc" label="项目组长" width="260" header-align="center" />
+        <el-table-column prop="member_name_desc" label="项目成员" header-align="center" />
 
-        <el-table-column prop="operation" label="操作" fixed="right" width="300">
+        <el-table-column label="操作" fixed="right" width="260" header-align="center">
           <template slot-scope="{ row }">
             <span class="link" @click="projectInfoManage(row.id, true)">项目信息管理</span>
             <span class="link" @click="productionEditorialManage(row.id)">制作与审校管理</span>
@@ -40,13 +45,15 @@ export default {
     return {
       list: [],
       total: 0,
+      cur_page_begin_index: 0,
     };
   },
   methods: {
     queryMyProjectList_Leader(data) {
-      PageQueryMyProjectList_Leader(data).then(({ total_count, project_list }) => {
+      PageQueryMyProjectList_Leader(data).then(({ total_count, cur_page_begin_index, project_list }) => {
         this.total = total_count;
         this.list = project_list;
+        this.cur_page_begin_index = cur_page_begin_index;
       });
     },
     /**
@@ -73,15 +80,6 @@ export default {
 
 .project {
   @include page-base;
-
-  &-list {
-    display: flex;
-    flex: 1;
-    flex-direction: column;
-
-    .el-table {
-      flex: 1;
-    }
-  }
+  @include table-list;
 }
 </style>

+ 22 - 13
src/views/project_manage/book/index.vue

@@ -4,11 +4,28 @@
 
     <div class="book-list">
       <el-table :data="list">
-        <el-table-column prop="sn" label="编号" width="140" />
-        <el-table-column prop="name" label="名称" width="240" />
+        <el-table-column label="序号" width="80" class-name="index-column" align="center" header-align="center">
+          <template slot-scope="{ $index }">
+            {{ cur_page_begin_index + $index }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="sn" label="编号" width="120" header-align="center" />
+        <el-table-column prop="name" label="名称" width="240" header-align="center" />
+        <el-table-column prop="version" label="版本" width="100" header-align="center" />
+        <el-table-column prop="project_sn" label="项目编号" width="120" header-align="center" />
+        <el-table-column prop="project_name" label="项目名称" width="180" header-align="center" />
+        <el-table-column prop="project_leader" label="项目组长" width="180" header-align="center" />
+        <el-table-column prop="create_date" label="创建日期" width="140" header-align="center" />
+        <el-table-column prop="content_count_SJ" label="教材内容数" width="120" header-align="center" />
+        <el-table-column prop="publish_date" label="上架日期" header-align="center" />
+        <el-table-column label="操作" fixed="right" width="150" header-align="center">
+          <template slot-scope="{ row }">
+            <span class="link danger">申请下架</span>
+          </template>
+        </el-table-column>
       </el-table>
 
-      <PaginationPage :total="total" @getList="queryMyProjectList_Leader" />
+      <PaginationPage :total="total" @getList="" />
     </div>
   </div>
 </template>
@@ -27,6 +44,7 @@ export default {
     return {
       list: [],
       total: 0,
+      cur_page_begin_index: 0,
     };
   },
   methods: {},
@@ -38,15 +56,6 @@ export default {
 
 .book {
   @include page-base;
-
-  &-list {
-    display: flex;
-    flex: 1;
-    flex-direction: column;
-
-    .el-table {
-      flex: 1;
-    }
-  }
+  @include table-list;
 }
 </style>

+ 77 - 4
src/views/project_manage/common/ProjectMenu.vue

@@ -1,8 +1,21 @@
 <template>
-  <div></div>
+  <div class="menu">
+    <ul class="menu-list">
+      <li
+        v-for="{ key, name } in subMenuList"
+        :key="key"
+        :class="['menu-item', { active: activeKey === key }]"
+        @click="changeSubMenu(key)"
+      >
+        {{ name }}
+      </li>
+    </ul>
+  </div>
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
+
 export default {
   name: 'ProjectMenu',
   props: {
@@ -12,10 +25,70 @@ export default {
     },
   },
   data() {
-    return {};
+    const token = getToken();
+
+    const userType = (token?.user_type ?? 'USER').toUpperCase();
+    const subMenuMap = {
+      USER: [
+        { key: 'project', name: '项目' },
+        { key: 'book', name: '已上架教材' },
+      ],
+      ORG_MANAGER: [
+        { key: 'org/project', name: '已立项' },
+        { key: 'org/final', name: '已终审' },
+        { key: 'org/book', name: '已上架教材' },
+      ],
+    };
+
+    return {
+      activeKey: this.curKey,
+      subMenuList: subMenuMap[userType] || [],
+    };
+  },
+  methods: {
+    /**
+     * 切换子菜单
+     * @param {number} key - 子菜单键
+     */
+    changeSubMenu(key) {
+      if (key === this.activeKey) return;
+      this.activeKey = key;
+      this.$router.push({ path: `/project_manage/${key}` });
+    },
   },
-  methods: {},
 };
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.menu {
+  z-index: 9;
+  display: flex;
+  background-color: $main-background-color;
+
+  &-list {
+    display: flex;
+    justify-content: space-around;
+    background-color: #f5f5f5;
+    border-radius: 8px;
+
+    .menu-item {
+      padding: 10px 20px;
+      cursor: pointer;
+      background-color: #ebebeb;
+      border: $border;
+      border-radius: 4px;
+      transition: background-color 0.3s ease;
+
+      &:hover {
+        background-color: #e0e0e0;
+      }
+
+      &.active {
+        font-weight: bold;
+        background-color: #fff;
+        box-shadow: 0 2px 4px #e6e6e6;
+      }
+    }
+  }
+}
+</style>

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

@@ -0,0 +1,29 @@
+<template>
+  <div class="book org">
+    <ProjectMenu cur-key="org/book" />
+  </div>
+</template>
+
+<script>
+import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
+
+export default {
+  name: 'OrgBookPage',
+  components: {
+    ProjectMenu,
+  },
+  data() {
+    return {};
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.book {
+  @include page-base;
+  @include table-list;
+}
+</style>

+ 70 - 0
src/views/project_manage/org/final/OrgFinalPreview.vue

@@ -0,0 +1,70 @@
+<template>
+  <div class="org-final-preview">
+    <ProjectMenu cur-key="org/final" />
+
+    <CommonPreview ref="preview" :project-id="project_id">
+      <template #operator="{ courseware }">
+        <span class="link">查看项目信息</span>
+        <span class="link">查看教材信息</span>
+        <span v-if="isTrue(courseware.is_can_shangjia_book)" class="link" @click="shangjiaBook">上架</span>
+        <span v-if="isTrue(courseware.is_can_rollback_project)" class="link" @click="rollbackProject">退改</span>
+        <span class="link" @click="goBackBookList">返回教材列表</span>
+      </template>
+    </CommonPreview>
+  </div>
+</template>
+
+<script>
+import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
+import CommonPreview from '@/components/CommonPreview.vue';
+
+import { isTrue } from '@/utils/common';
+import { ShangjiaBook, RollbackProject } from '@/api/project';
+
+export default {
+  name: 'OrgFinalPreview',
+  components: {
+    ProjectMenu,
+    CommonPreview,
+  },
+  data() {
+    return {
+      project_id: this.$route.params.projectId || '',
+      isTrue,
+    };
+  },
+  methods: {
+    shangjiaBook() {
+      ShangjiaBook({ project_id: this.project_id }).then(() => {
+        this.$message.success('上架成功');
+        this.$refs.preview.getProjectBaseInfo();
+      });
+    },
+    rollbackProject() {
+      this.$confirm('确定要退改吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          RollbackProject({ project_id: this.project_id }).then(() => {
+            this.$message.success('退改成功');
+            this.$refs.preview.getProjectBaseInfo();
+          });
+        })
+        .catch(() => {});
+    },
+    goBackBookList() {
+      this.$router.push({ path: '/project_manage/org/final' });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.org-final-preview {
+  @include page-content(true);
+}
+</style>

+ 86 - 0
src/views/project_manage/org/final/index.vue

@@ -0,0 +1,86 @@
+<template>
+  <div class="final">
+    <ProjectMenu cur-key="org/final" />
+
+    <div class="final-list">
+      <el-table :data="list">
+        <el-table-column label="序号" width="80" align="center" header-align="center" class-name="index-column">
+          <template slot-scope="{ $index }">
+            {{ cur_page_begin_index + $index }}
+          </template>
+        </el-table-column>
+        <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="org_name" label="所属机构" width="120" header-align="center" />
+        <el-table-column prop="leader_name_desc" label="项目组长" width="120" header-align="center" />
+        <el-table-column prop="create_date" label="创建日期" width="180" header-align="center" />
+        <el-table-column prop="content_count_SJ" label="教材内容数" width="100" header-align="center" />
+        <el-table-column label="申请状态" width="80" align="center">
+          <template slot-scope="{ row }">
+            <span :style="{ color: row.request_status === 2 ? '#f53f3f' : '' }">
+              {{ requestStatusList[row.request_status] }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="version" label="申请上架教材版本" width="80" align="center" header-align="center" />
+        <el-table-column prop="version_desc_YSJ" label="已上架教材版本" header-align="center" />
+
+        <el-table-column label="操作" fixed="right" width="240" align="center" header-align="center">
+          <template slot-scope="{ row }">
+            <span class="link">查看信息</span>
+            <span class="link" @click="previewProject(row.id)">预览项目</span>
+            <span class="link">预览历史版本</span>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <PaginationPage :total="total" @getList="pageQueryProjectList_OrgManager" />
+    </div>
+  </div>
+</template>
+
+<script>
+import PaginationPage from '@/components/PaginationPage.vue';
+import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
+
+import { PageQueryProjectList_OrgManager } from '@/api/list';
+
+export default {
+  name: 'OrgFinalPage',
+  components: {
+    PaginationPage,
+    ProjectMenu,
+  },
+  data() {
+    return {
+      list: [],
+      total: 0,
+      cur_page_begin_index: 0,
+      requestStatusList: ['无', '申请上架', '申请退改'],
+    };
+  },
+  methods: {
+    pageQueryProjectList_OrgManager(data) {
+      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;
+        },
+      );
+    },
+    previewProject(projectId) {
+      this.$router.push({ path: `/project_manage/org/final/preview/${projectId}` });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.final {
+  @include page-base;
+  @include table-list;
+}
+</style>

+ 42 - 0
src/views/project_manage/org/project/OrgProjectPreview.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="org-project-preview">
+    <ProjectMenu cur-key="org/project" />
+
+    <CommonPreview ref="preview" :project-id="project_id">
+      <template #operator>
+        <span class="link" @click="goBackBookList">返回教材列表</span>
+      </template>
+    </CommonPreview>
+  </div>
+</template>
+
+<script>
+import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
+import CommonPreview from '@/components/CommonPreview.vue';
+
+export default {
+  name: 'OrgProjectPreview',
+  components: {
+    ProjectMenu,
+    CommonPreview,
+  },
+  data() {
+    return {
+      project_id: this.$route.params.projectId || '',
+    };
+  },
+  methods: {
+    goBackBookList() {
+      this.$router.push({ path: '/project_manage/org/project' });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.org-project-preview {
+  @include page-content(true);
+}
+</style>

+ 76 - 0
src/views/project_manage/org/project/index.vue

@@ -0,0 +1,76 @@
+<template>
+  <div class="project org">
+    <ProjectMenu cur-key="org/project" />
+
+    <div class="project-list">
+      <el-table :data="list">
+        <el-table-column label="序号" width="80" align="center" header-align="center" class-name="index-column">
+          <template slot-scope="{ $index }">
+            {{ cur_page_begin_index + $index }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="sn" label="编号" width="140" header-align="center" />
+        <el-table-column prop="name" label="名称" width="260" header-align="center" />
+        <el-table-column prop="org_name" label="所属机构" width="120" header-align="center" />
+        <el-table-column prop="leader_name_desc" label="项目组长" width="140" header-align="center" />
+        <el-table-column prop="create_date" label="创建日期" width="180" header-align="center" />
+        <el-table-column prop="content_count_SJ" label="教材内容数" width="100" header-align="center" />
+        <el-table-column prop="" label="教材内容审核通过数" header-align="center" />
+
+        <el-table-column fixed="right" label="操作" width="150" align="center" header-align="center">
+          <template slot-scope="{ row }">
+            <el-button type="text" size="small" @click="previewProject(row.id)">预览项目</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <PaginationPage :total="total" @getList="pageQueryProjectList_OrgManager" />
+    </div>
+  </div>
+</template>
+
+<script>
+import PaginationPage from '@/components/PaginationPage.vue';
+import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
+
+import { PageQueryProjectList_OrgManager } from '@/api/list';
+
+export default {
+  name: 'OrgProjectPage',
+  components: {
+    PaginationPage,
+    ProjectMenu,
+  },
+  data() {
+    return {
+      list: [],
+      total: 0,
+      cur_page_begin_index: 0,
+      requestStatusList: ['无', '申请上架', '申请退改'],
+    };
+  },
+  methods: {
+    pageQueryProjectList_OrgManager(data) {
+      PageQueryProjectList_OrgManager({ ...data, status: 0 }).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;
+        },
+      );
+    },
+    previewProject(projectId) {
+      this.$router.push({ path: `/project_manage/org/project/preview/${projectId}` });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.project {
+  @include page-base;
+  @include table-list;
+}
+</style>

+ 66 - 0
src/views/project_manage/project/ProjectPreview.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="audit">
+    <ProjectMenu cur-key="project" />
+
+    <CommonPreview ref="preview" :project-id="project_id">
+      <template #operator="{ courseware }">
+        <span v-if="isTrue(courseware.is_can_request_shangjia_book)" class="link" @click="requestShangjiaBook">
+          申请上架
+        </span>
+        <span v-if="isTrue(courseware.is_can_request_rollback_project)" class="link" @click="requestRollbackProject">
+          申请退回
+        </span>
+        <span class="link" @click="goBackBookList">返回教材列表</span>
+      </template>
+    </CommonPreview>
+  </div>
+</template>
+
+<script>
+import ProjectMenu from '@/views/project_manage/common/ProjectMenu.vue';
+import CommonPreview from '@/components/CommonPreview.vue';
+
+import { isTrue } from '@/utils/common';
+import { RequestShangjiaBook, RequestRollbackProject } from '@/api/project';
+
+export default {
+  name: 'AuditTaskPage',
+  components: {
+    ProjectMenu,
+    CommonPreview,
+  },
+  data() {
+    return {
+      project_id: this.$route.params.projectId || '',
+      isTrue,
+    };
+  },
+  methods: {
+    goBackBookList() {
+      this.$router.push({ path: `/project_manage/project` });
+    },
+    // 申请上架
+    requestShangjiaBook() {
+      RequestShangjiaBook({ project_id: this.project_id }).then(() => {
+        this.$message.success('申请上架成功');
+        this.$refs.preview.getProjectBaseInfo();
+      });
+    },
+    // 申请退回
+    requestRollbackProject() {
+      RequestRollbackProject({ project_id: this.project_id }).then(() => {
+        this.$message.success('申请退回成功');
+        this.$refs.preview.getProjectBaseInfo();
+      });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.audit {
+  @include page-content(true);
+}
+</style>

+ 27 - 13
src/views/project_manage/project/index.vue

@@ -4,8 +4,26 @@
 
     <div class="project-manage-list">
       <el-table :data="list">
-        <el-table-column prop="sn" label="编号" width="140" />
-        <el-table-column prop="name" label="名称" width="240" />
+        <el-table-column label="序号" width="80" align="center" header-align="center" class-name="index-column">
+          <template slot-scope="{ $index }">
+            {{ cur_page_begin_index + $index }}
+          </template>
+        </el-table-column>
+        <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="create_date" 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="申请状态" width="120" header-align="center" />
+        <el-table-column prop="version" label="教材版本" width="120" header-align="center" />
+        <el-table-column prop="version_desc_YSJ" label="已上架教材版本" header-align="center" />
+        <el-table-column label="操作" fixed="right" width="240" align="center" header-align="center">
+          <template slot-scope="{ row }">
+            <span class="link">查看信息</span>
+            <span class="link" @click="previewProject(row.id)">预览项目</span>
+            <span class="link">预览历史版本</span>
+          </template>
+        </el-table-column>
       </el-table>
 
       <PaginationPage :total="total" @getList="queryMyProjectList_Leader" />
@@ -29,15 +47,20 @@ export default {
     return {
       list: [],
       total: 0,
+      cur_page_begin_index: 0,
     };
   },
   methods: {
     queryMyProjectList_Leader(data) {
-      PageQueryMyProjectList_Leader(data).then(({ total_count, project_list }) => {
+      PageQueryMyProjectList_Leader(data).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;
       });
     },
+    previewProject(projectId) {
+      this.$router.push({ path: `/project_manage/project/preview/${projectId}` });
+    },
   },
 };
 </script>
@@ -47,15 +70,6 @@ export default {
 
 .project-manage {
   @include page-base;
-
-  &-list {
-    display: flex;
-    flex: 1;
-    flex-direction: column;
-
-    .el-table {
-      flex: 1;
-    }
-  }
+  @include table-list;
 }
 </style>

+ 12 - 11
src/views/user_manage/index.vue

@@ -54,26 +54,27 @@
           width="60"
           align="center"
           :index="(form.cur_page - 1) * form.page_capacity + 1"
+          header-align="center"
+          class-name="index-column"
         />
-        <el-table-column prop="real_name" width="130" label="真实姓名" align="center" />
-        <el-table-column prop="user_name" width="130" label="用户名" align="center" />
-        <el-table-column prop="email" label="邮箱" align="center" />
-        <el-table-column prop="org_name" label="所属机构" align="center" />
-        <el-table-column prop="is_org_manager" label="机构管理员" align="center" width="120">
+        <el-table-column prop="real_name" width="130" label="真实姓名" align="center" header-align="center" />
+        <el-table-column prop="user_name" width="130" label="用户名" align="center" header-align="center" />
+        <el-table-column prop="email" label="邮箱" align="center" header-align="center" />
+        <el-table-column prop="org_name" label="所属机构" align="center" header-align="center" />
+        <el-table-column prop="is_org_manager" label="机构管理员" align="center" width="120" header-align="center">
           <template slot-scope="{ row }">
             <i v-if="row.is_org_manager === 'true'" class="el-icon-check"></i>
           </template>
         </el-table-column>
-        <el-table-column prop="register_time" label="注册时间" align="center" width="170" />
-        <el-table-column prop="is_stop" label="已停用" align="center"
-width="100"
-          ><template slot-scope="{ row }">
+        <el-table-column prop="register_time" label="注册时间" align="center" width="170" header-align="center" />
+        <el-table-column prop="is_stop" label="已停用" align="center" width="100" header-align="center">
+          <template slot-scope="{ row }">
             <i v-if="row.is_stop === 'true'" class="el-icon-check"></i>
           </template>
         </el-table-column>
-        <el-table-column prop="stop_time" label="停用时间" align="center" width="170" />
+        <el-table-column prop="stop_time" label="停用时间" align="center" width="170" header-align="center" />
 
-        <el-table-column prop="operation" label="操作" fixed="right" width="200" align="center">
+        <el-table-column prop="operation" label="操作" fixed="right" width="200" align="center" header-align="center">
           <template slot-scope="{ row }">
             <span class="link" @click="setOrgManager(row)">{{
               row.is_org_manager === 'true' ? '取消机构管理员' : '设为机构管理员'

+ 10 - 8
src/views/user_manage_org/index.vue

@@ -52,28 +52,30 @@
       <el-table v-if="tableHeight" :data="list" :max-height="tableHeight + 'px'">
         <el-table-column
           type="index"
+          class-name="index-column"
           label="序号"
           width="60"
           align="center"
           :index="(form.cur_page - 1) * form.page_capacity + 1"
+          header-align="center"
         />
-        <el-table-column prop="real_name" label="真实姓名" align="center" />
-        <el-table-column prop="user_name" label="用户名" align="center" />
-        <el-table-column prop="email" label="邮箱" align="center" width="220" />
-        <el-table-column prop="is_org_manager" label="机构管理员" align="center">
+        <el-table-column prop="real_name" label="真实姓名" align="center" header-align="center" />
+        <el-table-column prop="user_name" label="用户名" align="center" header-align="center" />
+        <el-table-column prop="email" label="邮箱" align="center" width="220" header-align="center" />
+        <el-table-column prop="is_org_manager" label="机构管理员" align="center" header-align="center">
           <template slot-scope="{ row }">
             <i v-if="row.is_org_manager === 'true'" class="el-icon-check"></i>
           </template>
         </el-table-column>
-        <el-table-column prop="register_time" label="注册时间" align="center" width="170" />
-        <el-table-column prop="is_stop" label="已停用" align="center">
+        <el-table-column prop="register_time" label="注册时间" align="center" width="170" header-align="center" />
+        <el-table-column prop="is_stop" label="已停用" align="center" header-align="center">
           <template slot-scope="{ row }">
             <i v-if="row.is_stop === 'true'" class="el-icon-check"></i>
           </template>
         </el-table-column>
-        <el-table-column prop="stop_time" label="停用时间" align="center" width="170" />
+        <el-table-column prop="stop_time" label="停用时间" align="center" width="170" header-align="center" />
 
-        <el-table-column prop="operation" label="操作" fixed="right" width="200" align="center">
+        <el-table-column prop="operation" label="操作" fixed="right" width="200" align="center" header-align="center">
           <template slot-scope="{ row }">
             <span class="link" @click="setOrgUserStop(row)">{{ row.is_stop === 'true' ? '启用' : '停用' }}</span>
             <span class="link" @click="setPower(row)">设置权限</span>