文档管理页面布局优化与文档内容表设计

文档管理布局优化

这里主要涉及到antd的Grid栅格

<a-row>
  <a-col :span="12">col-12</a-col>
  <a-col :span="12">col-12</a-col>
</a-row>

在这里插入图片描述
主要对admin-doc.vue进行修改,如下,

<template>
  <a-layout>
    <a-layout-content :style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }">
      <a-row :gutter="24">
        <a-col :span="8">
          <p>
            <a-button type="primary" @click="handleAdd">新增</a-button>
          </p>
          <a-table :columns="columns" :data-source="docs" :row-key="record=>record.id" size="small"
                   :loading="loading" :pagination="false" @change="handleChange"
                   v-if="docs.length" default-expand-all-rows
          >
            <template #customeTitle="{text,record}">
              {{record.sort}} {{text}}
            </template>
            <template #action="{text,record}">
              <a-space>
                <a-button type="primary" @click="edit(record)" size="small">编辑</a-button>
                <a-popconfirm
                    title="删除后不可恢复,确认删除?"
                    ok-text="确定"
                    cancel-text="取消"
                    @confirm="confirm(record)"
                >
                  <a-button type="danger" size="small">删除</a-button>
                </a-popconfirm>
              </a-space>
            </template>
          </a-table>
        </a-col>
        <a-col :span="16">
          <p>
            <a-button type="primary" @click="handleSave">保存</a-button>
          </p>
          <a-form :model="doc"  layout="vertical">
            <a-form-item>
              <a-input v-model:value="doc.name" placeholder="名称" />
            </a-form-item>
            <a-form-item>
              <a-tree-select
                  v-model:value="doc.parent"
                  :replace-fields="{children:'children', title:'name', key:'id', value: 'id' }"
                  style="width: 100%"
                  :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
                  :tree-data="treeData"
                  tree-default-expand-all
                  placeholder="请选择父文档"
              >
              </a-tree-select>
            </a-form-item>
            <a-form-item>
              <a-input v-model:value="doc.sort" placeholder="排序"/>
            </a-form-item>
            <a-form-item>
              <div id="content">
              </div>
            </a-form-item>
          </a-form>
        </a-col>
      </a-row>
    </a-layout-content>
  </a-layout>
</template>


<script lang="ts">
import {createVNode, defineComponent, onMounted, ref} from 'vue';
import axios from "axios";
import {arrayToTree} from "@/tools/tool";
import {useRoute} from "vue-router";
import {Modal} from "ant-design-vue";
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import E from "wangeditor";

interface docItem {
  id:number,
  name:string,
  parent?:number,
  sort?:number,
  children?:docItem[],
  ebookId?:number,
  viewCount?: number,
  voteCount?: number,
  disabled?:boolean,
}

const setDisable = (arr:docItem[],targetId:number|string) => {
  if(arr.length === 0) return ;
  for(let i=0;i<arr.length;i++){
    let node = arr[i];
    if(Number(node.id)=== Number(targetId)){
      node.disabled = true;
      let children = node.children;
      if(children && children.length){
        for(let j=0;j<children.length;j++){
          setDisable(children,children[j].id);
        }
      }
    }else{
      let children = node.children;
      if(children && children.length){
        setDisable(children,targetId);
      }
    }
  }
}

