第7章 网站前台-吐槽与问答

学习目标:

**
完成吐槽列表与详细页
完成发吐槽与评论功能,掌握富文本编辑器的使用
完成问答频道功能
掌握DataURL和阿里云OSS**

1 吐槽列表与详细页

1.1 吐槽列表页

1.1.1 吐槽列表页数据渲染
吐槽列表页已经构建,我们现在来实现数据的渲染
(1)easyMock模拟数据
URL: spit/spit/search/{page}/{size}
Method: post

{
"code": 20000,
"flag": true,
"message": "查询成功",
"data": {
"total": "@integer(60, 100)",
"rows|10": [{
"id": "@string",
"content": "@cword(100,300)",
"publishtime": "@datetime",
"userid": "@string",
"nickname": "小雅",
"visits": "@integer(60, 100)",
"thumbup": "@integer(60, 100)",
"share": "@integer(60, 100)",
"comment": "@integer(60, 100)",
"state": "@string",
"parentid": "@string"
}]
}
}

(2)api目录下创建spit.js

import request from '@/utils/request'
const group_name = 'spit'
const api_name = 'spit'export default { search(page, size, searchMap) { return request({url: `/${group_name}/${api_name}/search/${page}/${size}`,method: 'post',data: searchMap }) } }

(3)修改pages/spit/index.vue

<template>
<div>
<div class="wrapper tag‐item">
<div class="fl left‐list">
<div class="tc‐data‐list">
<div class="tc‐list">
<ul class="detail‐list">
<li class="qa‐item" v‐for="(item,index) in items" :key="index">
<div class="fl record">
<div class="number">
<div class="border useful">
<p class="usenum"><a href="#" class="zan"><i class="fa fa‐ thumbs‐up " aria‐hidden="true"></i></a></p>
<p class="zannum"> {{item.thumbup}} </p>
</div>
<div class="border answer">
<a href="#" class="star"><i class="fa fa‐star‐o" aria‐ hidden="true"></i></a> </div>
</div>
</div>
<div class="info">
<p class="text"> <a href="~/assets/spit‐detail.html" target="_blank"> {{item.content}} </a> </p>
<div class="other">
<div class="fl date">
<span>{{item.publishtime}}</span>
</div>
<div class="fr remark">
<a href="#" data‐toggle="modal" data‐target="#shareModal" class="share"><i class="fa fa‐share‐alt" aria‐hidden="true"></i> 分享</a>
<a href="#" data‐toggle="modal" data‐target="#remarkModal" class="comment"><i class="fa fa‐commenting" aria‐hidden="true"></i> 回 复</a>
</div>
</div>
</div>
<div class="clearfix"></div> </li>
</ul>
<div class="modal fade" id="shareModal" tabindex="‐1"role="dialog" aria‐labelledby="myModalLabel">
<div class="modal‐dialog" role="document">
<div class="modal‐content">
<div class="modal‐header">
<button type="button" class="close" data‐dismiss="modal" aria‐ label="Close"><span aria‐hidden="true">&times;</span></button>
<h4 class="modal‐title" id="myModalLabel">分享到</h4>
</div>
<div class="modal‐body" style="overflow:hidden">
<div class="jiathis_style_32x32">
<a class="jiathis_button_qzone"></a>
<a class="jiathis_button_tsina"></a>
<a class="jiathis_button_weixin"></a>
<a class="jiathis_button_cqq"></a>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="remarkModal" tabindex="‐1" role="dialog" aria‐labelledby="myModalLabel">
<div class="modal‐dialog" role="document">
<div class="modal‐content">
<div class="modal‐header">
<button type="button" class="close" data‐dismiss="modal" aria‐ label="Close"><span aria‐hidden="true">&times;</span></button>
<h4 class="modal‐title" id="myModalLabel">发表评论</h4> </div> <div class="modal‐body"> <div class="comment">
<span class="who"><img src="~/assets/img/asset‐photo.png" /> 匿名评论</span>: 今天入职腾讯,产品岗,普通非985211大学,上家是不到五十人创业小 公司!16年毕业!找内推腾讯给的面试机会,五轮面试!可能是我把运气攒一起了!
</div>
<div class="form">
<textarea class="form‐control" rows="5" placeholder="匿名评 论"></textarea> <div class="remark">
<button class="sui‐btn btn‐info">发表</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="fl right‐tag">
<div class="block‐btn">
<p>来个匿名吐槽,发泄一下你心中的怒火吧!</p>
<a class="sui‐btn btn‐block btn‐share" href="~/assets/spit‐ submit.html" target="_blank">发吐槽</a>
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</template>
<script>
import '~/assets/css/page‐sj‐spit‐index.css'
import spitApi from '@/api/spit'
export default {
asyncData(){
return spitApi.search(1,10, {state:'1'} ).then( res=> {
return {items:res.data.data.rows}}) } }</script>

1.1.2 吐槽列表瀑布流
修改页面pages/spit/index.vue

<div class="tc‐list" v‐infinite‐scroll="loadMore" >

修改pages/spit/index.vue ,添加pageNo用于记录页码 增加方法

data(){
return {
pageNo: 1
}
},
methods:{
loadMore(){
this.pageNo++ spitApi.search( this.pageNo,10,{state:'1'} ).then( res=>{ this.items=this.items.concat( res.data.data.rows )}) } }

1.2 吐槽详情页

