EBook list

Posted by l053r on Sun, 26 Dec 2021 01:34:38 +0100

EBook list

E-book analysis data display

EbookUpload component

this.emit('onSuccess',response.data)

book/components/detail.vue

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

After getting the data

Then write a setData method

 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,
      };
    },

Re execute

The case of filename here is wrong, but it is better to change the filename to originalName

Tree expansion

El tree problem solving

Pass in the data attribute, which is an array. The array contains several objects. Each object corresponds to a level of tree, which contains label and children

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

E-book directories are different. We only use one-dimensional arrays for easy parsing, while the example is a nested array

We go to the server to transform it into nested. Of course, we can also do processing at the front end

If pid is not recognized, it indicates that it is a primary directory

              const chapterTree=[]
              chapters.forEach(c=>{
                c.children=[]
                //If pid is not recognized, it indicates that it is a primary directory
                if(c.pid===''){
                  chapterTree.push(c)
                }
              })
      		  const chapterTree = []
              chapters.forEach(c => {
                // We have defined and assigned the label attribute earlier 
                c.children = []
                //If pid is not recognized, it indicates that it is a primary directory
                if (c.pid === '') {
                  chapterTree.push(c)
                } else {
                  // If pid is not empty, it means that if there is a parent, find the parent first
                  const parent = chapters.find(_ =>
                    // If it's the same, you'll find the parent
                    _.navId === c.pid
                  )
                  parent.children.push(c)
                }
              })
              console.log(chapterTree);

How to view the chapter content after clicking

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

Look what's in the data

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

The most important is the text parameter

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

Click into a chapter

But there are still problems with some books. For example, this one is different from the directory. 15-3 is optimized, so it is not optimized here

Development of e-book form verification function

Click the cross to clear

Supplement defaultForm

Check function

Looking at the element UI source code, you can see that two parameters will be returned

    submitForm(){
      this.$refs.postForm.validate((valid,fileds)=>{
         console.log(valid,fileds);
          // Pass verification
        if(valid){
         
        }else{

        }
      })
    },

Write a rules object whose key is the prop attribute written in El form item

You can directly write required in the title. The custom verification rules used here (pass in function)

  const validateRequire=(rule,value,callback)=>{
      if(value===''||value.length==0){
        callback(new Error('title Cannot be empty'))
      }else{
        callback()
      }
    }

You can print the rule

reform

callback(new Error(rule.field + 'must be filled in'))

Here's a trick to do a field mapping

Write an object

callback(new Error(fields[rule.field] + 'required'))

Now I want to get this sentence and show it on the page

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

   submitForm(){
      this.$refs.postForm.validate((valid,fields)=>{
        console.log(valid,fields);
        if(valid){
         
        }else{
          // This is a general exception handling method
          console.log(fields[Object.keys(fields)[0]][0].message)
          const message=fields[Object.keys(fields)[0]][0].message
          this.$message({
            message,type:'error'
          })
        }
      })
    },

Add a prop author publisher language to the author publisher language

Add mapping

Then add rules in rules

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

New e-book front-end logic

There are two methods for shallow copy: extension operator / object assign

Request interface

src/api/book.js

Detail.vue introduction

Interface development

utils/index.js uses the decode here

book.js

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

Write a Book object. The logic generated by this book object can be from model / book JS

Database data

When printing data, you must pay attention to the code specification. The hump is the hump. Several useless humps regret it

  createBookFromData(data) {
    // The fields in data need to be mapped with the database fields
    // 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 means it is the default book, otherwise it means it comes from the Internet (1)
    this.updateType=data.updateType===0?data.updateType:1
    this.category=data.category||99
    this.categoryText=data.categoryText||'custom'
  }

The book object is generated so that sql statements can be generated through the object

New e-book core logic ideas

Create a new service / book JS database operations

const Book=require('../model/Book')
function insertBook(book){
  return new Promise((resolve,reject)=>{
    try {
      // Book must be an instance of the book object to avoid the absence of some parameters
      // If it is an instance, it has the advantage of ensuring that those parameters are complete,
      // Otherwise, an error may occur if a book object is arbitrarily passed in for insert ion
      if(book instanceof Book) {
        
      }else{
        reject(new Error('The added Book object is illegal'))
      }
    } catch (error) {
      reject(error)
    }
  })
}
module.exports={insertBook}

router/book.js

router.post('/create', (req, res, next) => {
  const decoded = decode(req)
  // Solve jwt
  // console.log(decoded);
  // Front end incoming information
  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);
    // Error and front end linkage 
    next(boom.badImplementation(err))
  })  
  console.log(book)
})

This is the general framework

Judge whether the e-book exists. If it already exists, remove the information in the database and the file

After insert, insert the directory into the directory table

There will be a large number of asynchronous operations in the operation database. Use async await to turn it into a synchronous method to reduce the number of callbacks. If promise is used, there will be a lot of nesting

