Browse Source

教材链接改进

dusenyao 4 months ago
parent
commit
07444528b7

+ 1 - 0
src/components/common/CommonTable.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="common-table">
+    <slot name="tab"></slot>
     <div class="common-table-list" :style="{ 'min-height': `${minHeight}px` }">
       <div v-if="name.length > 0" class="common-table-list-title">
         <div>{{ name }}</div>

+ 1 - 1
src/layouts/components/LayoutHeader.vue

@@ -102,7 +102,7 @@ export default {
         },
         {
           path: '/oboc',
-          name: '一书一码',
+          name: '教材链接',
           isShow: is_inner
         },
         {

+ 1 - 1
src/router/index.js

@@ -261,7 +261,7 @@ const routes = [
   {
     path: '/oboc',
     component: Layout,
-    meta: { title: '一书一码', path: '/oboc' },
+    meta: { title: '教材链接', path: '/oboc' },
     redirect: '/oboc/index',
     children: [
       {

+ 24 - 1
src/utils/list.js

@@ -9,23 +9,46 @@ export function useList(capacity = 10) {
   let cur_page = ref(1); // 当前页
   let total_count = ref(0); // 总记录数
   let list = ref([]); // 列表
+  let order_column_list = ref([]); // 排序字段列表
 
+  /**
+   * 修改页码
+   * @param {number} newPage
+   * @param {Function} fn
+   */
   function changePage(newPage, fn) {
     cur_page.value = newPage;
     fn();
   }
 
+  /**
+   * 修改每页容量
+   * @param {number} pageSize
+   * @param {Function} fn
+   */
   function changePageSize(pageSize, fn) {
     page_capacity.value = pageSize;
     fn();
   }
 
+  /**
+   * 表格排序
+   * @param {Object} data
+   * @param {Function} fn
+   */
+  function sortChange({ order, prop }, fn) {
+    order_column_list.value = prop ? [`${prop}${order === 'descending' ? ':desc' : ''}`] : [];
+    fn();
+  }
+
   return {
     page_capacity,
     cur_page,
     total_count,
     list,
+    order_column_list,
     changePage,
-    changePageSize
+    changePageSize,
+    sortChange
   };
 }

+ 80 - 23
src/views/oboc/CreateLink.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <el-dialog width="480px" title="创建链接" :visible="visible" :before-close="close" :close-on-click-modal="false">
+    <el-dialog width="600px" title="创建链接" :visible="visible" :before-close="close" :close-on-click-modal="false">
       <el-form ref="form" :model="createForm" label-width="60px">
         <el-form-item label="教材" prop="book_name">
           <div class="select-book">
@@ -15,15 +15,19 @@
           </el-select>
         </el-form-item>
         <el-form-item label="有效期">
-          <el-select v-model="createForm.effective_day_count" class="link-select">
-            <el-option label="永久" :value="-1" />
-            <el-option
-              v-for="item in Array.from({ length: 90 }, (_, i) => i + 1)"
-              :key="item"
-              :label="`${item}天`"
-              :value="item"
-            />
-          </el-select>
+          <el-date-picker
+            v-model="createForm.effective_date"
+            type="daterange"
+            :disabled="createForm.is_forever"
+            value-format="yyyy-MM-dd"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+          />
+          <el-checkbox v-model="createForm.is_forever">永久</el-checkbox>
+        </el-form-item>
+        <el-form-item label="备注">
+          <el-input v-model="createForm.memo" placeholder="请输入备注" class="link-select" />
         </el-form-item>
       </el-form>
 
@@ -44,22 +48,44 @@ export default {
 </script>
 
 <script setup>
-import { ref, defineEmits } from 'vue';
+import { ref, watch, defineEmits } from 'vue';
 
 import SelectBook from './SelectBook.vue';
 
-defineProps({
+import { Message } from 'element-ui';
+
+const props = defineProps({
   visible: Boolean
 });
 
 const emits = defineEmits(['close', 'confirm']);
 
-let createForm = ref({
-  book_id: '',
-  book_name: '',
-  type: 1,
-  effective_day_count: 3
-});
+// 将 new Date() 转换为 '2024-01-01'
+function dateToString(date) {
+  return date.toISOString().split('T')[0];
+}
+
+function init() {
+  return {
+    book_id: '',
+    book_name: '',
+    type: 1,
+    is_forever: false,
+    effective_date: [dateToString(new Date()), dateToString(new Date(new Date().getTime() + 30 * 24 * 60 * 60 * 1000))],
+    memo: ''
+  };
+}
+
+let createForm = ref(init());
+
+watch(
+  () => props.visible,
+  val => {
+    if (val) {
+      createForm.value = init();
+    }
+  }
+);
 
 let visibleSelectBook = ref(false);
 function selectBook() {
@@ -70,9 +96,23 @@ function closeSelectBook() {
   visibleSelectBook.value = false;
 }
 
-function confirmSelectBook(id, name) {
-  createForm.value.book_id = id;
-  createForm.value.book_name = name;
+/**
+ * 确定选择教材
+ * @param {Array} multipleSelection
+ */
+function confirmSelectBook(multipleSelection) {
+  let book_id = createForm.value.book_id;
+  let book_name = createForm.value.book_name;
+  multipleSelection.forEach(({ id, name }) => {
+    // 判断是否已选择
+    if (book_id.includes(id)) {
+      return;
+    }
+    book_id += `${id};`;
+    book_name += `${name};`;
+  });
+  createForm.value.book_id = book_id;
+  createForm.value.book_name = book_name;
   visibleSelectBook.value = false;
 }
 
@@ -81,7 +121,19 @@ function close() {
 }
 
 function confirm() {
-  emits('confirm', createForm.value.book_id, createForm.value.type, createForm.value.effective_day_count);
+  if (createForm.value.book_id === '') {
+    Message.error('请选择教材');
+    return;
+  }
+  if (
+    createForm.value.effective_date === null ||
+    (createForm.value.effective_date.length === 0 && !createForm.value.is_forever)
+  ) {
+    Message.error('请选择有效期');
+    return;
+  }
+
+  emits('confirm', createForm.value);
 }
 </script>
 
@@ -97,7 +149,12 @@ function confirm() {
 
 .el-form {
   .link-select {
-    width: calc(100% - 43px);
+    width: 100%;
+  }
+
+  .el-date-editor {
+    width: calc(100% - 80px);
+    margin-right: 12px;
   }
 }
 </style>

+ 55 - 25
src/views/oboc/SelectBook.vue

@@ -1,8 +1,21 @@
 <template>
   <el-dialog width="1000px" title="选择教材" :visible="visible" :before-close="close" :close-on-click-modal="false">
     <div class="book-search">
-      <el-input v-model="name" placeholder="教材名称" @keyup.enter.native="pageQueryBookList" />
-      <el-button type="primary" @click="pageQueryBookList">搜索</el-button>
+      <span class="search-name">教材名称</span>
+      <el-input v-model="searchForm.name" placeholder="请输入教材名称" @keyup.enter.native="pageQueryBookList" />
+      <span class="search-name">作者</span>
+      <el-input v-model="searchForm.author" placeholder="请输入作者" @keyup.enter.native="pageQueryBookList" />
+      <span class="search-name">出版编号</span>
+      <el-input
+        v-model="searchForm.publish_number"
+        placeholder="请输入出版编号"
+        @keyup.enter.native="pageQueryBookList"
+      />
+      <span class="search-name">简介</span>
+      <el-input v-model="searchForm.description" placeholder="请输入简介" @keyup.enter.native="pageQueryBookList" />
+      <div class="book-button">
+        <el-button type="primary" @click="pageQueryBookList">搜索</el-button>
+      </div>
     </div>
     <CommonTable
       :page-size="page_capacity"
@@ -14,13 +27,16 @@
       @current-change="changePage($event, pageQueryBookList)"
       @size-change="changePageSize($event, pageQueryBookList)"
     >
-      <el-table :data="list" max-height="400px" highlight-current-row @current-change="handleCurrentChange">
+      <el-table :data="list" max-height="400px" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" header-align="center" align="center" />
         <el-table-column label="序号" width="60" align="center">
           <template slot-scope="{ $index }">
-            <span>{{ $index + 1 }}</span>
+            <span>{{ curPageIndex + $index }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="name" label="教材名称" width="360" />
+        <el-table-column prop="name" label="教材名称" width="200" />
+        <el-table-column prop="author" label="作者" width="150" />
+        <el-table-column prop="publish_number" label="出版编号" width="210" />
         <el-table-column prop="description" label="简介" />
       </el-table>
     </CommonTable>
@@ -54,17 +70,24 @@ const emits = defineEmits(['confirm', 'close']);
 
 const { page_capacity, cur_page, total_count, list, changePage, changePageSize } = useList();
 
-let name = ref('');
+let searchForm = ref({
+  name: '',
+  author: '',
+  publish_number: '',
+  description: ''
+});
+let curPageIndex = ref(1);
 function pageQueryBookList() {
   PageQueryBookList({
-    name: name.value,
+    ...searchForm.value,
     org_id: '',
     publish_status: 1,
     creator_id: '',
     sys_version_type: -1,
     page_capacity: page_capacity.value,
     cur_page: cur_page.value
-  }).then(({ book_list, total_count: total, cur_page: page }) => {
+  }).then(({ book_list, total_count: total, cur_page: page, cur_page_begin_index }) => {
+    curPageIndex.value = cur_page_begin_index || 0;
     list.value = book_list;
     total_count.value = total;
     cur_page.value = page;
@@ -72,29 +95,26 @@ function pageQueryBookList() {
 }
 pageQueryBookList();
 
-let book = ref({
-  book_id: '',
-  book_name: ''
-});
-function handleCurrentChange(curRow) {
-  if (curRow === null) {
-    book.value = { book_id: '', book_name: '' };
-    return;
-  }
-
-  const { id, name } = curRow;
-  book.value = { book_id: id, book_name: name };
+let multipleSelection = ref([]);
+/**
+ * 多选教材
+ * @param {Array} val
+ */
+function handleSelectionChange(val) {
+  multipleSelection.value = val.map(({ id, name }) => {
+    return { id, name };
+  });
 }
 
 function confirm() {
-  if (!book.value.book_id) {
+  if (multipleSelection.value.length === 0) {
     MessageBox.alert('请先选择教材', '提示', {
       confirmButtonText: '确定',
       type: 'warning'
     });
     return;
   }
-  emits('confirm', book.value.book_id, book.value.book_name);
+  emits('confirm', multipleSelection.value);
 }
 
 function close() {
@@ -107,9 +127,19 @@ function close() {
   display: flex;
   align-items: center;
 
-  .el-input {
-    width: 200px;
-    margin-right: 20px;
+  .search-name {
+    font-size: 14px;
+
+    & + .el-input {
+      width: 140px;
+      margin: 0 12px;
+    }
+  }
+
+  .book-button {
+    display: flex;
+    flex: 1;
+    justify-content: flex-end;
   }
 }
 </style>

+ 151 - 37
src/views/oboc/index.vue

@@ -1,7 +1,19 @@
 <template>
   <div class="oboc">
-    <div class="oboc-operation">
-      <el-button type="primary" @click="createLink">创建链接</el-button>
+    <div class="oboc-top">
+      <div class="oboc-top-search">
+        <span class="search-name">教材名称</span>
+        <el-input v-model="book_name" placeholder="请输入教材名称" />
+        <span class="search-name">识别码</span>
+        <el-input v-model="identity_code" placeholder="请输入识别码" />
+        <span class="search-name">备注</span>
+        <el-input v-model="memo" placeholder="请输入备注">
+          <el-button slot="append" icon="el-icon-search" @click="pageQueryOBOCList" />
+        </el-input>
+      </div>
+      <div class="oboc-top-operation">
+        <el-button type="primary" @click="createLink">创建链接</el-button>
+      </div>
     </div>
 
     <CommonTable
@@ -14,33 +26,37 @@
       @current-change="changePage($event, pageQueryOBOCList)"
       @size-change="changePageSize($event, pageQueryOBOCList)"
     >
-      <el-table :data="list">
-        <el-table-column label="类型" width="90" align="center">
-          <template slot-scope="{ row }">
-            <span>{{ row.type === 0 ? '试用' : '一书一码' }}</span>
-          </template>
-        </el-table-column>
+      <div slot="tab" class="table-tab">
+        <span
+          v-for="{ label, value } in typeList"
+          :key="value"
+          :class="{ active: value === type }"
+          @click="type = value"
+          >{{ label }}</span
+        >
+      </div>
+
+      <el-table :data="list" @sort-change="sortChange($event, pageQueryOBOCList)">
         <el-table-column prop="book_name" label="教材" width="240" />
         <el-table-column prop="identity_code" label="识别码" width="120" />
-        <el-table-column prop="creator_name" label="创建人" width="150" />
-        <el-table-column prop="create_time" label="创建时间" width="160" />
+        <el-table-column prop="create_time" label="创建时间" width="160" sortable="custom" />
         <el-table-column label="有效天数" width="100">
           <template slot-scope="{ row }">
             <span>{{ row.effective_day_count === -1 ? '永久' : `${row.effective_day_count}天` }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="到期截止日期" width="140">
+        <el-table-column prop="effective_end_date" label="有效期" width="260" sortable="custom">
           <template slot-scope="{ row }">
-            <span :style="{ color: row.is_disabled === 'true' ? '#CA0000' : '' }">
-              {{ row.is_disabled === 'true' ? '已废弃' : formateDate(row.effective_end_date) }}
-            </span>
+            <span :style="{ color: row.is_disabled === 'true' ? '#CA0000' : '' }"> {{ computedPeriod(row) }} </span>
           </template>
         </el-table-column>
+        <el-table-column prop="memo" label="备注" />
+
         <el-table-column label="操作" fixed="right" width="200">
           <template slot-scope="{ row }">
             <span class="set-oboc operable" @click="copyLink(row.url)">复制链接</span>
-            <span class="set-oboc operable" @click="deleteOBOC(row.id)">删除</span>
             <span v-if="row.is_disabled !== 'true'" class="set-oboc operable" @click="disableOBOC(row.id)">废弃</span>
+            <span v-else class="set-oboc operable" @click="deleteOBOC(row.id)">删除</span>
           </template>
         </el-table-column>
       </el-table>
@@ -57,7 +73,7 @@ export default {
 </script>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
 
 import { PageQueryOBOCList } from '@/api/list';
 import { CreateOBOC, DeleteOBOC, DisableOBOC } from '@/api/oboc';
@@ -67,14 +83,39 @@ import { Message, MessageBox } from 'element-ui';
 import CommonTable from '@/components/common/CommonTable.vue';
 import CreateLink from './CreateLink.vue';
 
-const { page_capacity, cur_page, total_count, list, changePage, changePageSize } = useList();
+const { page_capacity, cur_page, total_count, list, order_column_list, changePage, changePageSize, sortChange } =
+  useList();
+
+let book_name = ref('');
+let identity_code = ref('');
+let memo = ref('');
+let typeList = [
+  {
+    label: '试用',
+    value: 0
+  },
+  {
+    label: '一书一码',
+    value: 1
+  }
+];
+let type = ref(0);
+watch(type, () => {
+  pageQueryOBOCList();
+});
 
+/**
+ * 分页查询教材链接列表
+ */
 function pageQueryOBOCList() {
   PageQueryOBOCList({
     page_capacity: page_capacity.value,
     cur_page: cur_page.value,
-    type: -1,
-    book_name: ''
+    type: type.value,
+    book_name: book_name.value,
+    identity_code: identity_code.value,
+    memo: memo.value,
+    order_column_list: order_column_list.value
   }).then(({ oboc_list, total_count: total, cur_page: page }) => {
     list.value = oboc_list;
     total_count.value = total;
@@ -93,16 +134,26 @@ function closeCreateLink() {
 }
 
 /**
- * 创建一书一码
- * @param {String} book_id
- * @param {Number} type
- * @param {Number} effective_day_count
+ * 创建教材链接
+ * @param {Object} param0
+ * @param {String} param0.book_id
+ * @param {Number} param0.type
+ * @param {Boolean} param0.is_forever
+ * @param {String} param0.effective_date
+ * @param {String} param0.memo
  */
-function createOBOC(book_id, type, effective_day_count) {
-  CreateOBOC({ book_id, type, effective_day_count }).then(() => {
+function createOBOC({ book_id, type, is_forever, effective_date, memo }) {
+  CreateOBOC({
+    book_id_list: book_id.split(';').filter(id => id.length > 0),
+    type,
+    is_forever,
+    effective_begin_date: effective_date[0],
+    effective_end_date: effective_date[1],
+    memo
+  }).then(() => {
     visible.value = false;
     pageQueryOBOCList();
-    Message.success(`创建一书一码成功`);
+    Message.success(`创建教材链接成功`);
   });
 }
 
@@ -113,41 +164,59 @@ function copyLink(url) {
 }
 
 function deleteOBOC(id) {
-  MessageBox.confirm('是否删除该一书一码?', '提示', {
+  MessageBox.confirm('是否删除该教材链接?', '提示', {
     confirmButtonText: '确定',
     cancelButtonText: '取消',
     type: 'warning'
   }).then(() => {
     DeleteOBOC({ id }).then(() => {
-      Message.success(`删除一书一码成功`);
+      Message.success(`删除教材链接成功`);
       pageQueryOBOCList();
     });
   });
 }
 
 function disableOBOC(id) {
-  MessageBox.confirm('是否废弃该一书一码?', '提示', {
+  MessageBox.confirm('是否废弃该教材链接?', '提示', {
     confirmButtonText: '确定',
     cancelButtonText: '取消',
     type: 'warning'
   }).then(() => {
     DisableOBOC({ id }).then(() => {
       pageQueryOBOCList();
-      Message.success(`废弃一书一码成功`);
+      Message.success(`废弃教材链接成功`);
     });
   });
 }
 
 /**
- * 将 20241221 转换为 2024年12月21日 格式
+ * 计算有效期
+ * @param {Object} row
+ */
+function computedPeriod({
+  is_disabled,
+  effective_day_count,
+  effective_begin_date = '0000-01-01',
+  effective_end_date = '9999-12-31'
+}) {
+  if (is_disabled === 'true') {
+    return '已废弃';
+  }
+
+  if (effective_day_count === -1) {
+    return '永久';
+  }
+
+  return `${formateDate(effective_begin_date.length <= 0 ? '0000-01-01' : effective_begin_date)} ~ ${formateDate(
+    effective_end_date
+  )}`;
+}
+/**
+ * 将 2024-12-21 转换为 2024年12月21日 格式
  * @param {String} originalDate
  */
 function formateDate(originalDate) {
-  const dateStr = originalDate.toString();
-  let year = dateStr.substring(0, 4);
-  let month = dateStr.substring(4, 6);
-  let day = dateStr.substring(6, 8);
-
+  const [year, month, day] = originalDate.toString().split('-');
   return `${year}年${month}月${day}日`;
 }
 </script>
@@ -161,9 +230,54 @@ function formateDate(originalDate) {
 
   padding: 24px 0 46px;
 
-  &-operation {
+  &-top {
     display: flex;
     align-items: center;
+
+    &-search {
+      flex: 1;
+
+      .search-name {
+        width: 80px;
+
+        & + .el-input {
+          width: 200px;
+          margin: 0 18px;
+        }
+      }
+    }
+
+    &-operation {
+      margin-left: 12px;
+    }
+  }
+
+  .table-tab {
+    position: relative;
+    top: 20px;
+    display: inline-flex;
+    background-color: #fff;
+    border: 1px solid #ebeef5;
+    border-radius: 10px;
+
+    span {
+      padding: 12px;
+      cursor: pointer;
+
+      &.active {
+        color: #fff;
+        background-color: #f90;
+        border: 1px solid #f90;
+
+        &:first-child {
+          border-radius: 10px 0 0 10px;
+        }
+
+        &:last-child {
+          border-radius: 0 10px 10px 0;
+        }
+      }
+    }
   }
 
   .el-table {