电子书上传
创建上传页面组件
电子书上传过程分为新增电子书和编辑电子书,新增:
<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
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
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
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
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
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
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
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
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
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
2
3
toDefault 方法源码如下:
toDefault() {
this.postForm = Object.assign({}, defaultForm)
this.fileList = []
this.contentsTree = []
}
1
2
3
4
5
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
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
2
3
4
5
6
用户引导
具体用法查看用户引导