当前位置:网站首页>EBook list page

EBook list page

2022-06-12 05:55:00 Snow flies fast

EBook editing

The page display

  • Deleting the e-book will emit onRemove Method , When this method is executed, the data is assigned a default value
  • Click the tree menu to open a new one tabs
<template>
  <ebook-upload :file-list="fileList" :disabled="isEdit" @onSuccess="onUploadSucess" @onRemove="onUploadRemove" />
  
  <el-tree :data="contentsTree" @node-click="onContentClick" />
</template>

<script> const defaultForm = {
       title: '', author: '', publisher: '', language: '', rootFile: '', filePath: '', unzipPath: '', coverPath: '', cover: '', originalName: '' } export default {
       methods: {
       setData(data) {
       const {
       title, author, publisher, language, rootFile, cover, originalName, contents, coverPath, filePath, unzipPath, contentsTree } = data this.postForm = {
       ...this.postForm, title, //  Title  author, //  author  publisher, //  Press.  language, //  Language  rootFile, //  Root file path  filePath, //  File path  unzipPath, //  The path where the extracted file is located  coverPath, //  Cover picture path  cover, //  Cover picture URL contents, //  Catalog  originalName //  file name  } this.contentsTree = contentsTree }, setDefault() {
       this.postForm = Object.assign({
      }, defaultForm) this.contentsTree = [] }, onContentClick(data) {
       if (data.text) {
       window.open(data.text) } }, onUploadSucess(data) {
       this.setData(data) }, onUploadRemove() {
       this.setDefault() } } } </script>

problem : The length is not the same

newNavMap and epub.flow Length inconsistency

  • newNavMap : Information in the directory
  • epub.flow : Represents the reading order of the entire e-book reader , Some contents may not be reflected in the chapter contents
const path = require('path')

class Book {
    
  // ...
  const dir = path.dirname(ncxFilePath).replace(UPLOAD_PATH, '')
  // ...
  newNavMap.forEach((chapter, index) => {
    
    const src = chapter.content['$'].src
    chapter.text = `${
      UPLOAD_URL}${
      dir}/$`
    chapter.label = chapter.navLabel.text || ''
    chapter.navId = chapter['$'].id
    chapter.filename = filename
    chapter.order = index + 1
    chapters.push(chapter)
  })
}

Form verification

  • Add... To editable form items prop

  • adopt fields[Object.keys(fields)[0]][0].message Get error message

    In this way, the effect shown in the figure below can be achieved

const fields = {
    
  title: ' title ',
  author: ' author ',
  publisher: ' Press. ',
  language: ' Language '
}
export default {
    
  data() {
    
    const validateRequire = (rule, value, callback) => {
    
      if (value.length === 0) {
    
        callback(new Error(fields[rule.field] + ' Must fill in '))
      } else {
    
        callback()
      }
    }
    return {
    
      rules: {
    
        title: [{
     validator: validateRequire }],
        author: [{
     validator: validateRequire }],
        publisher: [{
     validator: validateRequire }],
        language: [{
     validator: validateRequire }]
      }
    }
  },
  methods: {
    
    submitForm() {
    
      this.$refs.postForm.validate((valid, fields) => {
    
        if (valid) {
    
          //...
        } else {
    
          const message = fields[Object.keys(fields)[0]][0].message
          this.$message({
    
            message,
            type: 'error',
            showClose: true
          })
        }
      })
    }
  }
}

Submit Form

You need to provide two interfaces when submitting forms ,createBook and updateBook

  • src\api\book.js
import request from '@/utils/request'

export function createBook(book) {
    
  return request({
    
    url: '/book/create',
    method: 'post',
    data: book
  })
}

modify submitForm Method

import {
     createBook } from '@/api/book'

if (valid) {
    
  const book = Object.assign({
    }, this.postForm)
  delete book.status
  delete book.contentsTree
  if (!this.isEdit) {
    
    createBook(book)
      .then(response => {
    
        const {
     msg } = response
        this.$notify({
    
          title: ' Successful operation ',
          message: msg,
          type: 'success',
          duration: 2000
        })
        this.setDefault()
      })
      .catch(() => {
    })
      .finally(() => (this.loading = false))
  } else {
    
    // updateBook(book)
  }
} else {
    
  const message = fields[Object.keys(fields)[0]][0].message
  this.$message({
    
    message,
    type: 'error',
    showClose: true
  })
}

After the upload is successful, there will be 2 A question

  1. After the new form is successfully added, a verification exception will appear when the form is cleared
  2. The file list was not removed
setDefault() {
    
  //...
  this.fileList = []
  this.$refs.postForm.resetFields()
}

The backend development API

New books

  • router\book.js increase create Interface

    adopt jwt Verify that the user name is obtained , After the req.body Parameters are passed into Book class

const {
     decoded } = require('../utils')
const bookService = require('../services/book')

router.post('/create', (req, res, next) => {
    
  const decode = decoded(req)
  if (decode && decode.username) {
    
    req.body.username = decode.username
  }
  const book = new Book(null, req.body)
  bookService
    .insertBook(book)
    .then(() => {
    
      new Result(' E-book added successfully ').success(res)
    })
    .catch(err => next(boom.badImplementation(err)))
})

stay utils\constant.js Fill in the following :

UPDATE_TYPE_FROM_WEB: 1

stay models\Book.js Fill in the following :

  • Add from data Object Book Object methods

  • newly added toDb Method , So that when inserting the database Book There are more fields in the object than in the design table

    book The contents of the design table are as follows :

const {
     UPDATE_TYPE_FROM_WEB } = require('../utils/constant')

createBookFromData(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.path || data.filePath
  this.filePath = data.path || data.filePath
  this.unzipPath = data.unzipPath
  this.coverPath = data.coverPath
  this.createUser = data.username
  this.createDt = new Date().getTime()
  this.updateDt = new Date().getTime()
  this.updateType = data.updateType === 0 ? data.updateType : UPDATE_TYPE_FROM_WEB
  this.contents = data.contents
  this.category = data.category || 99
  this.categoryText = data.categoryText || ' Customize '
}

toDb() {
    
  return {
    
    fileName: this.fileName,
    cover: this.cover,
    title: this.title,
    author: this.author,
    publisher: this.publisher,
    bookId: this.fileName,
    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,
  }
}

newly build services\book.js Fill in the following :

  • The main task here is to execute insert Method , Biography is book.toDb()
const Book = require('../models/Book')
const db = require('../db')

function exists(book) {
    }

function removeBook(book) {
    }

function insertContents(book) {
    }

function insertBook(book) {
    
  return new Promise(async (resolve, reject) => {
    
    try {
    
      if (book instanceof Book) {
    
        const result = await exists(book)
        if (result) {
    
          await removeBook(book)
          reject(new Error(' E-books already exist '))
        } else {
    
          await db.insert(book.toDb(), 'book')
          await insertContents(book)
          resolve()
        }
      } else {
    
        reject(new Error(' The added Book object is illegal '))
      }
    } catch (e) {
    
      reject(new Error(e))
    }
  })
}

module.exports = {
     insertBook }

stay utils\index.js The following methods are added in the :

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

perfect db\index.js Medium insert.js Method :

  • In fact, you can not add model.hasOwnProperty() , because Object.keys() The return includes the object itself ( Without inheritance ) All enumerable properties (enumerable: true
function insert(model, tableName) {
    
  return new Promise((resolve, reject) => {
    
    if (!isObject(model)) reject(' Insert database failed , Insert data non object ')
    const keys = []
    const values = []
    Object.keys(model).forEach(key => {
    
      if (!model.hasOwnProperty(key)) return
      keys.push(`\`${
      key}\``) //  avoid key And sql Duplicate keyword for 
      values.push(`'${
      model[key]}'`)
    })
    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})`
      DEBUG && console.log(sql)
      const conn = connect()
      try {
    
        conn.query(sql, (err, result) => {
    
          if (err) return reject(err)
          resolve(result)
        })
      } catch (e) {
    
        reject(e)
      } finally {
    
        conn.end() //  Be sure to add , Otherwise, memory leakage will occur 
      }
    } else {
    
      reject(new Error(' Insert database failed , Object doesn't have any properties '))
    }
  })
}

stay models\Book.js Newly added getContents Method :

getContents() {
    
  return this.contents
}

contents The contents of the design table are as follows :

stay services\book.js New China insertContents Method :

  • Use lodash Medium pick Method , Clean up redundant fields , After cleaning, see the following figure :

const _ = require('lodash')

async function insertContents(book) {
    
  const contents = book.getContents()
  if (contents && contents.length > 0) {
    
    for (let i = 0; i < contents.length; i++) {
    
      const content = contents[i]
      const _content = _.pick(content, [
        'fileName',
        'id',
        'href',
        'text',
        'order',
        'level',
        'label',
        'pid',
        'navId',
      ])
      await db.insert(_content, 'contents')
    }
  }
}

Click "add e-book" and the front prompt will appear : E-book added successfully , View that the database has successfully inserted data

E-book duplication check

Determine whether the e-book exists in the database , We can start from three aspects : Title 、 author 、 Press.

stay services\book.js Revision in China exists Method content :

function exists(book) {
    
  const {
     title, author, publisher } = book
  const sql = `select * from book where title='${
      title}' and author='${
      author}' and publisher='${
      publisher}'`
  return db.queryOne(sql)
}

If the ebook exists in the database , From book Table and contents Remove relevant content from the table

async function removeBook(book) {
    
  if (book) {
    
    book.reset()
    if (book.fileName) {
    
      const removeBookSql = `delete from book where fileName='${
      book.fileName}'`
      const removeContentSql = `delete from contents where fileName='${
      book.fileName}'`
      await db.querySql(removeBookSql)
      await db.querySql(removeContentSql)
    }
  }
}

book Example of reset Method is used to delete the duplicate e-book related resources just uploaded by the server (filePathcoverPathunzipPath

  • fs.unlinkSync : Used to delete the file of the corresponding path
  • fs.rmdirSync : Used to delete the folder of the corresponding path
reset() {
    
  if (Book.pathExists(this.filePath)) {
    
    fs.unlinkSync(Book.genPath(this.filePath))
  }
  if (Book.pathExists(this.coverPath)) {
    
    fs.unlinkSync(Book.genPath(this.coverPath))
  }
  if (Book.pathExists(this.unzipPath)) {
    
    //  Be careful node The second attribute... Will not be supported in the lower version ( Iteratively delete )
    fs.rmdirSync(Book.genPath(this.unzipPath), {
     recursive: true })
  }
}

static pathExists(path) {
    
  if (path.startsWith(UPLOAD_PATH)) return fs.existsSync(path)
  return fs.existsSync(Book.genPath(path))
}

Since then, the e-book de duplication operation has been completed

E-book query

Modify in the front-end project src\router\index.js stay book/edit After adding :fileName Form a dynamic route

{
    
  path: '/book/edit/:fileName',
  component: () => import('@/views/book/edit'),
  name: 'bookEdit',
  hidden: true,
  meta: {
     title: ' Editing books ', icon: 'edit', roles: ['admin'], activeMenu: '/book/list' }
},

stay src\views\book\components\Detail.vue New life cycle function in created and getBookData Method

created() {
    
  if (this.isEdit) {
    
    const fileName = this.$route.params.fileName
    this.getBookData(fileName)
  }
},
methods: {
    
  getBookData(fileName) {
    
    getBook(fileName).then(response => {
    
      this.setData(response.data)
    })
  },
}

stay src\api\book.js Newly added getBook API:

export function getBook(fileName) {
    
  return request({
    
    url: '/book/get',
    method: 'get',
    params: {
     fileName }
  })
}

Then you can start developing getBook The interface . stay router\book.js To add the following :

router.get('/get', (req, res, next) => {
    
  const {
     fileName } = req.query
  if (!fileName) {
    
    next(boom.badRequest(new Error(' Parameters fileName Can't be empty ')))
  } else {
    
    bookService
      .getBook(fileName)
      .then(book => {
    
        new Result(book, ' Successful acquisition of book information ').success(res)
      })
      .catch(err => {
    
        next(boom.badImplementation(err))
      })
  }
})

stay services\book.js To add the following :

function getBook(fileName) {
    
  return new Promise(async (resolve, reject) => {
    
    const bookSql = `select * from book where fileName='${
      fileName}'`
    const contentSql = `select * from contents where fileName='${
      fileName}' order by \`order\``
    const book = await db.queryOne(bookSql)
    const contents = await db.querySql(contentSql)
    if (book) {
    
      book.cover = Book.genCoverUrl(book)
      book.contentsTree = Book.genContentsTree(contents)
    } else {
    
      reject(new Error(' E-books don't exist '))
    }
    return resolve(book)
  })
}

Restart the server , Refresh the browser , There is something wrong with the cover and contents

stay utils\constant.js add OLD_UPLOAD_URL , Used to be compatible with previous projects cover The address of

const OLD_UPLOAD_URL = env === 'dev'?
      'http://127.0.0.1:8089/book/res/img' :
      'http://www.book.llmysnow.top/book/res/img'

stay models\Book.js New static transformation in cover Path method :

static genCoverUrl(book) {
    
  const {
     cover } = book
  if (+book.updateType === 0) {
    
    if (cover) {
    
      if (cover.startsWith('/')) {
    
        return `${
      OLD_UPLOAD_URL}${
      cover}`
      } else {
    
        return `${
      OLD_UPLOAD_URL}/${
      cover}`
      }
    } else {
    
      return null
    }
  } else {
    
    if (cover) {
    
      if (cover.startsWith('/')) {
    
        return `${
      UPLOAD_URL}${
      cover}`
      } else {
    
        return `${
      UPLOAD_URL}/${
      cover}`
      }
    } else {
    
      return null
    }
  }
}

such cover The path is handled , The next step is to deal with content 了 , stay models\Book.js New static transformation in contents Directory method :

static genContentsTree(contents) {
    
  if (contents) {
    
    const contentsTree = []
    contents.forEach(c => {
    
      c.children = []
      if (c.pid === '') {
    
        contentsTree.push(c)
      } else {
    
        const parent = contents.find(_ => _.navId === c.pid)
        parent.children.push(c)
      }
    })
    return contentsTree
  }
}

After the front end requests the data, it needs to display the upload list , stay setData Add :

setData(data) {
    
  // ....
  this.fileList = [{
     name: originalName || fileName }]
},

Update ebook

stay src\api\book.js Add the following new content in :

export function updateBook(book) {
    
  return request({
    
    url: '/book/update',
    method: 'post',
    data: book
  })
}

stay src\views\book\components\Detail.vue Of submitForm It is revised as follows :

submitForm() {
    
  const onSuccess = response => {
    
    const {
     msg } = response
    this.$notify({
    
      title: ' Successful operation ',
      message: msg,
      type: 'success',
      duration: 2000
    })
    this.loading = false
  }
  this.loading = true
  this.$refs.postForm.validate((valid, fields) => {
    
    if (valid) {
    
      const book = Object.assign({
    }, this.postForm)
      delete book.status
      delete book.contentsTree
      if (!this.isEdit) {
    
        createBook(book)
          .then(response => {
    
            onSuccess(response)
            this.setDefault()
          })
          .catch(() => {
    })
      } else {
    
        updateBook(book)
          .then(response => {
    
            onSuccess(response)
          })
          .catch(() => {
    })
      }
    } else {
    
      const message = fields[Object.keys(fields)[0]][0].message
      this.$message({
    
        message,
        type: 'error',
        showClose: true
      })
      this.loading = false
    }
  })
},

Next, modify the server , stay router\book.js New interface in :

router.post('/update', (req, res, next) => {
    
  const decode = decoded(req)
  if (decode && decode.username) {
    
    req.body.username = decode.username
  }
  const book = new Book(null, req.body)
  bookService
    .updateBook(book)
    .then(() => {
    
      new Result(' E-book update succeeded ').success(res)
    })
    .catch(err => next(boom.badImplementation(err)))
})

stay services\book.js New e-book update logic in updateBook Method :

function updateBook(book) {
    
  return new Promise(async (resolve, reject) => {
    
    try {
    
      if (book instanceof Book) {
    
        const result = await getBook(book.fileName)
        if (result) {
    
          const model = book.toDb()
          if (+result.updateType === 0) {
    
            reject(new Error(' Built in books cannot be edited '))
          } else {
    
            await db.update(model, 'book', `where fileName='${
      book.fileName}'`)
            resolve()
          }
        }
      } else {
    
        reject(new Error(' The added Book object is illegal '))
      }
    } catch (e) {
    
      reject(new Error(e))
    }
  })
}

stay db\index.js Add database update in update Method :

function update(zmodel, tableName, where) {
    
  return new Promise((resolve, reject) => {
    
    if (!isObject(model)) reject(' Insert database failed , Insert data non object ')
    const entry = []
    Object.keys(model).forEach(key => {
    
      if (model.hasOwnProperty(key)) {
    
        entry.push(`\`${
      key}\`='${
      model[key]}'`)
      }
    })
    if (entry.length > 0) {
    
      let sql = `UPDATE \`${
      tableName}\` SET`
      sql = `${
      sql} ${
      entry.join(',')} ${
      where}`
      DEBUG && console.log(sql)
      const conn = connect()
      try {
    
        conn.query(sql, (err, result) => {
    
          if (err) return reject(err)
          resolve(result)
        })
      } catch (e) {
    
        reject(e)
      } finally {
    
        conn.end()
      }
    }
  })
}

Delete book、img、unzip Contents of Li

Deleting these folders manually is really a bit cumbersome , Create a new directory in the root directory delete.js

  • I'm going through each file and delete it in turn ( The advantage is : It can be controlled in the middle , You can specify that a file is not deleted ) Of course, you can also delete the folder directly , Create folder again
const {
     UPLOAD_PATH } = require('./utils/constant')
const path = require('path')
const fs = require('fs')

const bookPath = path.resolve(UPLOAD_PATH, 'book')
const imgPath = path.resolve(UPLOAD_PATH, 'img')
const unzipPath = path.resolve(UPLOAD_PATH, 'unzip')

function removeFile(dirPath) {
    
  return new Promise((resolve, reject) => {
    
    fs.readdir(dirPath, (err, files) => {
    
      if (err) return reject(err)
      if (!files.length) return reject(`${
      dirPath} The folder is empty `)
      files.forEach(item => {
    
        const filePath = path.resolve(dirPath, item)
        if (fs.statSync(filePath).isFile()) {
    
          fs.unlinkSync(filePath)
        } else {
    
          fs.rmdirSync(filePath, {
     recursive: true })
        }
      })
      resolve(`${
      dirPath}  Delete successful `)
    })
  })
}

Promise.allSettled([removeFile(bookPath), removeFile(imgPath), removeFile(unzipPath)])
  .then(res => {
    
    res.forEach(item => {
    
      if (item.value) console.log(item.value)
      if (item.reason) console.log(item.reason)
    })
  })
  .catch(err => console.log(err))
原网站

版权声明
本文为[Snow flies fast]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/03/202203010613541347.html