That's the logic. Next, write a specific method

Database operation

The insert method determines whether the first parameter passed in is an object

Method to determine whether it is an object: utils / index js

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

It's very accurate to judge with this

function insert(model,tableName){
  return new Promise((resolve,reject)=>{
    if(!isObject(model)){
      reject(new Error('Failed to insert database, inserting data is not an object'))
    }
  })
}

Let's experiment

Back to the insert method

There is a skill in database operation

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

Why? For example, in select from from book, the first from is not a keyword but a key, but the database will automatically recognize that it is a keyword and report an error. After adding backquotes, there is no problem

  // Spell sql statement
      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)
      }

Then try to execute the sql statement successfully

 // Spell sql statement
      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('There are no properties in the object'))
      }

Prompt: there is no path field in the database

Here is a recommended practice

Book.js adds a new method toDb to filter the book object instead of using it as a whole

Remove the 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
    }
  }

This book will be changed to book Todd () passes the result returned by toDb to insertBook, which will report an error, because insertBook will judge whether the incoming object is an instance of book

It's appropriate to write here

In this way, click Add to find more data in the database

Front end interaction optimization

insertBook (book). Then ((RES) = > {}) res cannot be written here because the success method will automatically pass in res here, but in fact, res indicated by the red arrow above should be passed in

front end:

Get the back-end return value and display it on the front-end page

Don't use $message this time

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

Use a $notify that looks like him

Another detail is that after the upload is successful, the list data can be removed, which has been used in the remove method

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

The list is removed, but there are still bug s

Here is a setDefault method to solve these problems

Leave this blank and the title is gone

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

Next, eliminate the verification result

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

This is because no prop is passed in. If no prop is passed in, it will not be considered as a form option

The directory has not been eliminated

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

Removing is also a call to this method

Add catalog to database function

Next, write the insertContents method

You need to get the contents under book

But the contents was deleted earlier, so there was no directory passed in

front end:

Back end reception:

You can write a getContents

Ensure that you can insert the database after you get the contents

In contrast, there are still many redundant fields

You can create an object and assign values once. Here's a trick - it's implemented through lodash

Through lodash, we can use its methods to achieve the functions we want

Call the insert method

await db.insert(_content,'contents')

E-book deletion function

The following two logics have been written and the logic to be removed has been written

First, write the exists function to judge whether the e-book exists

In order to facilitate the test, put the front-end this setDefault(); Comment out

In this case, you need to remove the currently uploaded e-book

function removeBook(book) {
  if(book){
    // Delete relevant files (e-book files, cover pages, unzipped files)
    book.reset()
  }
}

Enter book js

  // Determine whether there is a static method for the path
  static pathExists(path){
    if(path.startsWith(UPLOAD_PATH)){
      return fs.existsSync(path)
    }else{
      return fs.existsSync(Book.genPath(path))
    }
  }

Let's simulate it

Single file deletion: unlinkSync

When deleting the decompression path, remember to add recursive: true FS rmdirSync(Book.genPath(this.unzipPath),{recursive:true})

This keeps only one file

The teacher added another operation to delete the database. I don't know why to add this step. It should not be written to the database, so I don't think I need to add this logic to delete the database

E-book query api

To realize the editing function, first get the filename and query the directory and content in the database according to the filename

It is returned to the front end after finding it, so you need to receive a parameter in the route

After that, you can click detail Get dynamic routing parameters from Vue

thinking

Now we need to implement the api of getBook

src/api/book.js

Note that the get method uses params. If it is post, it uses data

Server:

router.book.js

Rough frame

The getBook method is in service / book JS

Improve getBook logic

You need to query the book table and contents table

Further processing to show the data

    resolve(book[0])

But there is no title, cover or catalogue

Solve the problem of the cover first

To solve the problem of directory display

Now the contents is an array structure. You need to convert the array into a contenttree

In fact, we have done this logic before,

 const chapterTree = []
    chapters.forEach(c => {
      // We have defined and assigned the label attribute earlier 
      c.children = []
      //If pid is not recognized, it indicates that it is a primary directory
      if (c.pid === '') {
        chapterTree.push(c)
      } else {
        // If pid is not empty, it means that if there is a parent, find the parent first
        const parent = chapters.find(_ =>
          // If it's the same, you'll find the parent
          _.navId === c.pid
        )
        parent.children.push(c)

      }
    })

At this time, the directory still can't come out. Change the front-end code

The next unsolved problem is the file information

Just assign a value to fileList

Edit eBook

Next, write the logic here

An interface is added to the front end

Although the interface has not been developed yet, you can still go to the network to have a look. You can find that the interface and parameters can be sent correctly

Now the server adds an interface

It is very similar to the previous create interface (it should be update if written incorrectly)

Now go to service / book JS to write the logic of updateBook

front end

Book here Filename should be quoted

Write an update method to the database operation file

Topics: Vue