1.2.1 构建吐槽详情页
(1)根据spit-detail.html创建pages/spit/_id.vue ,内容略
(2)修改pages/spit/index.vue 页面链接

<router‐link :to="'/spit/'+item.id"> {{item.content}} </router‐link>

1.2.2 吐槽详情页数据渲染
(1)easyMock模拟数据
URL: spit/spit/{id}
Methos: GET

{
"code": 20000,
"flag": true,
"message": "@string",
"data": {
"id": "@string",
"nikename": "小雅",
"content": "@cword(100,300)",
"publishtime": "@datetime",
"thumbup": "@integer(60, 100)",
"share": "@integer(60, 100)",
"comment": "@integer(60, 100)"
}
}

URL: spit/spit/commentlist/{id}
Method: GET

{
"code": 20000,
"flag": true,
"message": "@string",
"data|10": [{
"id": "@string",
"content": "我要评论我要评论我要评论我要评论我要评论我要评论",
"nikename": "小雅",
"publishtime": "@datetime","thumbup": "@integer(60, 100)" }] }

(2)修改api/spit.js

findById(id){return request({ url: `/${api_group}/${api_name}/${id}`,method: 'get' }) },commentlist(id){return request({ url: `/${api_group}/${api_name}/commentlist/${id}`, method: 'get'})}

(3)修改pages/spit/_id.vue

<template>
<div class="wrapper tc‐detail">
<div class="fl left‐list">
<div class="tc‐detail">
<!‐‐ 标题区 ‐‐>
<div class="detail‐tit">
<div class="detail‐author">
<a href="javascript:;">{{pojo.nickname}}</a> 发布
</div>
<div class="detail‐content">
<p>{{pojo.content}}</p>
</div>
<div class="detail‐tool">
<ul>
<li><span class="star"><i class="fa fa‐thumbs‐o‐up" aria‐ hidden="true"></i> {{pojo.thumbup}}</span></li>
<li><a href="#" data‐toggle="modal" data‐target="#shareModal"><i class="fa fa‐share‐alt" aria‐hidden="true"></i> {{pojo.share}}</a></li>
<li><a data‐toggle="modal" data‐target="#remarkModal"><i class="fa fa‐commenting" aria‐hidden="true"></i> {{pojo.comment}}</a> </li>
</ul>
</div>
</div>
<!‐‐ 评论区 ‐‐>
<div class="comment‐area">
<div class="comment‐tit">
<span>评论</span>
</div>
<ul class="comment‐list">
<li v‐for="(item,index) in commentlist" :key="index">
<div class="item‐photo">
<img src="~/assets/img/widget‐widget‐photo.png" alt="" />
</div>
<div class="item‐content">
<p class="author"><a href="javascript:;">{{item.nickname}}</a> 发布</p>
<p class="content">{{item.content}}</p>
</div>
<div class="item‐thumb">
<div>
<i class="fa fa‐thumbs‐o‐up" aria‐hidden="true"></i> {{item.thumbup}}
</div>
</div> </li>
</ul>
</div>
</div>
</div>
<div class="fl right‐tag">
<div class="block‐btn"> <p>来个匿名吐槽,发泄一下你心中的怒火吧!</p>
<a class="sui‐btn btn‐block btn‐share" href="~/assets/spit‐ submit.html" target="_blank">发吐槽</a>
</div>
</div>
<div class="clearfix"></div>
</div>
</template>
<script>
import '~/assets/css/page‐sj‐spit‐detail.css'
import spitApi from '@/api/spit'
import axios from 'axios'
export default {
asyncData({params}){
return axios.all( [ spitApi.findById(params.id),spitApi.commentlist(params.id) ] ).then( axios.spread( function( pojo,commentlist ){
return {
pojo: pojo.data.data,
commentlist: commentlist.data.data
}
})
)
}
}
</script>

1.3 吐槽点赞

1.3.1 基本功能
(1)easyMock模拟数据
URL: spit/spit/thumbup/{id}
Method: put

{ "code": 20000, "flag": true, "message": "执行成功" }

(2)api/spit.js 增加方法

thumbup(id)
{
return request({
url: `/${api_group}/${api_name}/thumbup/${id}`,
method: 'put'
})
}

(3)修改pages/spit/index.vue

methods: {
thumbup(index){
spitApi.thumbup(this.items[index].id).then( res=>{
if(res.data.flag){this.items[index].thumbup++ } }) } }

(4)点赞调用

<p class="usenum" @click="thumbup(index)"><a href="#" class="zan"><i class="fa fa‐thumbs‐up " aria‐hidden="true"></i></a></p>

1.3.2 样式处理
实现点赞后,大拇指图标变色。样式表已经写好,在li 的样式上加上color 即可
(1)修改pages/spit/index.vue 的代码部分

import '~/assets/css/page‐sj‐spit‐index.css'
import spitApi from '@/api/spit'
export default {
asyncData(){
return spitApi.search(1,10, {state:'1'} ).then( res=> {
let tmp= res.data.data.rows.map( item=>{
return {
...item,
zan: ''
}
})
return {items:tmp}
})
},
data(){
return {
pageNo: 1
}
},
methods:{
loadMore(){
this.pageNo++
spitApi.search( this.pageNo,10,{state:'1'} ).then( res=>{
let tmp= res.data.data.rows.map( item=>{
return {
...item,zan: '' }}) this.items=this.items.concat( tmp ) }) },thumbup(index){ this.items[index].zan='color' spitApi.thumbup(this.items[index].id).then( res=>{ if(res.data.flag){ this.items[index].thumbup++} })}}}