let deleteIds:any = [];
let deleteDocs:any = [];
const getDeleteIds = (arr:docItem[],targetId:number|string) => {
  if(arr.length === 0) return ;
  for(let i=0;i<arr.length;i++){
    let node = arr[i];
    if(Number(node.id)=== Number(targetId)){
      deleteIds.push(node.id);
      deleteDocs.push(node.name);
      let children = node.children;
      if(children && children.length){
        for(let j=0;j<children.length;j++){
          getDeleteIds(children,children[j].id);
        }
      }
    }else{
      let children = node.children;
      if(children && children.length){
        getDeleteIds(children,targetId);
      }
    }
  }
}
export default defineComponent({
  name:"AdminDoc",
  setup: function () {
    const columns = [
      {
        dataIndex: 'name',
        key: 'name',
        title: "名称",
        slots: {customRender: 'customeTitle'}
      },
      {
        title: 'Action',
        key: 'action',
        slots: {customRender: 'action'},
      },
    ];
    const loading = ref(false);
    const docs = ref();
    docs.value = [];
    const doc = ref();
    doc.value = {
      name:"",
      parent:"",
      sort:""
    }
    const treeData = ref();
    const route = useRoute();
    let editor:any= null;
    const handleQuery = () => {
      loading.value = true;
      axios.get("/doc/all").then(response => {
        loading.value = false;
        const data = response.data;
        if (data.success) {
          let result = arrayToTree(data.content, 0);
          docs.value = result;
        }
      })
    }
    const handleSave = () => {
      axios.post("/doc/save", doc.value).then(response => {
        const data = response.data;
        if (data.success) {
          handleQuery();
        } else {
          console.log("save failed");
        }
      })
    }
    const handleDelete = (record: any) => {
      axios.delete("/doc/delete/" + deleteIds.join(",")).then(response => {
        const data = response.data;
        if (data.success) {
          handleQuery();
        } else {
          console.log("delete failed");
        }
      })
    }
    const showConfirm = (record:any) => {
      Modal.confirm({
        title: '重要提醒',
        icon: createVNode(ExclamationCircleOutlined),
        content: createVNode('div', { style: 'color:red;' }, '将删除【'+deleteDocs.join(",")+'】,删除后不可恢复,确认删除?'),
        onOk() {
          console.log(deleteDocs.join(","));
          handleDelete(record);
        },
        onCancel() {
          console.log('Cancel');
        }
      });
    };
    const handleChange = (p: any) => {
      handleQuery();
    }
    const edit = (record: any) => {
      doc.value = JSON.parse(JSON.stringify(record));
      treeData.value = JSON.parse(JSON.stringify(docs.value));
      treeData.value.unshift({id: 0, name: "无"});
      setDisable(treeData.value, record.id);
    }
    const confirm = (record: any) => {
      deleteIds = [];
      deleteDocs = []
      getDeleteIds(docs.value,record.id);
      console.log(deleteDocs);
      showConfirm(record);
    }
    const handleSearch = () => {
      handleQuery();
    }
    const handleAdd = () => {
      doc.value = {
        id: "",
        name: "",
        sort: "",
        ebookId:route.params.ebookId
      }

      treeData.value = JSON.parse(JSON.stringify(docs.value));
      treeData.value.unshift({id: 0, name: "无"});
    }
    onMounted(() => {
      handleQuery();
      editor = new E("#content");
      editor.config.zIndex = 0;
      editor.create();
    })

    return {
      docs,
      columns,
      loading,
      doc,
      treeData,
      handleQuery,
      handleChange,
      edit,
      showConfirm,
      handleSave,
      confirm,
      handleSearch,
      handleAdd
    };
  }

});
</script>


<style scoped>

</style>

在这里插入图片描述

文档内容表设计

文档内容表设计如下,

drop table if exists `content`;
create table `content` (
    `id` bigint not null comment '文档id',
    `txt` mediumtext not null comment '内容',
    primary key (`id`)
)engine=innodb default charset=utf8mb4 comment='文档内容';

content表包含两个字段:文档唯一标识id和文档内容txt

现在,回过头来看下之前的文档表设计,doc表也包含文档唯一标识id

drop table if exists `doc`;
create table `doc` (
    `id` bigint not null comment 'id',
    `ebook_id` bigint not null default 0 comment '电子书id',
    `parent` bigint not null default 0 comment '父id',
    `name` varchar(50) not null comment '名称',
    `sort` int comment '顺序',
    `view_count` int default 0 comment '阅读数',
    `vote_count` int default 0 comment '点赞数',
    primary key (`id`)
)engine=innodb default charset=utf8mb4 comment='文档';

文档内容是文档的一部分,也就是说,文档内容字段txt本可以放到doc表中。
txt的数据类型是mediumtext,是大字段,如果将它放到doc表里,可能会出现这么一种情况:假如每次查询doc表,将返回10条数据,每条数据里都包含txt这个大字段,由此就会造成较大的数据库压力。所以为了避免这种情况,就采用了分表。

分表有纵向分表和横向分表。

  • 纵向分表 ,将本来可以在同一个表的内容,人为划分为多个表,本例就是纵向分表。
  • 横向分表,就是把大的表结构,横向切割为相同结构的不同表。
Mybatis生成Model实体类、Mapper接口和Mapper XML文件

generatorConfig.xml内容如下,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

    <context id="Mysql" targetRuntime="MyBatis3">
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/test-database"
                        userId="test-user"
                        password="test@123">
        </jdbcConnection>

        <javaModelGenerator targetPackage="com.jepcc.test.model" targetProject="src\main\java">
        </javaModelGenerator>

        <sqlMapGenerator targetPackage="mapper"  targetProject="src\main\resources">
        </sqlMapGenerator>

        <javaClientGenerator type="XMLMAPPER" targetPackage="com.jepcc.test.mapper"  targetProject="src\main\java">
        </javaClientGenerator>

<!--        <table tableName="ebook" domainObjectName="Ebook" ></table>-->
<!--        <table tableName="category" domainObjectName="Category" ></table>-->
<!--        <table tableName="doc" domainObjectName="Doc" ></table>-->
        <table tableName="content" domainObjectName="Content" ></table>
        
    </context>
</generatorConfiguration>

执行命令:mybatis-generator:generate -e,生成如下文件,

  • Model实体类:com.jepcc.test.model.Contentcom.jepcc.test.model.ContentExample
  • Mapper接口类:com.jepcc.test.mapper.ContentMapper
  • Mapper XML文件:src/main/resources/mapper/ContentMapper.xml
参考文章

纵向分表和横向分表
纵向拆分和横向分区
antd树表设置default-expand-all-rows无效时的解决方案

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值