Vue项目实战:逸刻博客管理平台
# 逸刻博客管理项目
视频地址:https://www.bilibili.com/video/BV1Rm421T7wp
项目地址: https://gitee.com/yuan625/self-note-projects/tree/master/Vue/%E9%80%B8%E5%88%BB%E5%8D%9A%E5%AE%A2%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0
# 技术要求
VUE3 +TS + Node.js(Express) +Mysql
Yike Design
# 表结构梳理
用户(user): ID,名字,邮箱,密码,头像地址,创建时间
分类(subset):ID,分类名称,所属类型(0文章,1摄影,2资源),创建时间,备注
本地文件(file):ID,文件地址,文件名称,文件格式,所属分类ID,创建时间
文章/图库(article) : ID,标题,所属分类,所属类型(0文章,1摄影),标签,简介,内容,封面,查看次数,文章状态(0未发布,1发布),创建时间
文章点赞(praise): ID,所属文章ID,点赞者ID, 用户类型(0登录用户,1游客),创建时间
文章评论(praise): ID,所属文章ID,评论者ID, 用户类型(0登录用户,1游客),创建时间,评论内容,举报数,评论是否已读 (0未读,1已读)
标签(label): ID 标签名称 创建时间
日记diary: ID,标题,内容,图片,天气ID,心情,创建时间
天气weather : ID,天气名称,天气图标地址
私信message: ID,用户ID,用户类型(0登录用户,1游客),创建时间,联系方式(user_name) ,内容,是否已读(0未读,1已读)
埋点(record): ID,用户ID,用户类型(0登录用户,1游客),创建时间,访问位置,访问设备(0移动,1PC端)
# 创建表
# 数据接口文档
# 前端开发搭建
使用vitejs搭建:https://cn.vitejs.dev/guide/
选择pnpm搭建:pnpm create vite
- 项目命名(vue-manage),Vue, TypeScript
- cd vue-manage,pnpm install,pnpm run dev
- 使用vscode打开项目
使用Yike Design:https://yike.design/的组件库
- 需要安装less预编:pnpm install less --save-dev
测试是否安装生效:如果颜色生效就成功
<template>
<div class="test">aaaa</div>
</template>
<style scoped lang="less">
@color:#ee0004;
.test{
color: @color;
}
</style>
2
3
4
5
6
7
8
9
10
- 安装组件库:npm install --save-dev @yike-design/ui
- 安装其他依赖:
pnpm install --save-dev @yike-design/resolve
pnpm install --save-dev unplugin-vue-components
pnpm install --save-dev unplugin-auto-import
pnpm install --save-dev dayjs
2
3
4
按需引入:
vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { YikeResolver } from '@yike-design/resolver' // https://vitejs.dev/config/ export default
defineConfig({
plugins: [
vue(),
AutoImport({ resolvers: [YikeResolver()] }),
Components({ resolvers: [YikeResolver({ sideEffect: true })] }),
],
})
2
3
4
5
6
7
8
9
10
11
main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import '@yike-design/ui/es/index.less'
import { YkMessage, YkNotification } from '@yike-design/ui'
const app = createApp(App)
app.config.globalProperties.$notification = YkNotification
app.config.globalProperties.$message = YkMessage
app.mount('#app')
2
3
4
5
6
7
8
9
测试组件效果:
<yk-space>
<yk-button>主要按钮</yk-button>
<yk-button type="secondary">次要按钮</yk-button>
<yk-button type="outline">线框按钮</yk-button>
</yk-space>
2
3
4
5
在index.html中修改图标和名称
<head>
<link rel="icon" type="image/svg+xml" href="https://www.huohuo90.com:3006/yike.svg" />
<title>逸刻博客</title>
</head>
2
3
4
组件基准的颜色,大小规则: yike-design/ui/es/components/style/basis.less
如果要使用这里的颜色,需要在配置less预处理:https://yike.design/develop/theme
export default defineConfig({
plugins: [vue(),
AutoImport({ resolvers: [YikeResolver()] }),
Components({ resolvers: [YikeResolver({ sideEffect: true })] })],
css: {
// css预处理器
preprocessorOptions: {
less: {
charset: false,
additionalData: '@import "@yike-design/ui/es/components/styles/basis.less";',
},
},
},
})
2
3
4
5
6
7
8
9
10
11
12
13
14
此时可以直接使用:
<style scoped lang="less">
.test{
color: @scolor; //@scolor就是basis.less中的样式
}
</style>
2
3
4
5
# 配置路由环境
Vue Router地址:https://router.vuejs.org/zh/
入门,到安装:
pnpm add vue-router@4 --save-dev
根据设计图,页面分三类:
- 导航栏/左菜单栏/内容栏
- 导航栏//内容栏
- 登录页
除去登录页,所以页面可以设计为两部分:头部 & 内容,内容以路由方式切换(
App.vue
)
<template>
<!-- 头部 -->
<!-- 内容 -->
<router-view></router-view>
</template>
2
3
4
5
- 创建路由目录文件
src/router/index.ts
,路由文档下的入门中创建路由器实例,使用到嵌套路由
import { createMemoryHistory, createRouter } from 'vue-router'
import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
// 当 /user/:id/profile 匹配成功
// UserProfile 将被渲染到 User 的 <router-view> 内部
path: 'profile',
component: UserProfile,
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 将被渲染到 User 的 <router-view> 内部
path: 'posts',
component: UserPosts,
},
],
},
]
const router = createRouter({
history: createMemoryHistory(),
routes,
})
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
- 创建路由组件
src/views/IndexView.vue
和src/views/OverView.vue
<template>
<!-- 菜单 -->
<div> 菜单</div>
<router-view></router-view>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped></style>
2
3
4
5
6
7
<template>
<div> 总览(内容)</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped></style>
2
3
4
5
- 重新修改
src/router/index.ts
import { createWebHistory, createRouter } from 'vue-router'
import IndexView from '../views/IndexView.vue'
import HelloWorld from '../components/HelloWorld.vue'
const routes = [
{
path: '/',
name: 'home',
redirect: '/overview',
component: IndexView,
children: [
{
path: 'overview',
component:()=>import('../views/OverView.vue'),
},
{
path: 'hello',
component: HelloWorld,
},
],
},
{
path: '/hellox',
component: HelloWorld,
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router
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
- 将这个路由在
main.ts
中引入挂载使用
import router from './router'
app.use(router).mount('#app')
2
- 此时启动
pnpm dev
,访问:- 启动跳转路径(根路径)
/overview
: 总览+菜单 /hello
: 总览+ hello页/hellox
: hello页
- 启动跳转路径(根路径)
# 头部HeadBar组件开发
清除总体的样式
.style.css
,修改名称使用less:style.less
,在mian.ts中引入修改:因为样式是从上到下加载,所以这个样式要生效,要放到最下面。- style.less
body { background-color: @pcolor; }
1
2
3- mian.ts
import { createApp } from 'vue' import App from './App.vue' import '@yike-design/ui/es/index.less' import { YkMessage, YkNotification } from '@yike-design/ui' import './style.less' import router from './router' const app = createApp(App) app.config.globalProperties.$notification = YkNotification app.config.globalProperties.$message = YkMessage app.use(router).mount('#app')
1
2
3
4
5
6
7
8
9
10创建头部组件
src/components/HeaderBar.vue
<template>
<div>头部</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped></style>
2
3
4
5
- 在
App.vue
中引入使用
<script setup lang="ts">
import HeaderBar from './components/HeaderBar.vue'
</script>
<template>
<header-bar/>
<router-view/>
</template>
2
3
4
5
6
7
- 开始写头部组件的样式:position,离顶部和左多长,宽度,背景颜色,因为是层级,下面的穿在头部,所以需要层级 z-index来实现。
<template>
<div class="hear-bar">头部</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped>
.hear-bar{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 64px;
background-color: @bg-color-l;
z-index:10
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
因为整体都需要往下移动,所以需要在全局style.less中添加样式
#app{
padding-top: 64px;
}
2
3
头部分左右两部分:
- 左边:2个元素:博客名称 & logo ,间距:24px
- 右边:4个元素:信件、头像、黑白模型切换、退出按钮,间距:12px
组件中有:
间距布局<yk-space>
,kBadge 徽标,Icon 图标,Avatar 头像,开发-暗黑模式,Button 按钮
<template>
<div class="hear-bar">
<!-- 左 -->
<yk-space align="center" :size="12" style="cursor:pointer">
<img data-v-3a4b8fe9="" src="data:image/svg+xml,%3csvg%20width='40'%20height='40'%20viewBox='0%200%2040%2040'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3cg%20clip-path='url(%23clip0_14_19)'%3e%3cpath%20d='M28.4449%207.72412H39.7241L25.7991%2032.2759H14.5198L28.4449%207.72412Z'%20fill='%232B5AED'/%3e%3cpath%20opacity='0.5'%20d='M13.9681%2032.2759H25.2004L25.2004%207.72414H13.9681L13.9681%2032.2759Z'%20fill='%232B5AED'/%3e%3cpath%20opacity='0.5'%20d='M39.8212%2032.2759H30.166L30.166%207.72414H39.8212V32.2759Z'%20fill='%232B5AED'/%3e%3cpath%20d='M5.70597%2017.3793L11.3122%207.72409H0.079895L5.70597%2017.3793Z'%20fill='%232B5AED'/%3e%3cpath%20d='M13.9449%207.72412H25.2043L11.2793%2032.2759H0L13.9449%207.72412Z'%20fill='%232B5AED'/%3e%3c/g%3e%3cdefs%3e%3cclipPath%20id='clip0_14_19'%3e%3crect%20width='40'%20height='24.5517'%20fill='white'%20transform='translate(0%207.72412)'/%3e%3c/clipPath%3e%3c/defs%3e%3c/svg%3e"
style="width: 40px; height: 40px;" class="logo"/>
<span class="name">逸刻博客后台</span>
</yk-space>
<!-- 右 -->
<yk-space align="center" size="xl">
<yk-badge is-dot>
<IconMailOutline style="font-size: 20px;"/>
</yk-badge>
<yk-avatar
img-url="https://www.huohuo90.com:3003/user/6353b034dd4b583975e77fbe.png"
></yk-avatar>
<div>
<yk-theme />
</div>
<yk-button>退出</yk-button>
</yk-space>
</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped>
.hear-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 64px;
background-color: @bg-color-l;
// z-index:10
display: flex; //两边分
align-items: center; //上下居中
justify-content: space-between; //两边分
padding: 0 @space-xl;//两边间距 @space-xl为24px
.logo{
height: 24px;
width: 38px;
}
.name {
font-size: 20px;
font-weight: 600;
}
}
</style>
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
align="center"
上下居中
cursor:pointer
链接地址为手心
display: flex;
启用Flexbox布局模式。Flexbox是一种一维的布局模型,旨在提供更高效的空间分配机制
你可以与display: flex;
一起使用来定制布局:
flex-direction
: 控制主轴的方向,可以是row
(默认)、row-reverse
、column
或column-reverse
。justify-content
: 在主轴上对齐子元素,如flex-start
(默认)、flex-end
、center
、space-between
或space-around
。align-items
: 在交叉轴上对齐子元素,如flex-start
、flex-end
、center
、baseline
或stretch
(默认)。align-content
: 当有多行子元素时,在交叉轴上对齐这些行,类似于align-items
,但应用于行而不是单个子元素。flex-wrap
: 决定子元素是否应该换行,可以是nowrap
(默认)、wrap
或wrap-reverse
。flex-grow
,flex-shrink
, 和flex-basis
: 控制子元素如何在容器中扩展、收缩和占用空间。
justify-content: space-between;
是CSS Flexbox和Grid布局中的一个属性值,用于在容器的主轴上均匀分布子元素,并确保第一个子元素紧靠容器的起始边缘,最后一个子元素紧靠容器的结束边缘,而中间的空白空间则被平均分配给子元素之间的间距。
align-items: center;
用于在Flexbox布局中垂直居中元素。
# 菜单栏开发
- 规范化,components下新建bar目录,将HeaderBar.vue移动大这个之下,在该目录下新建
bar/MenuBar.vue
菜单组件
<template>
<!-- 菜单 -->
<div>菜单</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped></style>
2
3
4
5
6
- 修改
IndexView.vue
<template>
<!-- 菜单 -->
<menu-bar/>
<router-view/>
</template>
<script lang="ts" setup>
import MenuBar from '../components/bar/MenuBar.vue'
</script>
<style lang="less" scoped></style>
2
3
4
5
6
7
8
9
根据组件库选择space,排列方向选择竖排
vertical
因为是菜单列表,创建一个ts来模拟获取的后端数据,文件路径
src/utils/menu.ts
,icon图标来自组件库
export const navLinks = [
{
path: 'overview',
name: '总揽',
icon: 'IconHomepageFill'
},
{
path: 'localfile',
name: '本地文件',
icon: 'IconFolderCloseFill'
},
{
path: 'article',
name: '博文文章',
icon: 'IconFileFill'
},
{
path: 'gallery',
name: '摄影图库',
icon: 'IconImageFill'
},
{
path: 'diary',
name: '随笔随记',
icon: 'IconFillFill'
},
{
path: 'install',
name: '设置',
icon: 'IconSettingsFill'
},
]
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
- 编写菜单组件:循环router-link,还有组件库中的Typography 排版的文本
<template>
<yk-space dir="vertical" class="menu-bar">
<router-link :to="item.path" v-for="item in navLinks" :key="item.path">
<yk-space>
<component :is="item.icon"/>
<yk-text>{{ item.name }}</yk-text>
</yk-space>
</router-link>
</yk-space>
</template>
<script lang="ts" setup>
import {navLinks} from '../../utils/menu'
</script>
<style lang="less" scoped></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 图标未生效,因为是动态引入的,需要按需引入在main.ts中
import Icon from '@yike-design/ui/es/components/svg-icon'
app.use(router).use(Icon).mount('#app')
2
- 开始补充样式:外边距,上/左距离,宽度,position
.menu-bar{
width: 160px;
padding: @space-l;
top: 72px;
left: 8px;
position: fixed;
}
2
3
4
5
6
7
由于overview中的内容显示,修改IndexView中的样式,设置左边距:160+8+32=200px
<template>
<div class="index-view">
<menu-bar/>
<router-view/>
</div>
</template>
<script lang="ts" setup>
import MenuBar from '../components/bar/MenuBar.vue'
</script>
<style lang="less" scoped>
.index-view{
padding-left: 168px;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
- 设置每个菜单:之间的边距12px, 居中,给router-link设置class名,在设置宽/高/背景色/边框圆角
<template>
<yk-space dir="vertical" class="menu-bar" size="m">
<router-link :to="item.path" v-for="item in navLinks" :key="item.path" class="menu-bar_nav">
<yk-space align="center" size="m">
<component :is="item.icon"/>
<yk-text>{{ item.name }}</yk-text>
</yk-space>
</router-link>
</yk-space>
</template>
<script lang="ts" setup>
import {navLinks} from '../../utils/menu'
</script>
<style lang="less" scoped>
.menu-bar{
// width: 160px;
padding: @space-l;
top: 72px;
left: 8px;
position: fixed;
&_nav{
width: 160px; //宽度
height: 40px; //高度
border-radius: @radius-m; //边框圆角
padding: 0 @space-l; //设置边距
display: flex;
align-items: center; //垂直居中
text-decoration: none; //去掉字下面的划线
.yk-icon{
color: @font-color-ss; //黑色
width: 16px;
height:16px;
}
&:hover{ //鼠标激活状态
background: @bg-color-l; //背景色:白色
.yk-text{
font-weight: 600; //字体加粗
}
}
}
.router-link-active{ //默认路由激活状态
background:linear-gradient(180deg, var(--pcolor6) 0%, var(--pcolor8) 100%);
.yk-icon{
color: @bg-color-l; //白色
}
.yk-text{
color: @bg-color-l;
font-weight: 600; //字体加粗
}
}
}
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
- 处理头部组件
HeaderBar
中点击图标跳转到首页
<template>
<div class="hear-bar">
<!-- 左 -->
<yk-space align="center" :size="12" style="cursor:pointer" @click="backHome">
....
</yk-space>
</div>
</template>
<script lang="ts" setup>
import {useRouter} from 'vue-router'
const router = useRouter()
//返回总览
const backHome = ()=>{
router.push('/')
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 标题组开发
- 父子组件间传值defineEmits,typescript创建对象且默认值withDefaults,点击事件@click,组件中的搜索事件@search,组件标题yk-title的等级level,组件搜索框,v-if/show/model判读/展示/绑定事件,slot卡槽使用
创建标题组件src/componets/bar/TopTitle.vue
<template>
<div class="top-title">
<!-- :is-search值变化,标题有上下抖动,是因为高度不一样导致,使用line-height来调整 -->
<yk-title :level="3" style="margin: 0; line-height:36px">{{ props.name }}</yk-title>
<slot name="custom" />
<yk-space size ="s" v-if="isSearch">
<yk-button type="secondary" v-show="searchData" @click="cancleSearch">取消搜索</yk-button>
<yk-input-search placeholder="请输入..." style="width: 320px" v-model="searchData" @search="search">
<template #suffix>
<yk-button type="secondary"><IconSearchOutline/></yk-button>
</template>
</yk-input-search>
</yk-space>
</div>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
const searchData = ref()
type titleProps = {
name: string;
isSearch: boolean;
};
const props = withDefaults(defineProps<titleProps>(), {
name: "总览",
isSearch: true
});
const emit =defineEmits(['search'])
//搜索事件
const search =() =>{
emit('search',searchData.value)
}
//取消事件
const cancleSearch =() =>{
emit('search','')
searchData.value =''
}
</script>
<style lang="less" scoped>
.top-title{
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
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
在总览组件Overview.vue
中引入TopTitle
,is-search
是来自子组件的传值,控制是否展示搜索,同时可以使用custom来控制展示自定义内容
<template>
<top-title :is-search="false">
<template #custom>
<yk-button>hahaha</yk-button>
</template>
</top-title>
</template>
<script lang="ts" setup>
import TopTitle from '../components/bar/TopTitle.vue'
</script>
<style lang="less" scoped></style>
2
3
4
5
6
7
8
9
10
11
# 总览卡开发
- 使用mockjs模拟后端数据 mockjs.com:
pnpm install mockjs --save-dev
&npm i --save-dev @types/mockjs
- 创建mock数据
src/mock/data.ts
import Mock from "mockjs";
const Random = Mock.Random
//纵览数据
export const overview = Mock.mock({
"code": 200,
"data": {
"file": Random.float(60,100,3,3)+'M',
"article|0-50": 0,
"gallery|0-50": 0,
"diary|0-50": 0,
"project|0-50": 0,
"resource|0-50": 0,
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
将创建数据使用到menu.ts中:
//总览数据
export const overLink = [
{
path: "",
name: "本地文件",
total: "0M",
bColor: "180deg, #2b5aedcc 0%,#2B5AED 100%",
},
{
path: "editaricle",
name: "博客文章",
total: 0,
bColor: "180deg, #ff600829 0%,#ff60083d 100%",
},
{
path: "editgallery",
name: "摄影图库",
total: 0,
bColor: "180deg, #25df0629 0%,#25df063d 100%",
},
{
path: "edit",
name: "随笔随记",
total: 0,
bColor: "180deg, #00c9f529 0%,#00c9f53d 100%",
},
];
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
创建总览页面src/componets/overview/gather.vue
<template>
<div class="index-view">
</div>
</template>
<script lang="ts" setup>
</script>
<style lang="less" scoped>
</style>
2
3
4
5
6
7
8
将总览页面引入到OverView页面
<template>
<yk-space dir="vertical" size="m">
<top-title :is-search="false"/>
<gather/>
</yk-space>
</template>
<script lang="ts" setup>
import TopTitle from '../components/bar/TopTitle.vue'
import gather from '../components/overview/gather.vue';
</script>
<style lang="less" scoped></style>
2
3
4
5
6
7
8
9
10
11
编写gather页面,将mock数据渲染到页面
<template>
<yk-space>
<div v-for="(item,index) in overLink" :key="index">
<yk-space>
<yk-text>{{ item.name }}</yk-text>
<yk-title>{{ item.total }}</yk-title>
</yk-space>
<yk-button v-if="index>0" size="xl" type="secondary" shape="square">
<IconPlusOutline/>
</yk-button>
</div>
</yk-space>
</template>
<script lang="ts" setup>
import { overLink } from '../../utils/menu';
import { overview } from '../../mock/data';
</script>
<style lang="less" scoped>
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 添加样式
<template>
<yk-space class="gather">
<div v-for="(item,index) in gathers" :key="index" class="gather_list" :style="{background:'linear-gradient('+item.bColor+')'}">
<!-- 上下布局,间距 -->
<yk-space dir="vertical" size="s">
<yk-text>{{ item.name }}</yk-text>
<!-- 标题的字体大小 margin0-->
<yk-title :level="2" style="margin: 0;">{{ item.total }}</yk-title>
</yk-space>
<yk-button v-if="index>0" size="xl" type="secondary" shape="square">
<IconPlusOutline/>
</yk-button>
</div>
</yk-space>
</template>
<script lang="ts" setup>
import { ref,onMounted } from 'vue';
import { overLink } from '../../utils/menu';
import { overview } from '../../mock/data';
const gathers =ref(overLink)
const drawGatherData = ()=>{
let data = overview.data;
gathers.value[0].total = data.file;
gathers.value[1].total = data.article;
gathers.value[2].total = data.gallery;
gathers.value[3].total = data.diary;
}
onMounted(()=>{
drawGatherData();
})
</script>
<style lang="less" scoped>
.gather{
width: 100%;
&_list{
width: 25%;
display: flex;
justify-content: space-between;
padding: @space-xl;
border-radius: @radius-m;
align-items: center; //上下居中
&:first-child{
.yk-title,.yk-text{
color: @white;
}
}
}
}
</style>
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
# 仿知乎
视频:https://www.bilibili.com/video/BV1Wt421K7xz
# 搭建Vue3环境
使用vite创建项目:npm init vite
=> vue
=>javascript
,在执行pnpm install &pnpm dev
vite.config.js
中设置自动打开浏览器,设置端口:
export default defineConfig({
server: {
open: true, //自动打开
port: 8080 //设置端口
},
plugins: [vue()],
})
2
3
4
5
6
7
安装依赖:yarn add vue-router -S
,S:生产环境,D: 测试环境
配置路由src/router/index.js
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [{
path: '/',
name: 'Home',
component: () =>
import ('')
}]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
2
3
4
5
6
7
8
9
10
11
12
13
同时,在main.js中引入挂载路由
import router from './router/index.js' // ./ 同级,../ 上级,@/ 根目录(src)=>手动配置
createApp(App).use(router).mount('#app')
2
手动配置@/ 根目录(src): 在vite.config.js中配置:
import path from 'path'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
})
2
3
4
5
6
7
8
9
mian.jz中全局引入css样式:
import '@/assets/css/common.css'
import '@/assets/css/index.css'
createApp(App).use(router).mount('#app')
2
3
引入element-plus并安装yarn add element-plus -S
在进入https://element-plus.org/中的快速开始>按需导入 > 自动导入安装unplugin-vue-components
和 unplugin-auto-import
npm install -D unplugin-vue-components unplugin-auto-import
# 模块划分
views
- education 知乎学堂
- explore 发现
- index
-hotlist 热榜
-interest 关注页
-recommend 推荐
-video 视频
index.vue 首页
- login 登录页
- waiting 等你来答
2
3
4
5
6
7
8
9
10
11
# 路由
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [{
path: '/',
name: 'Home',
redirect: '/login'
// component: () =>import ('')
},
{
path: '/login',
name: 'login',
component: () =>
import ('../views/login/index.vue')
},
{
path: '/index',
name: 'index',
component: () =>
import ('../views/index/index.vue'),
children: [{
path: '/index/interest',
name: 'interest',
component: () =>
import ('../views/index/interest/index.vue')
}, {
path: '/index/hotlist',
name: 'hotlist',
component: () =>
import ('../views/index/hotlist/index.vue')
}, {
path: '/index/recommend',
name: 'recommend',
component: () =>
import ('../views/index/recommend/index.vue')
}, {
path: '/index/video',
name: 'video',
component: () =>
import ('../views/index/video/index.vue')
},
]
}, {
path: '/education',
name: 'education',
component: () =>
import ('../views/education/index.vue')
}, {
path: '/explore',
name: 'explore',
component: () =>
import ('../views/explore/index.vue')
}, {
path: '/waiting',
name: 'waiting',
component: () =>
import ('../views/waiting/index.vue')
},
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
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
App.vue添加路由
<script setup></script>
<template><router-view /></template>
<style scoped></style>
2
3
家具网址:https://www.bilibili.com/video/BV19N4y1R735
小鲜兔:https://www.bilibili.com/video/BV1Ac411K7EQ
仿微信:https://www.bilibili.com/video/BV1qz421Y7zR
网盘:https://www.bilibili.com/video/BV1Qs4y1z7Km
Vue3+Vite4+Pinia+ElementPlus从0-1 web项目搭建: https://www.bilibili.com/video/BV1Hz4y1j7Mw
vue3+vite企业应用级基础框架实战https://www.bilibili.com/video/BV1MC4y1o7x3
Vue CascadeSelect Component (primevue.org) (opens new window)