提示:文章有点长,请各位小伙伴耐心观看
效果图
提示:以下是本篇文章正文内容,下面案例可供参考
一、准备工作
1、创建vue3的工程文件
npm init vue@latest
Attention!!!:一步一步按照老师教的来、值得注意的事我并未选择eslint校验工具,因为在日常开放中会很麻烦。
1、创建对应目录文件
主页!!!!!!:只要是上面没有的文件全部删除
然后直接跳到 第三步的1、编写index.js(可以直接复制,前提是以上一、二步骤完成或者上面目录对应创建)
2、修改全局样式
1、删除src下的assets下的样式文件 只保留svg文件
2、在src下的main.js中注释掉 import ‘./assets/main.css’
// import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
3、删除app.vue里面的style以及其他不必要组件
<script setup>
import { RouterLink, RouterView } from 'vue-router'
</script>
<template>
<RouterView />
</template>
<style scoped>
</style>
2、安装element ui Plus
(1)、进入element官网element ui Plus
(2)、点击指南找到安装中的使用包管理器
npm install element-plus --save
(3)、全局引入 element ui
// main.js
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
二、开始编写基本页面
1、头部导航编写
使用element ui组件库搭建更快哟
(1)在src文件夹下的component文件夹下新建BarComponent.vue文件
(2)编写
这里我们使用的是vue3.2的写法
1.1、创建3.2vue模版
<template>
</template>
<script setup>
//在script 标签上加入 setup 语法糖
</script>
<style scoped>
/*在style上加伤scoped 防止样式干扰*/
</style>
1.2、在BarComponent.vue文件中使用 element ui Plus 的 Navigation 导航 中的 Menu 组件
在官网点击组件 在侧边导航栏找到 Navigation 导航 再找到 Menu
将以上源代码全部复制到 BarComponent.vue 中
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
:ellipsis="false"
@select="handleSelect"
>
<el-menu-item index="0">LOGO</el-menu-item>
<div class="flex-grow" />
<el-menu-item index="1">Processing Center</el-menu-item>
<el-sub-menu index="2">
<template #title>Workspace</template>
<el-menu-item index="2-1">item one</el-menu-item>
<el-menu-item index="2-2">item two</el-menu-item>
<el-menu-item index="2-3">item three</el-menu-item>
<el-sub-menu index="2-4">
<template #title>item four</template>
<el-menu-item index="2-4-1">item one</el-menu-item>
<el-menu-item index="2-4-2">item two</el-menu-item>
<el-menu-item index="2-4-3">item three</el-menu-item>
</el-sub-menu>
</el-sub-menu>
</el-menu>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const activeIndex = ref('1')
const handleSelect = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
</script>
<style>
.flex-grow {
flex-grow: 1;
}
</style>
·注意:这里面的script标签没有 lang = 'ts'
因为我们并未下载 typescript 的解析因此不能使用它
注意 !!!:毫无疑问会报错,因为这是使用的 typescript 的类型校验写法 因此我们需要对他进行改动
1.3、修改报错部分
删除类型教研即可
<script setup>
const handleSelect = (key, keyPath) => {
console.log(key, keyPath)
}
</script>
1.4、改造模版
因为他自己的模版是和我们的有差距因此我们需要改一下
<div class="bar">
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
:ellipsis="false"
@select="handleSelect"
>
<el-menu-item index="0">
<el-avatar>LOGO</el-avatar>
</el-menu-item>
<div class="flex-grow" />
<el-menu-item index='1'>登陆</el-menu-item>
<el-menu-item index='2'>注册</el-menu-item>
<el-menu-item index='3'>首页</el-menu-item>
<el-menu-item index='4'>退出登陆</el-menu-item>
</el-menu>
</div>
1.5、加入跳转效果
这里我们需要在 menu 上加上 :router=‘true’,这样导航栏就有了router跳转的功能,其中index就是跳转的地址
<div class="bar">
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
:ellipsis="false"
@select="handleSelect"
:router='true'
>
<el-menu-item index="0">
<el-avatar>LOGO</el-avatar>
</el-menu-item>
<div class="flex-grow" />
<el-menu-item index='1'>登陆</el-menu-item>
<el-menu-item index='2'>注册</el-menu-item>
<el-menu-item index='3'>首页</el-menu-item>
<el-menu-item index='4'>退出登陆</el-menu-item>
</el-menu>
</div>
现在的导航基本完成
2.注册页面编写
(1)在src目录下的view目录下新建RegisterView.vue文件
(2)编写
(2.1)这里的输入框以及按钮我也是使用的element UI
<template>
<div class="content">
<div>
<div style="color:aliceblue">
<h2>welcome to Register</h2>
</div>
<div>
<!-- element UI 组件库的input输入框-->
<el-input v-model="username" placeholder="Please input" clearable />
</div>
<div style="margin: 10px 0;">
<!-- element UI 组件库的input密码框-->
<el-input
v-model="password"
type="password"
placeholder="Please input password"
show-password
/>
</div>
<div>
<!-- element UI 组件库的按钮-->
<el-button @click="Login()" type="primary">Register</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
//改良一下可以使用 类的方式书写
let username = ref('')
let password = ref('')
let user = ref({}) //用户对象
let userArray = ref([]) //用户数组 存储所有的用户
function Login(){
user.value.name = username.value
user.value.password = password.value
//本地存储不过多解释
// 需要注意的是 存数组或对象 要把他们转化成字符串 取的时候又要把字符串还原
if(JSON.parse(localStorage.getItem('userA')) != null){
userArray.value = JSON.parse(localStorage.getItem('userA'))
}
userArray.value.push(user.value)
localStorage.setItem('userA',JSON.stringify(userArray.value))
}
</script>
<style scoped>
.content{
/* width: 500%; */
background-size: 500%;
height: 90vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(125deg,#29c55b,#328be5,#6130e6,#b831e1);
animation: bcColor 1s linear infinite;
}
@keyframes bcColor{
0%{background-position: 0% 50%;};
50%{background-position: 100% 50%;};
100%{background-position: 0% 50%;}
}
.content >div>div{
display: flex;
justify-content: center;
}
.content>div>div>button{
height: 34px;
width: 230px;
text-align: center;
/* color: aliceblue; */
font-size: 18px;
margin: 5px 0;
}
button:hover{
letter-spacing: 3px;
}
</style>
3.登陆页面编写
(1)在src目录下的view目录下新建LoginView.vue文件
(2)编写
(2.1)这里的输入框以及登陆按钮我也是使用的element UI
<template>
<div class="content">
<div>
<div style="color:aliceblue">
<h2>welcome to login</h2>
</div>
<div>
<!-- element UI 组件库的input输入框-->
<el-input v-model="username" placeholder="Please input" clearable />
</div>
<div style="margin: 10px 0;">
<!-- element UI 组件库的input密码框-->
<el-input
v-model="password"
type="password"
placeholder="Please input password"
show-password
/>
</div>
<div>
<!-- element UI 组件库的按钮-->
<el-button @click="Login()" type="primary">Login</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter, useRoute } from 'vue-router' //引入路由
const router = useRouter()
const route = useRoute()
let username = ref('')
let password = ref('')
function Login(){
//得到本地存储的 user 数组
let user = JSON.parse(localStorage.getItem('userA'))
//遍历 user 数组
user.forEach(el => {
// 比较数组中是否有我登陆的信息 有就登陆成功
if(el.name == username.value && el.password == password.value){
localStorage.setItem('token',`${username.value}/${password.value}`)
// 跳转到主页面
// router.push({
// path:'main'
// })
}
});
}
</script>
<style scoped>
.content{
/* width: 500%; */
background-size: 500%;
height: 90vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(125deg,#29c55b,#328be5,#6130e6,#b831e1);
animation: bcColor 1s linear infinite;
}
@keyframes bcColor{
0%{background-position: 0% 50%;};
50%{background-position: 100% 50%;};
100%{background-position: 0% 50%;}
}
.content >div>div{
display: flex;
justify-content: center;
}
.content>div>div>button{
height: 34px;
width: 210px;
text-align: center;
/* color: aliceblue; */
font-size: 18px;
margin: 5px 0;
}
button:hover{
letter-spacing: 3px;
}
</style>
4.主页面编写
(1)在src目录下的view目录下新建MainView.vue文件
(2)编写
(2.1)这里的输入框以及按钮我也是使用的element UI
代码如下(示例):
<template>
<div class="content">
<!--element ui tab组件-->
<el-tabs :tab-position="tabPosition" style="height: 90vh" class="demo-tabs">
<el-tab-pane label="图书管理">
<BookCViewVue></BookCViewVue>
</el-tab-pane>
<el-tab-pane label="用户管理">
<UserCViewVue></UserCViewVue>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { ref } from 'vue'
import BookCViewVue from './ControlView/BookCView.vue'
import UserCViewVue from './ControlView/UserCView.vue'
const tabPosition = ref('left')
</script>
<style scoped>
.demo-tabs > .el-tabs__content {
padding: 32px;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
.el-tabs--right .el-tabs__content,
.el-tabs--left .el-tabs__content {
height: 100%;
}
</style>
5.home页面的编写
(1)在src目录下的view目录下新建HomeView.vue文件
(2)编写
(2.1)这里的输入框以及按钮我也是使用的element UI
<template>
<div class="content">
<!--头部导航-->
<el-affix :offset="0">
<div class="bar">
<BarCmpVue></BarCmpVue>
</div>
</el-affix>
<!-- 导航对应内容部分-->
<div>
<RouterView></RouterView>
</div>
</div>
</template>
<script setup>
import { RouterView} from 'vue-router'
import BarCmpVue from '../components/BarComponent.vue'
</script>
该处使用的url网络请求的数据。
三、开始编写路由
1、编写index.js(可以直接复制,前提是以上一、二步骤完成)
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(
import.meta.env.BASE_URL),
routes: [{
path: '/',
name: 'home',
component: HomeView,
redirect: '/login',
children: [{
// 当 /user/:id/profile 匹配成功
// UserProfile 将被渲染到 User 的 <router-view> 内部
path: 'main',
component: () =>
import ('../views/MainView.vue'),
},
{
// 当 /user/:id/profile 匹配成功
// UserProfile 将被渲染到 User 的 <router-view> 内部
path: 'login',
component: () =>
import ('../views/LoginView.vue'),
},
{
// 当 /user/:id/profile 匹配成功
// UserProfile 将被渲染到 User 的 <router-view> 内部
path: 'register',
component: () =>
import ('../views/Register.vue'),
}
]
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import ('../views/AboutView.vue')
},
{
path: '/look',
name: 'look',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import ('../views/LookBookView.vue')
},
]
})
router.beforeEach(async(to, from) => {
if (to.path === '/login') return;
if (to.path === '/register') return;
//获取token
const tokenStr = localStorage.getItem('token')
if (!tokenStr || tokenStr == 'null') return '/login'
// next()
})
export default router
2、编写头部导航的路由对应关系
<template>
<div class="bar">
<!--
:default-active 默认显示的页面路由
@select="handleSelect" 选择导航时出发的事件
-->
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
:ellipsis="false"
@select="handleSelect"
:router="true"
style="height: 10vh;"
>
<el-menu-item index="0">
<!--头像组件-->
<el-avatar> {{ userTitle[0] }} </el-avatar>
</el-menu-item>
<div class="flex-grow" />
<!--这里我们使用v-for循环的方式-->
<el-menu-item v-for="(p,index) in path" :index="p.path" :key="index">{{ p.name }}</el-menu-item>
<el-menu-item @click="logout()">退出登陆</el-menu-item>
</el-menu>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const path = ref([
{name:'登陆',path:'login'},
{name:'注册',path:'register'},
{name:'首页',path:'main'},
])
const logout = () => {
localStorage.setItem('token',null)
router.push({
path:'login'
})
}
const userTitle = ref('')
const activeIndex = ref('login')
//组件对应的方法
const handleSelect = (key, keyPath) => {
activeIndex.value = key
localStorage.setItem('path',activeIndex.value)
}
onMounted(() => {
activeIndex.value = localStorage.getItem('path')
userTitle.value = localStorage.getItem('token')
if(userTitle.value == 'null'){
router.push({
path:'login'
})
}
})
</script>
<style scoped>
.flex-grow {
flex-grow: 1;
}
</style>
四、开始编写主页对应关系页面
以下部分有难度的
element ui官方文档
在 view目录下新建ControlView目录并在目录下新建BookCView.vue / UserCView.vue文件
1、编写BookCView.vue文件(图书管理)
<template>
<!--使用的是element ui里main的table组件-->
<el-table :data="filterTableData" style="width: 100%;" :stripe="true" :border="true" >
<el-table-column align="center" label="添加时间" prop="date"/>
<el-table-column align="center" label="书名" prop="bookName" />
<!-- <el-table-column align="center" label="详情" prop="textarea" /> -->
<el-table-column align="center" label="作者" prop="authorName" />
<el-table-column align="center" >
<template #header>
<div style="display: flex;justify-content: space-around;">
<el-button @click="addBook()">添加图书</el-button>
<el-input v-model="search" size="small" placeholder="查找图书" style="margin-left: 10px;"/>
</div>
</template>
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)" type="primary"
>编辑信息</el-button
>
<el-button size="small" @click="gotolook(scope.$index, scope.row)" type="success"
>查看图书</el-button
>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>删除图书</el-button
>
</template>
</el-table-column>
</el-table>
<el-dialog
v-model="dialogVisible"
title="图书添加"
width="80%"
style="height: 500px;"
>
<el-input v-model="bookName" placeholder="输入书名" clearable style="margin-bottom: 10px;"/>
<el-input v-model="textarea" :rows="13" type="textarea"
placeholder="输入内容"
/>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="add()">
Confirm
</el-button>
</span>
</template>
</el-dialog>
<el-dialog
v-model="dialogVisible11"
title="图书编辑"
width="80%"
style="height: 500px;"
>
<el-input v-model="bookName" placeholder="输入书名" clearable style="margin-bottom: 10px;"/>
<el-input v-model="textarea" :rows="13" type="textarea"
placeholder="输入内容"
/>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible11 = false">Cancel</el-button>
<el-button type="primary" @click="change()">
Confirm
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { useRouter, useRoute } from 'vue-router'
import { computed, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
const open2 = () => {
ElMessage({
message: '操作成功',
type: 'success',
})
}
const open4 = () => {
ElMessage.error('操作失败')
}
const dialogVisible = ref(false)
const dialogVisible11 = ref(false)
const search = ref('') //查找
let bookName = ref('') //书名
let textarea = ref('') //内容
let bookMsg = ref({}) //空书信息
let tableData= []
let indexxx = null
const handleEdit = (index, row) => {
dialogVisible11.value = true
bookName.value = row.bookName
textarea.value = row.textarea
indexxx = index
}
const router = useRouter()
const route = useRoute()
const gotolook = (index,row) =>{
router.push({
path: 'look',
query:{id:index},
})
}
// 编辑图书
const change = () =>{
tableData[indexxx].bookName = bookName.value
tableData[indexxx].textarea = textarea.value
localStorage.setItem('bookA',JSON.stringify(tableData))
dialogVisible11.value = false
open2()
}
// 删除图书
const handleDelete = (index, row) => {
console.log(index, row)
tableData.splice(index,1)
localStorage.setItem('bookA',JSON.stringify(tableData))
okk()
}
const addBook = () => {
dialogVisible.value = true
}
// 添加图书
const add = () => {
if( bookName.value !='' && textarea.value != ''){
bookMsg.value.date = new Date
bookMsg.value.bookName = bookName.value
bookMsg.value.textarea = textarea.value
bookMsg.value.authorName = localStorage.getItem('token').split('/')[0]
tableData.push(bookMsg.value)
localStorage.setItem('bookA',JSON.stringify(tableData))
open2()
dialogVisible.value = false
}else{
open4()
}
}
//判断是否有
//搜索
let filterTableData = ref([])
if(localStorage.getItem('bookA') != null){
tableData = JSON.parse(localStorage.getItem('bookA'))
filterTableData = computed(() =>
tableData.filter(
(data) =>
!search.value ||
data.bookName.toLowerCase().includes(search.value.toLowerCase())
)
)
}
</script>
<style scoped>
</style>
2、编写UserCView.vue文件(用户管理)
<template>
<div>
<table style="border-collapse:collapse;">
<tr class="title">
<td>序号</td>
<td>姓名</td>
<td>密码</td>
<td>操作</td>
</tr>
<tr v-for="(u,index) in dataUser" :key="index">
<td>{{ index+1 }}</td>
<td>{{ u.name }}</td>
<td>{{ u.password }}</td>
<td style="font-weight: 400;">
<el-button style="font-weight: 400;" size="small" type="primary" @click="change(u,index)">编辑</el-button>
<el-button style="font-weight: 400;" size="small" type="success" @click="show(u)">查看</el-button>
<el-button style="font-weight: 400;" size="small" type="danger" @click="del(index)">删除</el-button>
</td>
</tr>
</table>
</div>
<el-dialog
v-model="dialogVisible"
title="用户信息"
width="70%"
>
<el-descriptions :title="user.name">
<el-descriptions-item label="name">{{ user.name }}</el-descriptions-item>
<el-descriptions-item label="password">{{ user.password }}</el-descriptions-item>
<el-descriptions-item label="sex">{{ user.sex }}</el-descriptions-item>
<el-descriptions-item label="address">{{ user.address }}</el-descriptions-item>
</el-descriptions>
</el-dialog>
<el-dialog
v-model="dialogVisible11"
title="编辑信息"
width="50%"
>
<el-descriptions :title="user.name">
<el-descriptions-item label="name">
<el-input v-model="user.name" placeholder="Please input" clearable />
<!-- {{ user.name }} -->
</el-descriptions-item>
<el-descriptions-item label="password">
<el-input v-model="user.password" placeholder="Please input" clearable />
<!-- {{ user.password }} -->
</el-descriptions-item>
<el-descriptions-item label="sex">
<el-input v-model="user.sex" placeholder="Please input" clearable />
<!-- {{ user.sex }} -->
</el-descriptions-item>
<el-descriptions-item label="address">
<el-input v-model="user.address" placeholder="Please input" clearable />
<!-- {{ user.address }} -->
</el-descriptions-item>
</el-descriptions>
<el-button type="primary" @click="okk()">提交</el-button>
</el-dialog>
</template>
<script setup>
import { onMounted ,ref} from "vue";
let dataUser = ref([])
let user = ref({})
onMounted(()=>{
dataUser.value = JSON.parse(localStorage.getItem('userA'))
console.log(dataUser.value)
})
const del = (index)=>{
dataUser.value.splice(index,1)
localStorage.setItem('userA',JSON.stringify(dataUser.value))
}
const show = (u)=>{
dialogVisible.value = true
user = u
}
let indexxx
const change = (u,index)=>{
dialogVisible11.value = true
user = u
indexxx = index
}
const okk = ()=>{
dataUser.value[indexxx] = user
localStorage.setItem('userA',JSON.stringify(dataUser.value))
}
const dialogVisible = ref(false)
const dialogVisible11 = ref(false)
</script>
<style scoped>
input{
width: 100px;
}
.title{
/* font-weight: 450; */
color: #909399;
}
table{
width: 100%;
}
tr{
text-align: center;
border: #f2f2f2 1px solid;
}
td{
width: 200px;
color: #909399;
height:35px;
border: #f0f0f0 1px solid;
padding:0;
margin: 0;
}
tr:hover{
background:#f6f6f6;}
tr:nth-child(even){
background:#f6f6f6;}
</style>
3、编写LookBookView.vue文件(图书查看)
在view目录下新建LookBookView.vue文件
<template>
<div class="book">
<div>
<h1>{{ data['bookName'] }}</h1>
<span>作者:{{ data['authorName'] }}</span>
<br>
<span>发布时间:{{ data['date'] }}</span>
<div v-html="data['textarea']" style="margin-top: 20px;"></div>
</div>
</div>
</template>
<script setup>
import { useRouter, useRoute } from 'vue-router'
import { onMounted ,ref} from "vue";
const router = useRouter()
const route = useRoute()
let data = ref({})
onMounted(()=>{
data.value = JSON.parse(localStorage.getItem('bookA'))[route.query['id']]
console.log(data.value)
// console.log(route.query['id'])
})
</script>
<style scoped>
.book{
display: flex;
width: 100%;
height: 100vh;
padding: 10px;
justify-content: center;
align-items: center;
}
.book>div{
width: 80%;
height: 100vh;
background-color: #ebebeb;
}
span{
font-weight: 550;
}
</style>
总结+源码地址
有些难度、不懂得如果留言达到50出视屏讲解
地址:源码地址