电子书列表

电子书列表

电子书解析数据展示

EbookUpload组件里

this.emit(‘onSuccess’,response.data)

book/components/detail.vue

    handleSuccess(data){
    
    
      console.log(data);
    },

在这里插入图片描述

拿到data数据以后

在这里插入图片描述

之后写个setData方法

 setData(data) {
    
    
      const {
    
    
        title,
        author,
        publisher,
        language,
        rootFile,
        cover,
        originalName,
        url,
        contents,
        contentsTree,
        fileName,
        coverPath,
        filePath,
        unzipPath,
      } = data;
      this.postForm = {
    
    
        title,
        author,
        publisher,
        language,
        rootFile,
        cover,
        url,
        originalName,
        contents,
        fileName,
        coverPath,
        filePath,
        unzipPath,
      };
    },

重新执行

在这里插入图片描述

这里的filename大小写写错了,不过文件名称还是换成originalName更好点

在这里插入图片描述

树状目录展开

el-tree解决问题

传入data属性,data属性当中是个数组,数组中包含若干对象,每个对象对应一级的tree 里面包含label ,children

https://element.eleme.io/#/zh-CN/component/tree#tree-shu-xing-kong-jian

在这里插入图片描述

电子书目录不太一样,我们为了方便解析,只用了一维的数组,而范例是个嵌套的数组

我们去服务端来进行改造 改造成嵌套的 当然在前端做处理也可以

没有识别出pid时说明是一级目录

在这里插入图片描述

              const chapterTree=[]
              chapters.forEach(c=>{
    
    
                c.children=[]
                //没有识别出pid时说明是一级目录
                if(c.pid===''){
    
    
                  chapterTree.push(c)
                }
              })
      		  const chapterTree = []
              chapters.forEach(c => {
    
    
                // 我们前面已经定义过label属性并赋值了 
                c.children = []
                //没有识别出pid时说明是一级目录
                if (c.pid === '') {
    
    
                  chapterTree.push(c)
                } else {
    
    
                  // pid不为空,说明有parent 先要找到parent
                  const parent = chapters.find(_ =>
                    // 如果一样,就找到了parent
                    _.navId === c.pid
                  )
                  parent.children.push(c)
                }
              })
              console.log(chapterTree);

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

点击以后如何看章节内容

https://element.eleme.cn/#/zh-CN/component/tree#events

在这里插入图片描述

在这里插入图片描述

看一下data里都有啥

   onContentClick(data){
      console.log(data)
    },

在这里插入图片描述

最重要的就是text参数

    onContentClick(data){
    
    
      // console.log(data)
      if(data.text){
    
    
        window.open(data.text)
      }
    },

随便点进一个章节

在这里插入图片描述

但是还是有些书有问题的 比如说这本,点进去和目录不一样 15-3有优化 这里就不做优化了

在这里插入图片描述

电子书表单验证功能开发

点击叉号清空

补充defaultForm

在这里插入图片描述

在这里插入图片描述

校验功能

看element-ui源码可以看到,会返回两个参数

在这里插入图片描述

在这里插入图片描述

    submitForm(){
    
    
      this.$refs.postForm.validate((valid,fileds)=>{
    
    
         console.log(valid,fileds);
          // 通过验证
        if(valid){
    
    
         
        }else{
    
    

        }
      })
    },

在这里插入图片描述

写个rules对象,它的key是el-form-item里面写的prop属性

在这里插入图片描述

可以直接在title里写required 这里用的自定义校验规则(传入function)

在这里插入图片描述

  const validateRequire=(rule,value,callback)=>{
    
    
      if(value===''||value.length==0){
    
    
        callback(new Error('title不能为空'))
      }else{
    
    
        callback()
      }
    }

在这里插入图片描述

可以打印一下rule

在这里插入图片描述

改造

callback(new Error(rule.field+'必须填写'))

这里有个技巧,可以做个字段映射

写个对象

在这里插入图片描述

callback(new Error(fields[rule.field]+‘必须填写’))

在这里插入图片描述

现在想拿到这句话 然后展示在页面上

在这里插入图片描述

