手拉手带你用 vite2 + vue3 + elementPlus 做个博客尝尝鲜
这里主要是体验一下 vite2 + vue3 做项目是什么样子的,重点在于 vite2 + vue3 方面的介绍,博客只是一个例子,所以博客的功能比较弱,只实现了几个最基本的功能。
vite2
vite就一个字 —— 快,体验了一下确实是快,一开始还以为不支持第三方插件呢,后来试了一下,好多都是支持的。
环境
前端
vite2 构建项目。
yarn create @vitejs/app
vue3 框架
vue-router 路由。
yarn add vue-router@next
element-plus UI库。
yarn add element-plus
v-md-editor 用于编写博文。
yarn add @kangc/v-md-editor@next
安装的时候要带上@next,否则会报错。vscode 编写代码。
建议使用 yarn,因为可以更快速。快+快才是合理搭配。
后端
用webSQL模拟一下MySQL,重点在前端,所以后端就简略一下,用前端存储代替一下。
博客设计
先做个简单的个人博客,因为是个人版,所以可以省略注册、登录这些功能,表结构也可以简单一点。基础功能:添加博文、显示博文、博文列表+查询+分页、讨论列表和添加讨论。虽然功能弱了一点,但是麻雀虽小五脏俱全,vite2 和 vue3 的基础用法也可以体现一些。
1
功能设计
2
代码设计
3
model设计
model代码
就近原则,还是先把贴出来吧,后面代码比较多,不好找。/src/model/blogModel.js
/**
* 显示博文用,建表用
* @returns 博文的全部的属性
*/
export const blog = () => {
return {
// id: 0,
title: '', // 这是一个博客标题
groupId: 0, // 分组ID
addTime: new Date(), // 添加时间
introduction: '', // 这是博客的简介
concent: '', // 这是博客的详细内容
state: 1, // 1:草稿;2:发布;3:删除
viewCount: 0, // 浏览量
agreeCount: 0, // 点赞数量
discussCount: 0 // 讨论数量
}
}
/**
* 表单用的博文,绑定表单用。
* * title:文章标题
* @returns 添加博文需要的属性
*/
export const blogForm = () => {
return {
// id: new Date().valueOf(),
title: '', // 这是一个博客标题
addTime: new Date(), // 添加时间
introduction: '', // 这是博客的简介
concent: '', // 这是博客的详细内容
state: 1 // 1:草稿;2:发布;3:删除
}
}
/**
* 首页用的博文列表,按需设置字段
* @returns 博文列表
*/
export const blogList = () => {
return {
id: 0,
title: '', // 这是一个博客标题
addTime: '', // 添加时间
introduction: '', // 这是博客的简介
viewCount: 0, // 浏览量
agreeCount: 0, // 点赞数量
discussCount: 0 // 讨论数量
}
}
/**
* 编辑博文用的列表
* @returns 文章标题列表
*/
export const articleList = () => {
return {
id: 0,
title: '', // 这是一个博客标题
addTime: '', // 添加时间
viewCount: 0, // 浏览量
agreeCount: 0, // 点赞数量
discussCount: 0 // 讨论数量
}
}
/**
* 建表用的讨论
* @returns 讨论表
*/
export const discuss = () => {
return {
// id: 0,
blogId: 0,
discusser: '' , // 昵称
addTime: new Date(), // 时间
concent: '', // 内容
agreeCount: 0
}
}
/**
* @returns 讨论的model
*/
export const discussList = () => {
return {
id: 0,
discusser: '' , // 昵称
addTime: '', // 时间
concent: '', // 内容
agreeCount: 0
}
}
4
状态设计
状态嘛,简单地说就是多个组件共享的数据,当然这么说不够严谨。
/src/model/blogState.js
import { inject } from "vue"
export const blogState = {
currentGroupId: 0, // 选择的分组ID。0:没选择
currentArticleId: 0, // 选择的文章ID。
editArticleId: 0, // 当前修改的文章ID
findQuery: { }, // 查询条件
page: { // 分页参数
pageTotal: 100,
pageSize: 2,
pageIndex: 1,
orderBy: { id: false }
},
isReloadDiussList: false
}
/**
* 状态的管理
* * 获取状态
* * 设置当前选择的分组
* * 设置当前选择的文章
* * 设置当前编辑的文章
*/
export default function blogStateManage() {
// 先把状态取出来,否则在 function 里面无法读取。
const state = inject('blogState')
// 子组件里面获取状态
const getBlogState = (id) => {
return state
}
// 设置当前选择的分组
const setCurrentGroupId = (id) => {
state.currentGroupId = id
}
// 设置当前编辑的文章
const setEditArticleId = (id) => {
state.editArticleId = id
}
// 设置更新讨论列表
const setReloadDiussList = () => {
state.isReloadDiussList = !state.isReloadDiussList
}
return {
setReloadDiussList, // 设置更新讨论列表
getBlogState, // 获取状态
setEditArticleId, // 设置当前编辑的文章
setCurrentGroupId // 设置当前选择的分组
}
}
算是一个简易的状态管理吧,先定义一下需要的状态,在main里面注入状态,然后用 inject 提取状态,再写几个设置状态的函数,基本就可以搞定了。
后续想写成插件的形式,当然会完善一些功能,不会这么单薄。
感觉 Vuex 有点太厚重了,这里也不需要那么强大的功能,所以就自己实现了一个简单的。
管理类
import webSQLHelp from '../store/websql-help'
import { blog, blogForm, blogList, articleList, discuss, discussList } from './blogModel'
import blogStateManage from '../model/blogState'
// 连接数据库
const help = new webSQLHelp('vite2-blog', 1.0, '测试用的博客数据库')
// =====================数据库==============================
/**
* 建立 vite2-blog 数据库,blog表、discuss表
* @returns 建立数据库和表
*/
export const databaseInit = () => {
help.createTable('blog', blog())
help.createTable('discuss', discuss())
}
/**
* 删除:blog表、discuss表
* @returns 删除表
*/
export const deleteBlogTable = () => {
help.deleteTable('blog')
help.deleteTable('discuss')
}
/**
* 博客的管理类
* @returns 添加、修改、获得列表等
*/
export const blogManage = () => {
// =====================博文==============================
/**
* 添加新的博文
* @param { object } blog 博文 的 model
* @return {*} promise,新博文的ID
*/
const addNewBlog = (blog) => {
return new Promise((resolve, reject) => {
const newBlog = {}
Object.assign(newBlog, blog, {
addTime: new Date(), // 添加时间
viewCount: 0, // 浏览量
agreeCount: 0, // 点赞数量
discussCount: 0 // 讨论数量
})
help.insert('blog', newBlog).then((id) => {
resolve(id)
})
})
}
/**
* 修改博文
* @param { object } blog 博文 的 model
* @return {*} promise,修改状态
*/
const updateBlog = (blog) => {
return new Promise((resolve, reject) => {
help.update('blog', blog, blog.ID).then((state) => {
resolve(state)
})
})
}
/**
* 根据博文ID获取博文,编辑博文、显示博文用
* @param { number } id 博文ID
* @returns
*/
const getArtcileById = (id) => {
return new Promise((resolve, reject) => {
help.getDataById('blog', id).then((data) => {
if (data.length > 0) {
resolve(data[0])
} else {
console.log('没有找到记录', data)
resolve({})
}
})
})
}
/**
* 依据分组ID获取博文列表,编辑博文列表用。
* @param {number} groupId 分组ID
* @returns
*/
const getBlogListByGroupId = (groupId) => {
return new Promise((resolve, reject) => {
help.select('blog', articleList(), {groupId: [401, groupId]})
.then((data) => {
resolve(data)
})
})
}
// 状态管理
const { getBlogState } = blogStateManage()
const blogState = getBlogState()
/**
* 依据状态,分页查询博文
* @returns 博文列表
*/
const getBlogList = () => {
// 根据状态设置查询条件和分页条件
const _query = blogState.findQuery || {}
_query.state = [401, 2] // 显示发布的博文,设置固定查询条件
return new Promise((resolve, reject) => {
help.select('blog', blogList(), _query, blogState.page).then((data) => {
resolve(data)
})
})
}
const getBlogCount = () => {
// 根据状态设置查询条件和分页条件
const _query = blogState.findQuery || {}
_query.state = [401, 2] // 显示发布的博文,设置固定查询条件
return new Promise((resolve, reject) => {
help.getCountByWhere('blog', _query).then((count) => {
resolve(count)
})
})
}
// =====================讨论==============================
/**
* 添加一个新讨论
* @param {object}} discuss 讨论的model
* @returns
*/
const addDiuss = (discuss) => {
return new Promise((resolve, reject) => {
const newDiscuss = {}
Object.assign(newDiscuss, discuss, {
addTime: new Date(), // 添加时间
agreeCount: 0 // 点赞数量
})
help.insert('discuss', newDiscuss).then((id) => {
resolve(id)
})
})
}
/**
* 依据博文ID获取讨论列表。
* @param {number} blogId 分组ID
* @returns
*/
const getDiscussListByBlogId = (blogId) => {
return new Promise((resolve, reject) => {
help.select('discuss', discussList(), {blogId: [401, blogId]})
.then((data) => {
resolve(data)
})
})
}
return {
addDiuss, // 添加新讨论
getDiscussListByBlogId, // 依据博文ID获取讨论列表。
addNewBlog, // 添加 新博文
updateBlog, // 修改博文
getArtcileById, // 根据博文ID获取博文
getBlogListByGroupId, // 获取博文列表
getBlogList, // 获取博文列表
getBlogCount // 统计数量
}
}
其实应该分成两个类,一个是博文的管理类,一个是讨论的管理类,以后还可以有分组的管理类。现在因为讨论相关的只有两个函数,所以就没有分开。
把需要的功能集中起来,便于管理和复用,减少组件里面的代码,也便于代码的升级更换。比如现在是把数据保存在前端的webSQL里面,那么以后要提交到后端怎么办?只需要在这里改代码即可,不需要修改xxx.vue里面的代码。 把变化限制在最小的范围内。
编码
设计好了之后可以动手编码了,先看一下文件结构:
文件结构
个人感觉还是比较清晰的。
config设置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: '/vue3-blog/', // 修改发布网站的目录
build: {
outDir: 'blog' // 修改打包的默认文件夹
}
})
base,设置发布网站的目录。
发布的时候默认项目会部署在网站根目录,如果不是根目录的话,可以使用 base 来更改。build.outDir 修改默认(dist)的构建输出路径。
其他设置方式可以看这里:https://cn.vitejs.dev/config/,内容非常多。
路由设置
src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/home.vue'
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/write',
name: 'write',
component: () => import('../views/write.vue')
},
{
path: '/blogs/:id',
name: 'blogs',
props: true,
component: () => import('../views/blog.vue')
},
{
path: '/groups/:groupId',
name: 'groups',
props: true,
component: Home
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
除了 createWebHistory 的参数要去掉之外,没啥变化。路由设置也很简单,只有首页、编写博文、博文详细、分组显示博文这四项。
网页入口
/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vite2 + vue3 做的简单的个人博客</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
非常简洁,我们可以设置一个标题,用 type="module" 的方式加载入口js文件。其他的可以按照需要自行设置。
代码入口
/src/main.js
import { createApp, provide, reactive } from 'vue'
import App from './App.vue'
import router from './router' // 路由
// UI库
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import 'dayjs/locale/zh-cn'
import locale from 'element-plus/lib/locale/lang/zh-cn'
// Markdown 编辑插件
import VueMarkdownEditor from '@kangc/v-md-editor'
import '@kangc/v-md-editor/lib/style/base-editor.css'
import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js'
import '@kangc/v-md-editor/lib/theme/style/vuepress.css'
VueMarkdownEditor.use(vuepressTheme)
// markdown 显示插件
import VMdPreview from '@kangc/v-md-editor/lib/preview'
import '@kangc/v-md-editor/lib/style/preview.css'
// 引入你所使用的主题 此处以 github 主题为例
// import githubTheme from '@kangc/v-md-editor/lib/theme/github'
VMdPreview.use(vuepressTheme)
// 建立数据库
import { databaseInit, deleteBlogTable } from './model/blogManage'
// deleteBlogTable()
databaseInit()
// 注入状态
import { blogState } from './model/blogState'
const state = reactive(blogState)
createApp(App)
.provide('blogState', state) // 注入状态
.use(router) // 路由
.use(ElementPlus, { locale, size: 'small' }) // UI库
.use(VueMarkdownEditor) // markDown编辑器
.use(VMdPreview) // markDown 显示
.mount('#app')
这里的代码稍微有点长,除了常规操作外,还使用了 MarkdownEditor 用于编辑博文,这个部分代码有点多。
然后又加入了设计webSQL数据库的代码,以及自己用 provide 实现的简易的状态管理。
首页、博文列表
模板部分:
<template>
<!--博文列表-->
<el-row :gutter="12">
<el-col :span="5">
<!--分组-->
<blogGroup :isDetail="true"/>
</el-col>
<el-col :span="18">
<el-card shadow="hover"
v-for="(item, index) in blogList"
:key="'bloglist_' + index"
>
<template #header>
<div class="card-header">
<router-link :to="{name:'blogs', params:{id:item.ID}}">
{{item.title}}
</router-link>
<span class="button">({{dateFormat(item.addTime).format('YYYY-MM-DD')}})</span>
</div>
</template>
<!--简介-->
<div class="text item" v-html="item.introduction"></div>
<hr>
<i class="el-icon-view"></i> {{item.viewCount}}
<i class="el-icon-circle-check"></i> {{item.agreeCount}}
<i class="el-icon-chat-dot-square"></i> {{item.discussCount}}
</el-card>
<!--没有找到数据-->
<el-empty description="没有找到博文呢。" v-if="blogList.length === 0"></el-empty>
<el-pagination
background
layout="prev, pager, next"
v-model:currentPage="blogState.page.pageIndex"
:page-size="blogState.page.pageSize"
:total="blogState.page.pageTotal">
</el-pagination>
</el-col>
</el-row>
</template>
模板部分没啥变化,还是老样子,使用 el-row 做了一个简单的布局:
左面,blogGroup 显示分组的组件。 右面,用 el-card 做了一个列表,用于显示博文。 下面,用 el-pagination 实现分页功能。
代码部分:
<script setup>
import { watch, reactive } from 'vue'
import { useRoute } from 'vue-router'
import blogGroup from '../components/blog-group.vue'
import blogStateManage from '../model/blogState'
import { blogManage } from '../model/blogManage'
// 日期格式化
const dateFormat = dayjs
// 博文管理
const { getBlogList, getBlogCount } = blogManage()
// 状态管理
const { getBlogState } = blogStateManage()
// 博文的状态
const blogState = getBlogState()
// 博文列表
const blogList = reactive([])
【后面就不写这些引入的代码了】
/**
* 按照首页、分组、查询显示博文列表。
* 显示第一页,并且统计总记录数
*/
const showBlog = () => {
// 分组ID
let groupId = blogState.currentGroupId
if (groupId === 0) {
// 首页,清空查询条件,显示第一页
blogState.findQuery = {}
blogState.page.pageIndex = 1
} else {
// 分组的博文列表,设置分组条件,显示第一页
blogState.findQuery = {
groupId: [401, groupId]
}
blogState.page.pageIndex = 1
}
// 统计符合条件的总记录数
getBlogCount().then((count) => {
blogState.page.pageTotal = count
})
// 获取第一页的数据
getBlogList().then((data) => {
blogList.length = 0
blogList.push(...data)
})
}
const route = useRoute()
// 如果是首页,把 当前分组ID设置为 0 ,以便于显示所有分组的博文。
watch(() => route.fullPath, () => {
if (route.fullPath === '/' || route.fullPath === '/blog') {
blogState.currentGroupId = 0
}
})
// 监控选择的分组的ID
watch(() => blogState.currentGroupId, () => {
showBlog()
})
// 监听页号的变化,按照页号显示博文列表
watch(() => blogState.page.pageIndex, () => {
getBlogList().then((data) => {
blogList.length = 0
blogList.push(...data)
})
})
// 默认执行一遍
showBlog()
代码有点长,这说明了啥呢?还有优化的空间。
script setup
vite2 建立的项目,默认推荐的是这种方式,其实 vite2 也是支持 export default { setup (props, ctx) { }} 这种写法的。当然 vue-cli 建立的项目也是支持 script setup 这种方式。所以用哪一种可以看个人喜好。
script setup 更简洁,省去了好多“麻烦”,比如组件引入部分,import 就好,不需要再次注册了。const 后也不用 return 了,模板可以直接读取到。
各种js类
基于这种“散养”方式,所以必须写各种单独的js文件来实现基础功能,然后在 setup 里面整合,否则 setup 就没法看了。watch等
watch、ref、reactive这些的用法没有改变。
看一下效果:
后端出身,不会css,也没有艺术细胞所以比较难看,还望谅解
表单 发布博文
这里借鉴一下“简书”的编辑方式,个人感觉还是很方便的,左面是分组目录,中间的选择的分组的博文列表,右面是编辑博文的区域。
<template>
<el-row :gutter="12">
<el-col :span="4">
<!--分组-->
<blogGroup/>
</el-col>
<el-col :span="5">
<!--标题列表-->
<blogArticle/>
</el-col>
<el-col :span="14">
<!--写博文-->
<el-input
style="width:90%"
:show-word-limit="true"
maxlength="100"
placeholder="请输入博文标题,最多100字"
v-model="blogModel.title"
/>
<el-button type="primary" plain @click="submit"> 发布文章 </el-button>
{{dateFormat(blogModel.addTime).format('YYYY-MM-DD HH:mm:ss')}}
<v-md-editor
:include-level="[1, 2, 3, 4]"
v-model="blogModel.concent" :height="editHeight+'px'"></v-md-editor>1
</el-col>
</el-row>
</template>
blogGroup 博文分组的组件,显示分组列表,便于我们选择分组。
blogArticle 博文列表,选择分组后,显示分组里面的博文列表。在这里可以添加博文,点击博文标题,可以在右面加载博文的表单,进行博文编辑。
用过简书的编辑方式之后,感觉这个还是非常方便的。
代码部分:
【引入的代码略】
// 组件
import blogGroup from '../components/blog-group.vue'
import blogArticle from '../components/blog-article.vue'
// 可见的高度
const editHeight = document.documentElement.clientHeight - 200
// 管理
const { updateBlog, getArtcileById } = blogManage()
// 表单的model
const blogModel = reactive(blogForm())
// 监控编辑文章的ID
watch(() => blogState.editArticleId, (v1, v2) => {
getArtcileById(v1).then((data) => {
Object.assign(blogModel, data)
})
})
// 发布文章
const submit = () => {
blogModel.ID = blogState.editArticleId
blogModel.state = 2 // 改为发布状态
updateBlog(blogModel).then((id) => {
// 通知列表
})
}
watch(() => blogState.editArticleId 监听要编辑的博文ID,然后加载博文数据绑定表单,编辑之后用 submit 发布博文。
这里还需要一个自动保存草稿的功能,以后再完善。
submit 发布博文,其实这里是修改博文,因为添加的工作是在 blogArticle 组件里面实现的。
updateBlog 调用管理类里面的方式实现发布博文的功能。
各个平台的发文方式也体验了一下,还是喜欢这种方式,所以个人博客也采用这种方式来实现编辑博文的功能。
看一下效果:
目录导航:
v-md-editor 提供的目录导航功能,还是非常给力的,看着大纲编写,思路清晰多了。
博文内容 + 讨论
<template>
<el-row :gutter="12">
<el-col :span="5">
<!--分组-->
<blogGroup :isDetail="true"/>
</el-col>
<el-col :span="18">
<!--显示博文-->
<h1>{{blogInfo.title}}</h1>
({{dateFormat(blogInfo.addTime).format('YYYY-MM-DD')}})
<v-md-preview :text="blogInfo.concent"></v-md-preview>
<hr>
<!--讨论列表-->
<discussList :id="id"/>
<!--讨论表单-->
<discussForm :id="id"/>
</el-col>
</el-row>
</template>
【引入的代码略】
// 组件的属性,博文ID
const props = defineProps({
id: String
})
// 管理
const { getArtcileById } = blogManage()
// 表单的model
const blogInfo = reactive({})
getArtcileById(props.id).then((data) => {
Object.assign(blogInfo, data)
})
这个代码就很简单了,因为只实现了基本的发讨论和显示讨论的功能,其他暂略。
看看效果:
好吧,这个讨论做的蛮敷衍的,其实有好多想法,只是篇幅有限,以后再介绍。
组件级别的代码
虽然在vue里面,除了js文件,就是vue文件了,但是我觉得还是应该细分一下。比如上面都是是页面级的代码,下面这些是“组件”级别的代码了。
1
博文分组
多次提到的博文分组。
<template>
<!--分组,分为显示状态和编辑状态-->
<el-card shadow="hover"
v-for="(item, index) in blogGroupList"
:key="'grouplist_' + index"
>
<template #header>
<div class="card-header">
<span>{{item.label}}</span>
<span class="button"></span>
</div>
</template>
<div
class="text item"
style="cursor:pointer"
v-for="(item, index) in item.children"
:key="'group_' + index"
@click="changeGroup(item.value)"
>
{{item.label}}
</div>
</el-card>
</template>
暂时先用 el-card 来实现,后期会改成 NavMenu 来实现。
【引入的代码略】
// 组件的属性
const props = defineProps({
isDetail: Boolean
})
/**
* 博文的分组列表
*/
const blogGroupList = reactive([
{
value: '1000',
label: '前端',
children: [
{ value: '1001', label: 'vue基础知识', },
{ value: '1002', label: 'vue组件', },
{ value: '1003', label: 'vue路由', }
]
},
{ value: '2000', label: '后端',
children: [
{ value: '2001', label: 'MySQL', },
{ value: '2002', label: 'web服务', }
]
}
])
// 选择分组
const { setCurrentGroupId } = blogStateManage()
const router = useRouter()
const changeGroup = (id) => {
setCurrentGroupId(id)
// 判断是不是要跳转
// 首页、编辑页不跳,博文详细页面调整
if (props.isDetail) {
// 跳转到列表页
router.push({ name: 'groups', params: { groupId: id }})
}
}
分组数据暂时写死了,没有做成可以维护的方式,以后再完善。
2
博文列表,编辑用
<template>
<!--添加标题-->
<el-card shadow="hover">
<template #header>
<div class="card-header">
<el-button @click="addNewArticle" >添加新文章</el-button>
<span class="button"></span>
</div>
</template>
<div
class="text item"
style="cursor:pointer"
v-for="(item, index) in blogList"
:key="'article_' + index"
@click="changeArticle(item.ID)"
>
{{item.ID}}:{{item.title}} ({{dateFormat(item.addTime).format('YYYY-MM-DD')}})
</div>
<el-empty description="该分类里面还没有文章呢。" v-if="blogList.length === 0"></el-empty>
</el-card>
</template>
用 el-card 做个列表,上面是 添加博文的按钮,下面是博文列表,单击可以进行修改。
【引入的代码略】
// 博文列表
const blogList = reactive([])
// 博文管理
const { addNewBlog, getBlogListByGroupId } = blogManage()
// 状态管理
const { getBlogState, setEditArticleId } = blogStateManage()
// 博文的状态
const blogState = getBlogState()
// 更新列表
const load = () => {
getBlogListByGroupId(blogState.currentGroupId).then((data) => {
blogList.length = 0
blogList.push(...data)
})
}
load()
// 监控选择的分组的ID
watch(() => blogState.currentGroupId, () => {
load()
})
// 添加新文章,仅标题、时间
const addNewArticle = () => {
const newArticle = blogForm()
// 选择的分组ID
newArticle.groupId = blogState.currentGroupId
// 用日期作为默认标题
newArticle.title = dayjs(new Date()).format('YYYY-MM-DD')
addNewBlog(newArticle).then((id) => {
// 设置要编辑的文章ID
setEditArticleId(id)
// 通知列表
newArticle.ID = id
blogList.unshift(newArticle)
})
}
// 选择要编辑的文章
const changeArticle = (id) => {
setEditArticleId(id)
}
3
讨论列表
<el-card shadow="hover"
v-for="(item, index) in discussList"
:key="'bloglist_' + index"
>
<template #header>
<div class="card-header">
{{item.discusser}}
<span class="button">({{dateFormat(item.addTime).format('YYYY-MM-DD')}})</span>
</div>
</template>
<!--简介-->
<div class="text item" v-html="item.concent"></div>
<hr>
<i class="el-icon-circle-check"></i> {{item.agreeCount}}
</el-card>
<!--没有找到数据-->
<el-empty description="没有讨论呢,抢个沙发呗。" v-if="discussList.length === 0"></el-empty>
还是用 el-card 做个列表,el-empty 做一个没有讨论的提示。
【引入的代码略】
// 组件的属性
const props = defineProps({
id: String
})
// 管理
const { getDiscussListByBlogId } = blogManage()
// 获取状态
const { getBlogState } = blogStateManage()
const blogState = getBlogState()
// 表单的model
const discussList = reactive([])
getDiscussListByBlogId(props.id).then((data) => {
discussList.push(...data)
})
watch(() => blogState.isReloadDiussList, () => {
getDiscussListByBlogId(props.id).then((data) => {
discussList.length = 0
discussList.push(...data)
})
})
因为功能比较简单,所以代码也很简单,获取讨论数据绑定显示即可,暂时没有实现分页功能。
4
讨论表单
<el-form
style="width:400px;"
label-position="top"
:model="dicussModel"
label-width="80px"
>
<el-form-item label="昵称">
<el-input v-model="dicussModel.discusser"></el-input>
</el-form-item>
<el-form-item label="内容">
<el-input type="textarea" v-model="dicussModel.concent"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">发表讨论</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
用 el-form 做个表单。
【引入的代码略】
// 组件的属性
const props = defineProps({
id: String
})
// 管理
const { addDiuss } = blogManage()
// 获取状态
const { getBlogState, setReloadDiussList } = blogStateManage()
const blogState = getBlogState()
// 表单的model
const dicussModel = reactive(discuss())
// 发布讨论
const submit = () => {
dicussModel.blogId = props.id // 这是博文ID
addDiuss(dicussModel).then((id) => { // 可以想象成 axios 的提交
// 通知列表
setReloadDiussList()
})
}
分成多个组件,每个组件的代码就可以非常少了,这样便于维护。发布讨论的函数,先使用blogManage的功能提交数据,回调函数里面,使用的状态管理的功能提醒讨论列表刷新数据。
源码
https://gitee.com/naturefw/vue3-blog
在线演示
https://naturefw.gitee.io/vue3-blog
本文作者:自然框架
个人网址:jyk.cnblogs.com
声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。
写的不错?赞赏一下
长按扫码赞赏我