电子书上传

创建上传页面组件

电子书上传过程分为新增电子书和编辑电子书,新增:

<template>
  <detail :is-edit="false" />
</template>

<script>
import Detail from './components/Detail'

export default {
  name: 'CreateBook',
  components: { Detail }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12

编辑:

<template>
  <article-detail :is-edit />
</template>

<script>
import Detail from './components/Detail'

export default {
  name: 'EditBook',
  components: { Detail }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12

Detail 组件比较复杂,我们逐步实现,首先实现 Detail 的大体布局,包括一个 el-form 和 sticky 导航栏,sticky 在内容较多时会产生吸顶效果:

<div class="detail">
    <el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
      <sticky :z-index="10" :class-name="'sub-navbar ' + postForm.status">
        <el-button v-if="!isEdit" @click.prevent.stop="showGuide">显示帮助</el-button>
        <el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">
          {{ isEdit ? '编辑电子书' : '新增电子书' }}
        </el-button>
      </sticky>
      <div class="detail-container">
        <el-row>
          <Warning />
          <el-col :span="24">
            <!-- 编写具体表单控件 -->          
          </el-col>
          <el-col :span="24">
            <!-- 编写具体表单控件 -->          
          </el-col>
        </el-row>
      </div>
    </el-form>
</div>

<style lang="scss" scoped>
  @import "~@/styles/mixin.scss";

  .detail {
    position: relative;
    .detail-container {
      padding: 40px 45px 20px 50px;
      .preview-img {
        width: 200px;
        height: 270px;
      }
      .contents-wrapper {
        padding: 5px 0;
      }
    }
  }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

上传组件开发

这里我基于 el-upload 封装了上传组件 EbookUpload,基于 EbookUpload 我们再实现上传组件就非常容易了:

<el-form-item prop="image_uri" style="margin-bottom: 0">
  <Upload
    v-model="postForm.image_uri"
    :file-list="fileList"
    :disabled="isEdit"
    @onSuccess="onUploadSuccess"
    @onRemove="onUploadRemove"
  />
</el-form-item>
1
2
3
4
5
6
7
8
9

上传图书表单

图书表单中包括以下信息:

  • 书名
  • 作者
  • 出版社
  • 语言
  • 根文件
  • 文件路径
  • 解压路径
  • 封面路径
  • 文件名称
  • 封面
  • 目录
<el-col :span="24">
    <el-form-item style="margin-bottom: 40px;" prop="title">
      <MDinput v-model="postForm.title" :maxlength="100" name="name" required>
        书名
      </MDinput>
    </el-form-item>
    <div>
      <el-row>
        <el-col :span="12" class="form-item-author">
          <el-form-item :label-width="labelWidth" label="作者:">
            <el-input
              v-model="postForm.author"
              placeholder="作者"
              style="width: 100%"
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label-width="labelWidth" label="出版社:">
            <el-input
              v-model="postForm.publisher"
              placeholder="出版社"
              style="width: 100%"
            />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item :label-width="labelWidth" label="语言:">
            <el-input
              v-model="postForm.language"
              placeholder="语言"
              style="width: 100%"
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label-width="labelWidth" label="根文件:">
            <el-input
              v-model="postForm.rootFile"
              placeholder="根文件"
              style="width: 100%"
              disabled
            />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item :label-width="labelWidth" label="文件路径:">
            <el-input
              v-model="postForm.filePath"
              placeholder="文件路径"
              style="width: 100%"
              disabled
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label-width="labelWidth" label="解压路径:">
            <el-input
              v-model="postForm.unzipPath"
              placeholder="解压路径"
              style="width: 100%"
              disabled
            />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item :label-width="labelWidth" label="封面路径:">
            <el-input
              v-model="postForm.coverPath"
              placeholder="封面路径"
              style="width: 100%"
              disabled
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label-width="labelWidth" label="文件名称:">
            <el-input
              v-model="postForm.fileName"
              placeholder="文件名称"
              style="width: 100%"
              disabled
            />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="24">
          <el-form-item label-width="60px" label="封面:">
            <a v-if="postForm.cover" :href="postForm.cover" target="_blank">
              <img :src="postForm.cover" class="preview-img">
            </a>
            <span v-else></span>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="24">
          <el-form-item label-width="60px" label="目录:">
            <div
              v-if="postForm.contents && postForm.contents.length > 0"
              class="contents-wrapper"
            >
              <el-tree :data="contentsTree" @node-click="onContentClick" />
            </div>
            <span v-else></span>
          </el-form-item>
        </el-col>
      </el-row>
    </div>
</el-col>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

上传 API 开发

指定目的 nginx 上传路径,这样做的好处是一旦电子书拷贝到指定目录下后,就可以通过 nginx 生成下载链接:

const { env } = require('./env')
const UPLOAD_PATH = env === 'dev' ?
  '/Users/sam/upload/admin-upload-ebook' :
  '/root/upload/admin-upload-ebook'
1
2
3
4

开发电子书模型 Book 和电子书逻辑参考:电子书解析

安装 multer:

const multer = require('multer')
1

下载 API:

router.post(
  '/upload',
  multer({ dest: `${UPLOAD_PATH}/book` }).single('file'),
  function(req, res, next) {
    if (!req.file || req.file.length === 0) {
      new Result('上传电子书失败').fail(res)
    } else {
      const book = new Book(req.file)
      book.parse()
        .then(book => {
          new Result(book.toJson(), '上传成功').success(res)
        })
        .catch((err) => {
          console.log('/book/upload', err)
          next(boom.badImplementation(err))
          book.reset()
        })
    }
  })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

前端逻辑

上传成功事件

上传成功时,会将解析的电子书内容填入表单

onUploadSuccess(data) {
  this.setData(data)
}
1
2
3

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
    }
    this.fileList = [{ name: originalName, url }]
    this.contentsTree = contentsTree
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

删除电子书事件

删除电子书时,会将电子书表单内容复原

onUploadRemove() {
  this.toDefault()
}
1
2
3

toDefault 方法源码如下:

toDefault() {
    this.postForm = Object.assign({}, defaultForm)
    this.fileList = []
    this.contentsTree = []
}
1
2
3
4
5

我们需要将表单复原,所以需要表单的默认值:

const defaultForm = {
    title: '', // 书名
    author: '', // 作者
    publisher: '', // 出版社
    language: '', // 语种
    rootFile: '', // 根文件路径
    cover: '', // 封面图片URL
    coverPath: '', // 封面图片路径
    fileName: '', // 文件名
    originalName: '', // 文件原始名称
    filePath: '', // 文件所在路径
    unzipPath: '', // 解压文件所在路径
    contents: [] // 目录
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

目录点击事件

onContentClick(data) {
    const { text } = data
    if (text) {
      window.open(text)
    }
}
1
2
3
4
5
6

用户引导

具体用法查看用户引导

上次更新: 11/27/2019, 10:30:11 PM