fields[Object.keys(fields)[0]][0].message

   submitForm(){
    
    
      this.$refs.postForm.validate((valid,fields)=>{
    
    
        console.log(valid,fields);
        if(valid){
    
    
         
        }else{
    
    
          // 这是一个通用的异常处理方法
          console.log(fields[Object.keys(fields)[0]][0].message)
          const message=fields[Object.keys(fields)[0]][0].message
          this.$message({
    
    
            message,type:'error'
          })
        }
      })
    },

在这里插入图片描述

给作者 出版社 语言 也加个prop author publisher language

增加映射

在这里插入图片描述

然后在rules添加规则

    rules: {
    
    
        title: [{
    
     validator: validateRequire }],
        author: [{
    
     validator: validateRequire }],
        language: [{
    
     validator: validateRequire }],
        publisher: [{
    
     validator: validateRequire }],
      },

新增电子书前端逻辑

浅拷贝两种方法:扩展运算符/object.assign

在这里插入图片描述

请求接口

src/api/book.js

在这里插入图片描述

Detail.vue引入

在这里插入图片描述

在这里插入图片描述

接口开发

utils/index.js 用到了这里的decode

在这里插入图片描述

book.js

router.post('/create', (req, res, next) => {
    
    
  const decoded=decode(req)
  // 解出jwt
  console.log(decoded);
})

在这里插入图片描述

写一个book对象,这个book对象生成的逻辑可以从model/Book.js里写

在这里插入图片描述

在这里插入图片描述

数据库数据

在这里插入图片描述

打印一下data 一定要注意代码规范啊,该驼峰就驼峰 有几个没用驼峰后悔了

在这里插入图片描述

  createBookFromData(data) {
    
    
    // data里的字段需要与数据库字段做映射
    // console.log(69,data);
    this.fileName=data.filename
    this.cover=data.coverPath
    this.title=data.title
    this.author=data.author
    this.publisher=data.publisher
    this.bookId=data.filename
    this.language=data.language
    this.rootFile=data.rootFile
    this.originalName=data.originalname
    this.path=data.filePath
    this.filePath=data.filePath
    this.unzipPath=data.unzipPath
    this.coverPath=data.coverPath
    this.createUser=data.username
    this.createDt=new Date().getTime()
    this.updateDt=new Date().getTime()
    //为0 表示是默认图书,否则代表来自于互联网(1)
    this.updateType=data.updateType===0?data.updateType:1
    this.category=data.category||99
    this.categoryText=data.categoryText||'自定义'
  }

在这里插入图片描述

book对象就生成了 这样就可以通过对象生成sql语句了

新增电子书核心逻辑思路

新建service/book.js 有关数据库的操作

const Book=require('../model/Book')
function insertBook(book){
    
    
  return new Promise((resolve,reject)=>{
    
    
    try {
    
    
      // book一定要是Book对象的实例 可以避免某些参数不存在的情况
      // 如果是实例的话有个好处 能够保证那些参数都是完备的,
      // 不然的话随便传入一个book对象做insert的话可能会产生错误
      if(book instanceof Book) {
    
    
        
      }else{
    
    
        reject(new Error('添加的图书对象不合法'))
      }
    } catch (error) {
    
    
      reject(error)
    }
  })
}
module.exports={
    
    insertBook}

router/book.js

router.post('/create', (req, res, next) => {
    
    
  const decoded = decode(req)
  // 解出jwt
  // console.log(decoded);
  // 前端传入的信息
  console.log(req.body);
  if (decoded && decoded.username) {
    
    
    // book.username=decoded.username
    req.body.username = decoded.username
  }
  // const book = new Book(null, req.body)
  const book={
    
    }
  bookService.insertBook(book).then((res)=>{
    
    
    console.log(res);
  }).catch((err)=>{
    
    
    // console.log(err);
    // 错误与前端联动 
    next(boom.badImplementation(err))
  })  
  console.log(book)
})

在这里插入图片描述

在这里插入图片描述

这就是大致框架了

判断电子书是否存在,如果已经存在,就移除掉 需要把数据库里的信息移除掉,还要把文件移除掉

在这里插入图片描述

insert以后还要把目录插入到目录表里

在这里插入图片描述

在这里插入图片描述

操作数据库会存在大量的异步操作,用到async await 变成同步方法 减少回调的次数,如果用promise的话会存在大量嵌套

逻辑就是这样,接下来写具体的方法

数据库操作

insert方法判断第一个参数传入的是否为一个对象

判断是否为对象的方法:utils/index.js

