第7章 网站前台-吐槽与问答
学习目标: **
完成吐槽列表与详细页
完成发吐槽与评论功能,掌握富文本编辑器的使用
完成问答频道功能
掌握DataURL和阿里云OSS**
1 吐槽列表与详细页
1.1 吐槽列表页 【dataurl|第七天 黑马十次方 吐槽列表与详细页、发吐槽与评论功能、问答频道功能、掌握DataURL和阿里云OSS】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
-
发表评论
来个匿名吐槽,发泄一下你心中的怒火吧!
发吐槽
>
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}
})
}
}
1.1.2 吐槽列表瀑布流
修改页面pages/spit/index.vue
修改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 页面链接
{{item.content}}
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
{{pojo.content}}
- class="star"> {{pojo.thumbup}}
- {{pojo.share}}
- {{pojo.comment}}
>评论
-
文章图片
{{item.thumbup}}
来个匿名吐槽,发泄一下你心中的怒火吧!
发吐槽
>
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
}
})
)
}
}
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)点赞调用
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部分
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
发布吐槽
>
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
}
}
}
>
.quill‐editor {
min‐height: 200px;
max‐height: 400px;
overflow‐y: auto;
}
(5)修改pages/spit/index.vue 链接到此页面
发吐槽
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)发布按钮调用方法
2.2 吐槽评论 2.2.1 评论弹出框
我们这里的评论弹出框使用elementUI的弹出框来实现
(1)修改pages/spit/_id.vue ,添加弹出框, 弹出框中放置富文本编辑器
<
span slot="footer" class="dialog‐footer">
取 消
确 定
为富文本编辑框添加样式:
>
.quill‐editor {
min‐height: 200px;
max‐height: 400px;
overflow‐y: auto;
}
(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 中的回复链接
在这里插入代码片
{{pojo.comment}}
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 })
}
})
}
编辑提交按钮
提交
3 问答频道
3.1 嵌套布局与标签导航 3.1.1 嵌套布局
(1)创建pages/qa.vue
class="fr more">
>
import '~/assets/css/page‐sj‐qa‐logined.css'
export default { }
(2)创建pages/qa/label/_label.vue
-
12
有用
9
回答
- Php
- Javascript
浏览量 50 | 2017‐07‐05 15:09 来自 毕鹏
-
12
有用
9
回答
- Php
- Javascript
浏览量 50 | 2017‐07‐05 15:09 来自 毕鹏
-
12
有用
9
回答
- Php
- Javascript
浏览量 50 | 2017‐07‐05 15:09 来自 毕鹏
-
34
有用
9
回答
- Php
- Javascript
浏览量 50 | 2017‐07‐05 15:09 来自 毕鹏
-
12
有用
9
回答
- Php
- Javascript
浏览量 50 | 2017‐07‐05 15:09 来自 毕鹏
-
12
有用
9
回答
- Php
- Javascript
浏览量 50 | 2017‐07‐05 15:09 来自 毕鹏
热门回答
等待回答
php
Javascript
python
java
今天,有什么好东西要和大家分享么?
发布问题
(3)创建pages/qa/index.vue
>
export default {
created(){ this.$router.push('/qa/label/0') } }
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
class="fr more">
> import labelApi from '@/api/label'
export default {
asyncData ({ params, error}) {
return labelApi.toplist().then((res) => { return {labelList: res.data.data }
})
}
}
(4)创建pages/qa/index.vue
这里是问答列表
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 模板部分
-
{{item.thumbup}}
有用
{{item.reply}}
回答
浏览量 {{item.visits}} | {{item.createtime}} 来自 {{item.nickname}}
3.2.2 热门回答和等待回答列表
(1)定义属性type ,默认值为new
data(){ return { type:'new' } }
(2)修改选项卡
(3)修改div的样式为动态获取
最新回答列表
.....
热门回答列表
等待回答列表
(4)参照最新问答列表编写热门回答列表与等待回答列表内容
.....
3.2.3 问答列表瀑布流
(1)修改pages/qa/label/_label.vue模板部分
....
(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 属性指定了一个远程服务器上的资源。当网页加载到浏览 器中 时,浏览器会针对每个外部资源都向服务器发送一次拉取资源请求,占用网络资 源。大多数的浏览器都有一个并发请求数不能超过4个的限制。这意味着,如果一个 网页 里嵌入了过多的外部资源,这些请求会导致整个页面的加载延迟。而使用Data URL技 术,图片数据以base64字符串格式嵌入到了页面中,与HTML成为一体,它的形式如下
文章图片
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关键字使用,将异步结果直接返回。
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- IntelliJ|Vue学习笔记(狂神)
- vue.js|狂神说 Vue 笔记
- uniapp|uniapp显示日期格式化
- vue.js|vue数据push后不能响应式更新
- 技术栈|swagger 2速成
- Animate.css动画库使用
- vue项目img src=https://www.it610.com/article/“[object module]“
- 前端基础和框架使用|Vue的异步渲染axios
- 前端优秀开源项目|Vuestic UI - 免费开源的高质量 Vue3 UI 组件库,还内置了漂亮的 Vuestic Admin 后台框架
文章图片
匿名评论: 今天入职腾讯,产品岗,普通非985211大学,上家是不到五十人创业小 公司!16年毕业!找内推腾讯给的面试机会,五轮面试!可能是我把运气攒一起了!