(2)修改pages/spit/index.vue的html部分

<a href="#" class="zan">
<i :class="'fa fa‐thumbs‐up '+item.zan" aria‐hidden="true"></i>
</a>

1.3.3 判断是否登陆
要求:点赞必须要在用户登陆的情况下执行,非登陆状态下不能点赞。并且不可重复点 赞
导入getUser

import {getUser} from '@/utils/auth'

修改thumbup(点赞)方法

thumbup(index){
if(getUser().name===undefined){
this.$message({
message:'必须登陆才可以点赞哦~',
type:"warning" })
return }
if( this.items[index].zan==='color'){this.$message({ message:'不可以重复点赞哦~', type:"warning" })return}this.items[index].zan='color' spitApi.thumbup(this.items[index].id).then( res=>{ if(res.data.flag){ this.items[index].thumbup++ } }) }

1.3.4 提交token
修改utils/request.js ,每次请求将token添加到header里

import axios from 'axios'
import {getUser} from '@/utils/auth'
// 创建axios实例
const service = axios.create({
baseURL: 'http://192.168.184.133:7300/mock/5af314a4c612520d0d7650c7', // api的base_url
timeout: 30000, // 请求超时时间
headers: { 'Authorization': 'Bearer '+getUser().token }
})
export default service

2 发吐槽与吐槽评论

2.1 发吐槽

2.1.1 构建页面
我们这里用到VUE常用的富文本编辑器vue-quill-editor
详见文档: https://www.npmjs.com/package/vue-quill-editor
(1)安装vue-quill-editor

cnpm install vue‐quill‐editor ‐‐save

(2)plugins下创建nuxt-quill-plugin.js

import Vue from 'vue'
import VueQuillEditor from 'vue‐quill‐editor/dist/ssr' Vue.use(VueQuillEditor)

(3)修改nuxt.config.js ,配置插件和样式

plugins: [{ src: '~plugins/nuxt‐quill‐plugin.js', ssr: false } ],// some nuxt config... css: ['quill/dist/quill.snow.css', 'quill/dist/quill.bubble.css','quill/dist/quill.core.css' ],

(4)pages/spit/submit.vue

<template>
<div class="wrapper release‐tc">
<div class="release‐box">
<h3>发布吐槽</h3>
<div class="editor">
<div class="quill‐editor"
:content="content"
@change="onEditorChange($event)"@blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)" v‐quill:myQuillEditor="editorOption"> </div> <div class="btns"> <button class="sui‐btn btn‐danger btn‐release">发布</button> </div> <div class="clearfix"></div> </div> </div> <div class="clearfix"></div> </div> </template> <script> import '~/assets/css/page‐sj‐spit‐submit.css' export default { data () { return { content: '', editorOption: {// some quill options modules: { toolbar: [ [{ 'size': ['small', false, 'large'] }],['bold', 'italic'],[{ 'list': 'ordered'}, { 'list': 'bullet' }], ['link', 'image']] }} } },mounted() { console.log('app init, my quill insrance object is:', this.myQuillEditor) /*setTimeout(() => { this.content = 'i am changed' }, 3000)*/ },methods: { onEditorBlur(editor) { console.log('editor blur!', editor) },onEditorFocus(editor) { console.log('editor focus!', editor) },onEditorReady(editor) { console.log('editor ready!', editor) },onEditorChange({ editor, html, text }) { console.log('editor change!', editor, html, text) this.content = html }}} </script> <style> .quill‐editor { min‐height: 200px; max‐height: 400px; overflow‐y: auto; } </style>

(5)修改pages/spit/index.vue 链接到此页面

<router‐link class="sui‐btn btn‐block btn‐share" to="/spit/submit">发吐槽 </router‐link>

2.1.2 提交吐槽
(1)easyMock模拟数据
URL:spit/spit
Method: post

{
"code": 20000,
"flag": true,
"message": "执行成功"}

(2)修改api/spit.js ,增加提交吐槽的方法

save(pojo) {
return request({
url: `/${group_name}/${api_name}`,
method: 'post',
data: pojo
})
}

(2)修改pages/spit/submit.vue 引入API

import spitApi from '@/api/spit'

在methods增加方法

save(){
spitApi.save({ content:this.content } ).then(res=>{
this.$message({
message: res.data.message,
type: (res.data.flag?'success':'error')
})
if(res.data.flag){
this.$router.push('/spit')
}
})
}

$router.push()的作用是路由跳转。
(3)发布按钮调用方法

<button class="sui‐btn btn‐danger btn‐release" @click="save">发布</button>

2.2 吐槽评论

2.2.1 评论弹出框
我们这里的评论弹出框使用elementUI的弹出框来实现
(1)修改pages/spit/_id.vue ,添加弹出框, 弹出框中放置富文本编辑器

<el‐dialog
title="评论"
:visible.sync="dialogVisible"
width="40%" >
<div class="quill‐editor"
:content="content"
@change="onEditorChange($event)"
v‐quill:myQuillEditor="editorOption">
</div> <
span slot="footer" class="dialog‐footer">
<el‐button @click="dialogVisible = false">取 消</el‐button>
<el‐button type="primary" @click="dialogVisible = false">确 定
</el‐button>
</span>
</el‐dialog>