function isObject(o){
    
    
  return Object.prototype.toString.call(o)==='[object Object]'
}

用这个做判断非常精确

在这里插入图片描述

function insert(model,tableName){
    
    
  return new Promise((resolve,reject)=>{
    
    
    if(!isObject(model)){
    
    
      reject(new Error('插入数据库失败,插入数据非对象'))
    }
  })
}

来实验一下

在这里插入图片描述

在这里插入图片描述

回到insert方法

在这里插入图片描述

数据库操作有个技巧

 keys.push(`\`${
      
      key}\``)

为什么呢 比如说select from from book 其中第一个from并不是关键字而是个key,但是数据库会自动识别出他是个关键字就会报错,加了反引号之后就没有这个问题了

  // 拼sql语句
      if(keys.length>0&&values.length>0){
    
    
        let sql=`insert into \`${
      
      tableName}\`(`
        const keysString=keys.join(',')
        const valuesString=values.join(',')
        sql=`${
      
      sql}${
      
      keysString}) values (${
      
      valuesString})`
        console.log(sql)
      }

在这里插入图片描述

接着尝试执行sql语句能否成功

 // 拼sql语句
      if(keys.length>0&&values.length>0){
    
    
        let sql=`insert into \`${
      
      tableName}\`(`
        const keysString=keys.join(',')
        const valuesString=values.join(',')
        sql=`${
      
      sql}${
      
      keysString}) values (${
      
      valuesString})`
        console.log(sql)
        const conn=connect()
        try {
    
    
          conn.query(sql,(err,result)=>{
    
    
            if(err){
    
    
              reject(err)
            }else{
    
    
              resolve(result)
            }
          })
        } catch (error) {
    
    
          reject(error)
        }finally{
    
    
          conn.end()
        }
      }else{
    
    
        reject(new Error('对象中没有任何属性'))
      }

在这里插入图片描述

提示数据库没有path这个字段

这里推荐个做法

Book.js新增个方法toDb 对book对象进行过滤而不是整体都拿去使用

把path去掉

在这里插入图片描述

toDb() {
    
    
    return {
    
    
      fileName: this.fileName,
      cover: this.cover,
      title: this.title,
      author: this.author,
      publisher: this.publisher,
      bookId: this.bookId,
      language: this.language,
      rootFile: this.rootFile,
      originalName: this.originalName,
      filePath: this.filePath,
      unzipPath: this.unzipPath,
      coverPath: this.coverPath,
      createUser: this.createUser,
      createDt:this.createDt,
      updateDt: this.updateDt,
      updateType:this.updateType,
      category: this.category,
      categoryText: this.categoryText
    }
  }

这个book就要改成book.toDd() 把toDb返回的结果传给insertBook 这样会报错,因为insertBook里面会判断传入的对象是不是Book的实例

在这里插入图片描述

写在这里是合适的

在这里插入图片描述

这样的话点击添加就可以发现数据库多了条数据

在这里插入图片描述

前端交互优化

在这里插入图片描述

insertBook(book).then((res)=>{}) 这里不能写res因为success方法会自动传入这里的res 但是实际上应该传入上面红箭头所指的res

在这里插入图片描述

前端:

拿到后端的返回值,显示到前端页面

这次不用$message了

https://element.eleme.cn/#/zh-CN/component/notification#notification-tong-zhi

用一个跟他很像的 $notify

在这里插入图片描述

在这里插入图片描述

还有个细节,上传成功之后可以把列表数据都移除掉 remove方法里用过

this.postForm = Object.assign({
    
    }, defaultForm);

列表是移除了 但是还是有bug

在这里插入图片描述

这里写个setDefault方法,来解决这些问题

在这里插入图片描述

把这个置空书名就没了

    setDefault(){
    
    
      this.postForm = Object.assign({
    
    }, defaultForm);
      this.fileList=[]
    },

接下来要把校验结果消除

在这里插入图片描述

  setDefault(){
    
    
      // this.postForm = Object.assign({}, defaultForm);
      this.fileList=[]
      this.$refs.postForm.resetFields()
    },

在这里插入图片描述

这是因为没有传入prop,如果没有传入prop,他就不认为是一个表单的选项

在这里插入图片描述

目录还没有消除

    setDefault() {
    
    
      // this.postForm = Object.assign({}, defaultForm);
      this.contentsTree = []; // 消除目录
      this.fileList = []; // 标题
      this.$refs.postForm.resetFields();
    },

移除也是调用这个方法

添加目录到数据库功能

接下来写insertContents方法

需要取到book下的contents

但是前面把contents删掉了 所以传进来没有目录

前端:

在这里插入图片描述

后端接收:

在这里插入图片描述

可以编写一个getContents

在这里插入图片描述

确保能拿到contents之后就可以做插入数据库操作了

在这里插入图片描述

对比一下还是有很多冗余字段

在这里插入图片描述

可以建一个对象一次赋值,这里有个技巧—通过lodash实现

通过lodash可以使用它的方法来实现我们想要的功能

在这里插入图片描述

在这里插入图片描述

调用insert方法

await db.insert(_content,‘contents’)

在这里插入图片描述

在这里插入图片描述

电子书删除功能

下面两个逻辑都写完了 接下来写移除的逻辑

在这里插入图片描述

首先把exists函数写了 功能是判断电子书是否存在

在这里插入图片描述

为了方便测试 把前端的this.setDefault();注释掉

在这里插入图片描述

这种情况就需要把当前上传的电子书给移除

function removeBook(book) {
    
    
  if(book){
    
    
    // 删除相关文件(电子书文件,封面,解压后的文件)
    book.reset()
  }
}

进入Book.js

  // 判断路径是否存在静态方法
  static pathExists(path){
    
    
    if(path.startsWith(UPLOAD_PATH)){
    
    
      return fs.existsSync(path)
    }else{
    
    
      return fs.existsSync(Book.genPath(path))
    }
  }

来模拟一下

在这里插入图片描述

单个文件删除:unlinkSync

在这里插入图片描述

解压路径删除记得加上recursive:true fs.rmdirSync(Book.genPath(this.unzipPath),{recursive:true})

这样就只保留一个文件了

老师这里又加了个删除数据库的操作,不知道为啥要加这一步,应该没有写入数据库才对,所以我觉得不用加这个删除数据库的逻辑了

在这里插入图片描述

电子书查询api

实现编辑功能首先要拿到fileName,根据filename到数据库查询 查询目录和内容

查到之后返给前端 所以需要在路由中接收个参数

在这里插入图片描述

之后可以在Detail.vue里拿到动态路由参数

在这里插入图片描述

在这里插入图片描述

思路

在这里插入图片描述

现在要实现的就是getBook的api

src/api/book.js

注意get方法使用的是params,如果是post则使用data

在这里插入图片描述

在这里插入图片描述

服务端:

router.book.js

大致框架

在这里插入图片描述

getBook方法在service/book.js里写

在这里插入图片描述

完善getBook逻辑

需要到book表和contents表进行查询

在这里插入图片描述

在这里插入图片描述

进一步处理让数据展示出来

    resolve(book[0])

但是标题,封面,目录都没有

先解决封面的问题

在这里插入图片描述

来解决目录的显示问题

现在的contents是个数组的结构,需要将数组转换成一个contentsTree

其实前面我们已经做过这个逻辑了,

 const chapterTree = []
    chapters.forEach(c => {
    
    
      // 我们前面已经定义过label属性并赋值了 
      c.children = []
      //没有识别出pid时说明是一级目录
      if (c.pid === '') {
    
    
        chapterTree.push(c)
      } else {
    
    
        // pid不为空,说明有parent 先要找到parent
        const parent = chapters.find(_ =>
          // 如果一样,就找到了parent
          _.navId === c.pid
        )
        parent.children.push(c)

      }
    })

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这时候目录还是出不来 前端代码改一下

在这里插入图片描述

接下来还有个问题没解决 就是文件的信息

给fileList赋值就ok

在这里插入图片描述

编辑电子书

接下来写这里的逻辑

在这里插入图片描述

前端增加个接口

在这里插入图片描述

虽然还没开发出来接口 但是还是可以去network里面看一下 可以发现接口和参数都能正确发出去了

在这里插入图片描述

现在服务端增加接口

和之前create的接口很相似(写错了 应该是update)

在这里插入图片描述

现在去service/book.js里写updateBook的逻辑

在这里插入图片描述

前端

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这里的book.fileName应该要加引号的

在这里插入图片描述

数据库操作文件写一个update方法

在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiaozhazhazhazha/article/details/119611015
今日推荐