为富文本编辑框添加样式:

<style>
.quill‐editor {min‐height: 200px; max‐height: 400px; overflow‐y: auto;}
</style>

(2)修改pages/spit/index.vue代码部分

data(){return {
dialogVisible: false,
content: '',
editorOption: {// some quill options
modules: {
toolbar: [
[{ 'size': ['small', false, 'large'] }],
['bold', 'italic'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
['link', 'image']
]
}
}
}
},
methods:{
onEditorChange({ editor, html, text }) {
console.log('editor change!', editor, html, text)
this.content = html
}
}

(3)修改pages/spit/_id.vue 中的回复链接

在这里插入代码片
<a @click="dialogVisible=true;content=''"><i class="fa fa‐commenting" aria‐hidden="true"></i> {{pojo.comment}}</a>

2.2.2 提交评论
修改pages/spit/_id.vue ,增加提交回复的方法

save(){
spitApi.save({ content:this.content,parentid: this.pojo.id } ).then(res=>{ this.$message({
message: res.data.message,
type: (res.data.flag?'success':'error')
})
if(res.data.flag){
this.dialogVisible=false
spitApi.commentlist(this.pojo.id).then(res=>{ this.commentlist=res.data.data })} }) }

编辑提交按钮

<el‐button type="primary" @click="save">提交</el‐button>

3 问答频道

3.1 嵌套布局与标签导航

3.1.1 嵌套布局
(1)创建pages/qa.vue

<template>
<div>
<div class="tab‐nav ">
<div class="wrapper"><ul class="fl sui‐nav nav‐tabs navbar‐dark"> <li class="active"><a href="#index" data‐toggle="tab">首页</a> </li> <li><a href="#php" data‐toggle="tab">Php</a></li><li><a href="#js" data‐toggle="tab">Javascript </a></li> <li><a href="#python" data‐toggle="tab">Python</a></li> <li><a href="#java" data‐toggle="tab">Java</a></li> </ul> <span class="fr more"><a href="./qa‐allTag.html" target="_blank">更多</a></span> <div class="clearfix"></div> </div> </div> <nuxt‐child/> </div></template> <script> import '~/assets/css/page‐sj‐qa‐logined.css' export default { }</script>

(2)创建pages/qa/label/_label.vue

<template>
<div class="wrapper qa‐content">
<div class="fl left‐list">
<div class="tab‐content">
<div id="index" class="tab‐pane active">
<div class="tab‐bottom‐line">
<ul class="sui‐nav nav‐tabs">
<li class="active"><a href="#new" data‐toggle="tab">最新回答</a> </li>
<li><a href="#hot" data‐toggle="tab">热门回答</a></li>
<li><a href="#wait" data‐toggle="tab">等待回答</a></li>
</ul>
<div class="qa‐list">
<div class="tab‐content">
<div id="new" class="tab‐pane active">
<ul class="detail‐list">
<li class="qa‐item">
<div class="fl record">
<div class="number">
<div class="border useful">
<p class="usenum">12</p>
<p>有用</p>
</div>
<div class="border answer">
<p class="ansnum">9</p>
<p>回答</p>
</div>
</div>
</div>
<div class="fl info">
<div class="question">
<p class="author"><span class="name">luckness</span> <span>3</span>分钟前回答</p>
<p class="title"><a href="./qa‐detail.html" target="_blank">有关PHP初级进阶的问题?</a></p>
</div> <div class="other">
<ul class="fl sui‐tag">
<li>Php</li>
<li>Javascript</li>
</ul>
<div class="fr brower">
<p>浏览量 50 | 2017‐07‐05 15:09 来自 <a href="#">毕鹏 </a> </p>
</div>
</div>
</div>
<div class="clearfix"></div> </li>
<li class="qa‐item">
<div class="fl record">
<div class="number">
<div class="border useful">
<p class="usenum">12</p>
<p>有用</p>
</div>
<div class="border answer">
<p class="ansnum">9</p>
<p>回答</p>
</div>
</div>
</div>
<div class="fl info">
<div class="question">
<p class="author"><span class="name">牛奶糖</span> <span>3</span>分钟前回答</p> <p class="title"><a href="./qa‐detail.html" target="_blank">springMVC的controller接收json数据失败</a></p>
</div>
<div class="other">
<ul class="fl sui‐tag">
<li>Php</li>
<li>Javascript</li>
</ul>
<div class="fr brower">
<p>浏览量 50 | 2017‐07‐05 15:09 来自 <a href="#">毕鹏 </a> </p>
</div>
</div>
</div>
<div class="clearfix"></div> </li>
<li class="qa‐item">
<div class="fl record">
<div class="number">
<div class="border useful">
<p class="usenum">12</p>
<p>有用</p>
</div>
<div class="border answer">
<p class="ansnum">9</p>
<p>回答</p>
</div>
</div>
</div>
<div class="fl info">
<div class="question">
<p class="author"><span class="name">大白兔</span> <span>3</span>分钟前回答</p> <p class="title"><a href="./qa‐detail.html" target="_blank">监听器中timer查询数据库</a></p>
</div>
<div class="other">
<ul class="fl sui‐tag">
<li>Php</li>
<li>Javascript</li>
</ul>
<div class="fr brower">
<p>浏览量 50 | 2017‐07‐05 15:09 来自 <a href="#">毕鹏 </a> </p>
</div>
</div>
</div>
<div class="clearfix"></div> </li>
<li class="qa‐item">
<div class="fl record">
<div class="number">
<div class="border useful">
<p class="usenum">34</p>
<p>有用</p>
</div>
<div class="border answer">
<p class="ansnum">9</p>
<p>回答</p>
</div>
</div>
</div>
<div class="fl info">
<div class="question">
<p class="author"><span class="name">luckness</span> <span>3</span>分钟前回答</p>
<p class="title"><a href="./qa‐detail.html" target="_blank">服务器上安装了一个考试系统ASP.NET,安装完成后访问不了,求助</a> </p>
</div>
<div class="other">
<ul class="fl sui‐tag">
<li>Php</li>
<li>Javascript</li>
</ul>
<div class="fr brower">
<p>浏览量 50 | 2017‐07‐05 15:09 来自 <a href="#">毕鹏 </a> </p>
</div>
</div>
</div>
<div class="clearfix"></div> </li>
<li class="qa‐item">
<div class="fl record">
<div class="number">
<div class="border useful">
<p class="usenum">12</p>
<p>有用</p>
</div>
<div class="border answer">
<p class="ansnum">9</p>
<p>回答</p>
</div>
</div>
</div>
<div class="fl info">
<div class="question">
<p class="author"><span class="name">牛奶糖</span> <span>3</span>分钟前回答</p> <p class="title"><a href="./qa‐detail.html"target="_blank">springMVC的controller接收json数据失败</a></p>
</div>
<div class="other">
<ul class="fl sui‐tag"> <li>Php</li>
<li>Javascript</li>
</ul>
<div class="fr brower">
<p>浏览量 50 | 2017‐07‐05 15:09 来自 <a href="#">毕鹏 </a> </p>
</div>
</div>
</div>
<div class="clearfix"></div> </li>
<li class="qa‐item">
<div class="fl record">
<div class="number">
<div class="border useful">
<p class="usenum">12</p>
<p>有用</p>
</div>
<div class="border answer">
<p class="ansnum">9</p>
<p>回答</p> </div>
</div>
</div>
<div class="fl info">
<div class="question">
<p class="author"><span class="name">大白兔</span> <span>3</span>分钟前回答</p> <p class="title"><a href="./qa‐detail.html" target="_blank">监听器中timer查询数据库</a></p>
</div>
<div class="other">
<ul class="fl sui‐tag">
<li>Php</li>
<li>Javascript</li>
</ul>
<div class="fr brower">
<p>浏览量 50 | 2017‐07‐05 15:09 来自 <a href="#">毕鹏 </a></p>
</div>
</div>
</div>
<div class="clearfix"></div> </li>
</ul>
</div>
<div id="hot" class="tab‐pane">
<p>热门回答</p>
</div>
<div id="wait" class="tab‐pane">
<p>等待回答</p>
</div>
</div>
</div>
</div>
</div>
<div id="php" class="tab‐pane">
php
</div>
<div id="js" class="tab‐pane">
Javascript
</div>
<div id="python" class="tab‐pane">python
</div>
<div id="java" class="tab‐pane">
java
</div>
</div>
</div>
<div class="fl right‐tag">
<div class="block‐btn">
<p>今天,有什么好东西要和大家分享么?</p>
<a class="sui‐btn btn‐block btn‐share" href="./qa‐submit.html" target="_blank">发布问题</a>
</div>
<div class="hot‐tags">
<div class="head">
<h3 class="title">热门标签</h3>
</div>
<div class="tags">
<ul class="sui‐tag">
<li>Php</li>
<li>Javascript</li>
<li>Gif</li>
<li>Java</li>
<li>C#</li>
<li>iOS</li>
<li>C++</li>
</ul>
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
</template>

(3)创建pages/qa/index.vue

<template>
<div></div>
</template>
<script>
export default {
created(){ this.$router.push('/qa/label/0') } }</script>

3.1.2 标签导航
(1)easyMock模拟数据
URL: base/label/toplist
Method: GET

{
"flag": true,
"code": 20000,
"data": [{
"id": "1", "labelname": "JAVA" },{ "id": "2", "labelname": "PHP" },{ "id": "3", "labelname": "前端" },{ "id": "4", "labelname": "Python" }
]
}

(2)编写标签API 创建api/label.js

import request from '@/utils/request' import {getUser} from '@/utils/auth' const api_group = 'base'
const api_name = 'label'
export default {
toplist() {
return request({url:`/${api_group}/${api_name}/toplist`,
method: 'get'
})
}
}

(3)修改pages/qa.vue

<template>
<div>
<div class="tab‐nav ">
<div class="wrapper">
<ul class="fl sui‐nav nav‐tabs navbar‐dark">
<router‐link tag="li" to="/qa" active‐class="active" exact > <a> 首页</a></router‐link>
<router‐link tag="li" :to="'/qa/label/'+item.id" active‐ class="active" v‐for="(item,index) in labelList" :key="index" >
<a>{{item.labelname}} </a>
</router‐link>
</ul>
<span class="fr more"><a href="./qa‐allTag.html" target="_blank">更多</a></span>
<div class="clearfix">
</div>
</div>
</div>
<nuxt‐child/>
</div>
</template>
<script> import labelApi from '@/api/label'
export default {
asyncData ({ params, error}) {
return labelApi.toplist().then((res) => { return {labelList: res.data.data }
})
}
}
</script>

(4)创建pages/qa/index.vue

<template>
<div>
这里是问答列表
</div>
</template>

3.2 问答列表

3.2.1 最新问答列表
(1)easy-mock模拟数据 URL:/qa/problem/newlist/{label}/{page}/{size} Method:GET

{
"code": "@integer(60, 100)",
"flag": "@boolean",
"message": "@string",
"data": {
"total": "@integer(60, 100)", "rows|10": [{
"id": "@integer(1, 1000)",
"title": "@cword(20,30)",
"content": "@string",
"createtime": "@datetime", "updatetime": "@datetime",
"userid": "@integer(1, 1000)", "nickname": "小马",
"visits": "@integer(60, 100)", "thumbup": "@integer(60, 100)", "reply": "@integer(60, 100)",
"solve": "@string",
"replyname": "小牛",
"replytime": "@datetime"
}]
}
}

(2)API编写 创建api/problem.js

import request from '@/utils/request' const group_name = 'qa'
const api_name = 'problem'
export default { list(type,label,page,size){return request({ url:`/${group_name}/${api_name}/${type}/${label}/${page}/${size}`, method: 'get'
})
}
}

(3)修改pages/qa/label/_label.vue 脚本部分

import problemApi from '@/api/problem' import axios from 'axios'
export default {
asyncData({params}){
return axios.all([problemApi.list('newlist',params.label,1,10), problemApi.list('hotlist',params.label,1,10), problemApi.list('waitlist',params.label,1,10) ] ).then( axios.spread(function(newlist,hotlist,waitlist ){ return { newlist:newlist.data.data.rows, hotlist:hotlist.data.data.rows, waitlist:waitlist.data.data.rows } })) } }

(4)修改pages/qa/label/_label.vue 模板部分

<ul class="detail‐list">
<li class="qa‐item" v‐for="(item,index) in newlist" :key="index"><div class="fl record">
<div class="number">
<div class="border useful">
<p class="usenum">{{item.thumbup}}</p> <p>有用</p>
</div><div class="border answer"> <p class="ansnum">{{item.reply}}</p> <p>回答</p> </div> </div> </div> <div class="fl info"> <div class="question"> <p class="author"><span class="name">{{item.replyname}} </span><span>{{item.replytime}}</span>回答</p> <p class="title"><a href="./qa‐detail.html" target="_blank">{{item.title}}</a></p> </div> <div class="other"> <div class="fr brower"> <p>浏览量 {{item.visits}} | {{item.createtime}} 来自 <a href="#">{{item.nickname}} </a></p> </div> </div> </div> <div class="clearfix"></div> </li> </ul>

3.2.2 热门回答和等待回答列表
(1)定义属性type ,默认值为new

data(){ return { type:'new' } }

(2)修改选项卡

<ul class="sui‐nav nav‐tabs">
<li :class="type=='new'?'active':''"><a @click="type='new'">最新回 答</a></li>
<li :class="type=='hot'?'active':''"><a @click="type='hot'">热门回 答</a></li>
<li :class="type=='wait'?'active':''"><a @click="type='wait'">等待 回答</a></li> </ul>

(3)修改div的样式为动态获取
最新回答列表

<div id="new" :class="'tab‐pane '+(type=='new'?'active':'')"> ..... </div>

热门回答列表

<div id="hot" :class="'tab‐pane '+(type=='hot'?'active':'')">

等待回答列表

<div id="wait" :class="'tab‐pane '+(type=='wait'?'active':'')">

(4)参照最新问答列表编写热门回答列表与等待回答列表内容

<li class="qa‐item" v‐for="(item,index) in hotlist" :key="index"> .....
<li class="qa‐item" v‐for="(item,index) in waitlist" :key="index">

3.2.3 问答列表瀑布流
(1)修改pages/qa/label/_label.vue模板部分

<div class="qa‐list" v‐infinite‐scroll="loadMore"> ....

(2)修改pages/qa/label/_label.vue脚本部分

import problemApi from '@/api/problem' import axios from 'axios'
export default {
asyncData({params}){return axios.all([problemApi.list('newlist',params.label,1,10), problemApi.list('hotlist',params.label,1,10), problemApi.list('waitlist',params.label,1,10) ] ).then( axios.spread(function(newlist,hotlist,waitlist ){ return { newlist:newlist.data.data.rows, hotlist:hotlist.data.data.rows, waitlist:waitlist.data.data.rows, label:params.label //标签ID,我们需要记录下来 }}))
},
data(){
return {
type:'new',
page_new: 1,//记录最新问题列表的页码 page_hot: 1,//记录热门问题列表的页码 page_wait: 1//记录等待回答列表的页码 } }
,methods:{
loadMore(){
if(this.type==='new'){
this.page_new++ problemApi.list('newlist',this.label,this.page_new,10).then( res=>{ this.newlist=this.newlist.concat( res.data.data.rows )
})
}if(this.type==='hot'){ this.page_hot++ problemApi.list('hotlist',this.label,this.page_hot,10).then( res=>{ this.hotlist=this.hotlist.concat( res.data.data.rows )
})
}
if(this.type==='wait'){ this.page_wait++ problemApi.list('waitlist',this.label,this.page_wait,10).then( res=>{ this.waitlist=this.waitlist.concat( res.data.data.rows )
})
}
}
}
}

3.3 问答详细页

学员实现

3.4 发布问题页

学员实现。使用富文本编辑器(参见吐槽模块的实现)

3.5 标签列表与关注标签

4 图片上传

4.1 Data URL

Data URL给了我们一种很巧妙的将图片“嵌入”到HTML中的方法。跟传统的用 img 标记将 服务器上的图片引用到页面中的方式不一样,在Data URL协议中,图片被转换成base64 编码的字符串形式,并存储在URL中,冠以mime-type。
传统方式:

<img src="data:images/myimg.gif ">

这种方式中, img 标记的 src 属性指定了一个远程服务器上的资源。当网页加载到浏览 器中 时,浏览器会针对每个外部资源都向服务器发送一次拉取资源请求,占用网络资 源。大多数的浏览器都有一个并发请求数不能超过4个的限制。这意味着,如果一个 网页 里嵌入了过多的外部资源,这些请求会导致整个页面的加载延迟。而使用Data URL技 术,图片数据以base64字符串格式嵌入到了页面中,与HTML成为一体,它的形式如下

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAM8AAAA4CAYAAABaFqz+AA AAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9 iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5U Y3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9I kFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNy AgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzA yLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1s bnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0c DovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG 9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWR vYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6 OUQyRTkxNThFRDEyMTFFNjhENUY5QjM0OUZGQzEwN0IiIHhtcE1NOkRvY3VtZW50SUQ9Inhtc C5kaWQ6OUQyRTkxNTlFRDEyMTFFNjhENUY5QjM0OUZGQzEwN0IiPiA8eG1wTU06RGVyaXZlZE Zyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5RDJFOTE1NkVEMTIxMUU2OEQ1RjlCMzQ 5RkZDMTA3QiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5RDJFOTE1N0VEMTIxMUU2OEQ1 RjlCMzQ5RkZDMTA3QiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wb WV0YT4gPD94cGFja2V0IGVuZD0iciI/Po4slv0AABviSURBVHja7F0JfBRF1u/puXJxCqIiiI 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 ozl3Z9A5AAAAAElFTkSuQmCC">

vue-quill-editor的图片上传默认采用Data URL方式。

4.2 辅助插件vue-quill-editor-upload

如果你不想使用Data URL方式存储图片,我们可以通过一个辅助插件vue-quill-editor- upload 来让vue-quill-editor实现传统方式的上传。 (1)安装:

cnpm install vue‐quill‐editor‐upload ‐‐save

(2)修改submit.vue 引入插件

import {quillRedefine} from 'vue‐quill‐editor‐upload'

(3)将editorOption的值改为{}

data () {
return {
content: '',
editorOption:{}//修改此处!
}
}

(4)新增created 钩子函数

created () {
this.editorOption = quillRedefine( { // 图片上传的设置
uploadConfig: {
action:'http://localhost:3000/upload', // 必填参数 图片上传地 址
// 必选参数 res是一个函数,函数接收的response为上传成功时服务器返 回的数据// 你必须把返回的数据中所包含的图片地址 return 回去 res: (respnse) => { return respnse.info },name: 'img' // 图片上传参数名} } )}

4.3 Multer(了解)

课程中提供了上传图片的服务端代码 upload-server ,我们可以先测试运行后,对照在 线文档阅读并理解代码(课程不要求学员独立编写此段代码)

cnpm install
npm run start

这段代码主要应用两项技术:
(1)Express --node.js的web框架 在线文档: http://www.expressjs.com.cn/4x/api.html
(2)Multer --Express官方推出的,用于multipart/form-data请求数据处理的中间件 在线文档: https://github.com/expressjs/multer/blob/master/doc/README-zh- cn.md

4.4 云存储解决方案-阿里云OSS

为了能够解决海量数据存储与弹性扩容,我们在十次方项目中采用云存储的解决方案- 阿 里云OSS。
4.4.1 准备工作
(1)申请阿里云账号并完成实名认证: 可以使用我们之前发短信用的阿里云账号。
(2)开通OSS: 登录阿里云官网。将鼠标移至产品找到并单击对象存储OSS打开OSS产 品详情页面。在OSS产品详情页中的单击立即开通。开通服务后,在OSS产品详情页面单 击管理控制台直接进入OSS管理控制台界面。您也可以单击位于官网首页右上方菜单栏的 控制台,进入阿里云管理控制台首页,然后单击左侧的对象存储OSS菜单进入OSS管理控 制台界面。


(3)创建存储空间
新建Bucket,命名为tensquare ,读写权限为公共读

4.4.2 代码编写
(1)安装ali-oss

cnpm install ali‐oss ‐‐save
cnpm install co ‐‐save

(2)修改file-upload-demo-master的server.js

var co = require('co');
var OSS = require('ali‐oss');
var client = new OSS({
accessKeyId: 'LTAIWaEERTRWSD2',
accessKeySecret: 'PznrHXxYvTcADAFFDDDJnoAokJ0NSWEWF',
endpoint: 'oss‐cn‐beijing.aliyuncs.com',
bucket: 'tensquare'
});
app.post('/upload', upload.single('img'), (req, res) =>
{
// 没有附带文件
if (!req.file) {
res.json({ ok: false });
return;
}
co(function* () {
var stream = fs.createReadStream(req.file.path);
var result = yield client.putStream(req.file.originalname, stream); console.log("result:"+result);
res.json({ ok: true , info: result.url})
}).catch(function (err) {
console.log(err);
});
});

co :已同步的方式调用异步的代码 配合yield关键字使用,将异步结果直接返回。

第七天 黑马十次方 吐槽列表与详细页、发吐槽与评论功能、问答频道功能、掌握DataURL和阿里云OSS相关推荐

  1. 解决七牛云链接失效以及批量下载图片并迁移阿里云 OSS

    起因 七牛云对测试域名进行回收,而我博文中图片用的链接仍然是测试域名的链接,因此导致部分链接失效,浏览器返回如下错误: {"error":"no such domain& ...

  2. 七牛云和阿里云OSS存储图片服务器使用

    目录 1 图片存储方案 1.1 介绍 1.2 七牛云存储 1.2.1 注册.登录 1.2.2 新建存储空间 1.2.3 查看存储空间信息 1.2.4 开发者中心 1.2.5 鉴权 1.2.6 Java ...

  3. Fusion Upload组件 对接 阿里云OSS/七牛/又拍

    BeforeUpload 方法 Upload组件的BeforeUpload方法,是一个十分强大的方法,可以在上传请求发起之前做一系列的操作行为.从而达到改变请求参数的目的. beforeUpload ...

  4. 用多备份将网站数据备份到百度云,七牛云存储,阿里云OSS,亚马逊S3,金山云等云存储服务上

    真正运营过网站的人都知道,数据对一个网站来说至关重要,因此,网站数据备份也是日常必做工作.因为误操作,网站被攻击等种种原因都会导致数据丢失,这时,你才会明白"有备无患"的道理.备份 ...

  5. (转)云存储:阿里云OSS 、又拍云和 七牛 的比较

    阿里OSS:好处就是,那是一套完整的体系,存储,数据库,CDN,服务器,阿里都可以给你全包.缺点,费用对于没有盈利的网站来说太高了,好像定位就是给那些高端客户使用的,而且CDN,OSS的流量是分开收费 ...

  6. Springboot文件上传(本地上传、阿里云oss、腾讯云、华为云、Minio上传、七牛云)

    文件上传: 本地上传.阿里云oss.腾讯云.华为云.Minio上传.七牛云. <!-- 阿里云存储 --><dependency><groupId>com.aliy ...

  7. PbootCms阿里云OSS插件和七牛云存储插件

    特别注意事项 URL请填写全地址,如下图是阿里云OSS提供的Bucket域名,请填写完整这个域名. 插件介绍 增加图片上传到七牛云和阿里云OSS,可选择启用. 图片上传.编辑器上传图片均已接入云存储. ...

  8. 阿里云OSS转移至七牛云存储(详细简洁教程)

    阿里云OSS转移至七牛云存储 只需五步,将阿里云OSS的图片转移到七牛云 七牛云官方提供了工具和详细文档,文字比较多也比较详细,下面写一下简洁一点的 第一步:下载数据迁移工具(kodoimport) ...

  9. Django项目,上传图片到七牛云,阿里云OSS

    Django项目,上传图片阿里云OSS教程 上传项目到七牛云:链接 或者到我的主页查看 Django项目上传图片到阿里云OSS简略步骤为: 1.注册登陆阿里云,搜索对象存储oss进行购买,需要实名认证 ...

最新文章

  1. [JLOI2011]飞行路线
  2. SAP PM 初级系列10 - 维护通知单相关的配置
  3. Fiddle抓包Https
  4. Transact_SQL小手册
  5. 屠龙之路_战胜View对DataBase猛烈进攻_ThirdDay
  6. android media_rw sdcard_rw,大约Android 了解权限管理
  7. SqlAlchemy初探
  8. 浮点数可以直接相加么?_鸭粪屎可以直接做有机肥么?
  9. 判断设置两天后时间,时间戳
  10. 得物:两款问题商品系标识标注方式不符合最新规定 已先行下架商品
  11. 今天分享一个做自媒体的方法论
  12. gimp中文版教程_GIMP视频教程集合(中文+英文)下载 | 卧云楼
  13. 查看服务器虚拟机版本,查看虚拟机版本命令
  14. 【python基础】h5py库的基本使用
  15. 【ANSYS命令流】为什么是命令流?
  16. 大智慧炒股软件下载|选股软件
  17. 影响计算机关键性能参数测试研究,基于Labview软件的ADC计算机辅助测试系统设计...
  18. 检测图片篡改困难?快来试试这款黑科技
  19. 如何直观理解AUC评价指标?
  20. Geophysical research letters 进度2

热门文章

  1. input类型为file时,accept为表格xlsl
  2. Hadoop数据本地化
  3. 至快乐的11.11---牛X光棍的呐喊!(转)
  4. 常用Linux性能检测命令
  5. 《起跑吧,Opa》 -- 中译本 第一章 初识Opa
  6. 使用exe4j把jar转换成exe文件时,报错java.lang.NoClassDefFoundError: org/eclipse/swt/widgets/Composite
  7. php字符串函数处理emoji,PHP中处理内容含有emoji表情的几种方式
  8. TLS初探(4)多域名证书
  9. 小白学 Python 爬虫(26):为啥上海二手房你都买不起
  10. Outlook 2013 pst/ost邮件数据文件迁移实现