目录
1.创建Role.vue,在router/index.ts添加Role子路由
后台管理系统实现
1. 项目搭建
1.创建项目
2.启动项目
cd vue3-ts-demo
npm run serve
3.搭建第三方库element-plus
1.安装
NPM
npm install element-plus --save
2.完整引入
如果你对打包后的文件大小不是很在乎,那么使用完整导入会更方便。
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//createApp(App).use(router).use(ElementPlus).mount('#app')
// 如果报错的话,设置类型为any
const app:any=createApp(App)
app.use(router).use(ElementPlus).mount('#app')
Unexpected any. Specify a different type @typescript-eslint/no-explicit-any 关闭报错即可
//package.json
"rules": {
"@typescript-eslint/no-explicit-any":["off"]
}
3.按需导入
您需要使用额外的插件来导入要使用的组件。
自动导入推荐
1.首先你需要安装unplugin-vue-components
和 unplugin-auto-import
这两款插件
npm install -D unplugin-vue-components unplugin-auto-import
2.然后把下列代码插入到你的 Vite
或 Webpack
的配置文件中
Webpack--->可以在vue.config.js中添加webpack.config.js即可
const { defineConfig } = require('@vue/cli-service')
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack:{//配置的是webpack的内容
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
}
})
//main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
修改配置了之后,要重启项目
角色列表消息框出现问题,使用完整引入方法
2.登录页面
1.下载插件
自动生成Vue3代码:Vue VSCode Snippets(下载失败:使用管理员运行vscode)
输入vb即可快速生成模板
2.配置路由
router/index.ts
{
path: '/login',
name: 'login',
component: () => import('../views/LoginView.vue'),
},
3.样式配置
App.vue
<template>
<div>
<router-view />
</div>
</template>
<style lang="scss">
* {
margin: 0;
padding: 0;
}
html,
body,
#app {
width: 100%;
height: 100%;
}
</style>
4.设置背景图片
<template>
<div class="login-box"></div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
setup() {
return {};
},
});
</script>
<style lang='scss' scoped>
.login-box {
width: 100%;
height: 100vh; //100%没有显示图片
background: url("../assets/bg.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
background-attachment: fixed;
}
</style>
5.表单展示
//Login.vue
<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
export default defineComponent({
setup() {
const data = reactive({
ruleForm: {
username: "",
password: "",
},
rules: {
username: [
{
required: true,
message: "请输入账号",
trigger: "blur",
},
{
min: 3,
max: 10,
message: "账号的长度在3-10之间",
trigger: "blur",
},
],
password: [
{
required: true,
message: "请输入密码",
trigger: "blur",
},
{
min: 3,
max: 10,
message: "密码的长度在3-10之间",
trigger: "blur",
},
],
},
});
// 解构出来,就可以直接使用
return { ...toRefs(data) };
},
});
</script>
<template>
<div class="login-box">
<el-form
ref="ruleFormRef"
:model="ruleForm"
status-icon
:rules="rules"
label-width="80px"
class="demo-ruleForm"
>
<h2>后台管理系统</h2>
<el-form-item label="账号:" prop="username">
<el-input v-model="ruleForm.username" autocomplete="off" />
</el-form-item>
<el-form-item label="密码:" prop="password">
<el-input
v-model="ruleForm.password"
type="password"
autocomplete="off"
/>
</el-form-item>
<el-form-item>
<el-button
class="loginBtn"
type="primary"
@click="submitForm(ruleFormRef)"
>登录</el-button
>
<el-button class="loginBtn" @click="resetForm(ruleFormRef)"
>重置</el-button
>
</el-form-item>
</el-form>
</div>
</template>
<style lang='scss' scoped>
.login-box {
width: 100%;
height: 100vh; //100%没有显示图片
background: url("../assets/bg.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
background-attachment: fixed;
text-align: center;
// 外边距重叠问题
// padding: 1px;
overflow: hidden;
.demo-ruleForm {
width: 500px;
margin: 200px auto;
background-color: rgb(232, 250, 223);
padding: 40px;
border-radius: 20px;
}
.loginBtn {
width: 48%;
}
h2 {
margin-bottom: 10px;
}
}
</style>
外边距重叠
样式下拉时,出现父样式随着子样式移动的情况,跟上面外边距一起了(外边距重叠)父元素受子元素影响。可以给使用父元素使用overflow:hidden;也可以给父元素设置padding一定距离。
6.使用ts对数据类型限制
type文件夹:规范整个项目里面所对应的数据的类型。
//src/type/login.ts
export interface LoginFormInt {
username: string
password: string
}
export class LoginData {
ruleForm: LoginFormInt = {
username:"",
password:""
}
}
//Login.vue
import {LoginData} from '../type/login'
export default defineComponent({
setup() {
const data = reactive(new LoginData());
const rules={//rules是不会变的,可以拿出来
username: [
{
required: true,
message: "请输入账号",
trigger: "blur",
},
{...
return { ...toRefs(data),rules };
}
})
//重置
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
data.ruleForm.username = "";
data.ruleForm.password = "";
};
// data.ruleForm.username=1//不能将类型“number”分配给类型“string”。
// data.ruleForm.username=""//才可以(ts的类型推断)
// 解构出来,就可以直接使用
return { ...toRefs(data),rules,resetForm };
7.封装axios和重置
1.安装
npm install axios
2.封装axios
request/index.ts:封装axios的文件夹
import axios from 'axios'
// 创建axios实例
const service = axios.create({
baseURL:
'https://www.fastmock.site/mock/bf1fcb3c2e2945669c2c8d0ecb8009b8/api',
timeout: 5000, //超时值
headers: {
//会返回一个token,需要把token保存到头部里面
'Content-Type': 'application/json;charset=utf-8',
},
})
// 请求拦截
service.interceptors.request.use((config) => {
// headers没有,需要给它一个空的对象
config.headers = config.headers || {}
// 如果没有token,不用在headers加入token(确保它登录了);有就加token
if (localStorage.getItem('token')) {
config.headers.token = localStorage.getItem('token') || ''
}
return config
})
// 响应拦截
service.interceptors.response.use((res)=>{
// 获取code,进行类型定义
const code:number=res.data.code
// 状态码code
if(code!=200){
// 信息错误
return Promise.reject(res.data)
}
return res.data
},(err)=>{//打印错误信息
console.log(err);
})
// 发送请求需要整个实例,把实例暴露出去
export default service
8.登录的逻辑实现
src/request/api.ts:针对发送的请求做处理。
- 登录接口
- 地址:/login
- 方式:post
- 参数:username && password
//app.ts
import service from '.'
interface LoginData {
username: string
password: string
}
//登录接口
// 给data做类型规范
export function login(data: LoginData) {
// 返回service,通过axios实例发送请求
return service({
url: '/login',
method: 'post',
data,
})
}
发送请求
import { ref } from "vue";
import type { FormInstance } from 'element-plus'
import {login} from '../request/api'
import { useRouter } from "vue-router";
// 登录
const ruleFormRef = ref<FormInstance>()
const router=useRouter()//-->Router
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
// 对表单内容进行验证
//valid布尔类型,为true表示验证成功,反之
formEl.validate((valid) => {
if (valid) {
// console.log("submit!");
login(data.ruleForm).then((res) => {
console.log(res);
// 将token进行保存
localStorage.setItem("token", res.data.token);
// 跳转页面,首页
// $router.push vue2
router.push("/");
});
} else {
console.log("error submit!");
return false;
}
});
// console.log(formEl);
};
return {ruleFormRef, submitForm};
3.首页
1.首页的头部编写
1.首页布局
2.头部布局
//Home.vue
<template>
<div class="home">
<el-container>
<el-header>
<el-row :gutter="20">
<el-col :span="4">
<img src="../assets/logo.png" alt="" class="logo" />
</el-col>
<el-col :span="16">
<h2>后台管理系统</h2>
</el-col>
<el-col :span="4"> <span class="quit-login">退出系统</span> </el-col>
</el-row>
</el-header>
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-main>Main</el-main>
</el-container>
</el-container>
</div>
</template>
<style lang="scss" scoped>
.el-header {
height: 80px;
background-color: rgb(136, 220, 67);
.logo {
height: 80px;
}
h2,
.quit-login {
text-align: center;
height: 80px;
line-height: 80px;
color: rgb(244, 244, 244);
}
}
</style>
2.侧边栏的基本样式
<!-- 侧边栏 -->
<el-aside width="200px">
<el-menu
active-text-color="#ffd04b"
background-color="#545c64"
class="el-menu-vertical-demo"
default-active="2"
text-color="#fff"
>
<el-menu-item index="2">
<span>商品列表</span>
</el-menu-item>
</el-menu>
</el-aside>
.el-aside {
.el-menu {
// 拿到窗口到底部部分100vh-上面后台管理系统盒子高度80px
height: calc(100vh - 80px);
}
}
3.侧边栏的动态路由
根据侧边栏去渲染(展示)对应的内容,在右边区域。点击商品列表,展示商品列表的数据。
需要写Home.vue的子路由,因为只有当它是子路由的时候,才可以在当前页面里面去渲染另一个页面。
1.创建Goods.vue页面
2.写入Home.vue的子路由中
//src/router/index.ts
{
path: '/',
name: 'home',
component: HomeView,
children: [
{
path: 'goods',
name: 'goods',
// 懒加载
component: () => import('../views/GoodsView.vue'),
},
],
},
3.生成动态路由
根据首页的子路由,去展示侧边栏的内容。
//src/router/index.ts
name: 'goods',
meta: {
isShow: true,
},//根据meta来设置,有哪些子路由需要展示
4.创建子路由:用户列表User.vue
//Home.vue
import { useRouter } from "vue-router";
setup() {
const router = useRouter();
console.log(router.getRoutes()); //Array(5)
//拿取到页面所有的路由,发现childern里的值是不确定的,会随着添加而添加,所以需要直接拿到isShow
const list = router.getRoutes().filter((v) => v.meta.isShow); //filter不能在html模块用 vue3
console.log(list);//Array(2)
//渲染页面,需要用到list
return {list}
},
//src/router/index.ts
meta: {
isShow: true,
title:"商品列表"
},
meta: {
isShow: true,
title:"用户列表"
},
5.把title渲染到侧边栏
<el-menu-item index="2" v-for="item in list" :key="item.path">
<span>{{item.meta.title}}</span>
</el-menu-item>
6.index改为动态颜色
index为文字点击颜色。
7.开启路由模式
router:开启路由模式,通过el-menu-item 的index来进行跳转。点击哪一个路由,就会跳转到所对应的index路径去。
<el-menu
router
>
<el-menu-item :index="item.path" >
</el-menu-item>
8.右边路由内容展示
组件哪个地方需要展示出来,需要设置路由出口。
<!-- 右边路由内容 -->
<el-main>
<router-view></router-view>
</el-main>
4.商品列表子路由
1.商品列表的表单搜索页面
- 商品列表接口
- 地址:/getGoodsList
- 方式:get
src/request/api.ts
// 商品列表接口
export function getGoodsList(){
return service({
url:"/getGoodsList",
method:"get"
})
}
执行整个函数getGoodsList,发送请求
import { getGoodsList } from "../request/api";
export default defineComponent({
setup() {
getGoodsList().then((res) => {
console.log(res);
});
2.展示res中的数据
1.复制Form表单
//Goods.vue
<template>
<div>
<div class="select-box">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item label="标题">
<el-input v-model="formInline.user" placeholder="请输入关键字" />
</el-form-item>
<el-form-item label="详情">
<el-input v-model="formInline.user" placeholder="请输入关键字" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
2.规范数据
//src/type/goods.ts
// 定义接口
export interface ListInt {
userId: number
id: number
title: string
introduce: string
}
interface selectDataInt {
title: string
introduce: string
page: number //页码
count: number //总条数
pagesize: number //默认一页显示几条
}
// 定义class类
export class InitData {
// selectData针对搜索表单去定义的
selectData: selectDataInt = {
title:"",
introduce:"",
page:1,
count:0,
pagesize:10//设置默认展示10条数据
}
list: ListInt[] = [] //展示的内容的数据,接收后台返回的数据
}
3.使用
//GoodsView.vue
import { defineComponent, reactive, toRefs } from "vue";
//引入
import {InitData} from '../type/goods'
//实例化
const data =reactive(new InitData())
//返回data
return {...toRefs(data)};
4.外界就可以使用selectData的属性
<el-form :inline="true" :model="selectData" class="demo-form-inline">
<el-form-item label="标题">
<el-input v-model="selectData.title" placeholder="请输入关键字" />
</el-form-item>
<el-form-item label="详情">
<el-input v-model="selectData.introduce" placeholder="请输入关键字" />
</el-form-item>
</el-form>
3.商品列表的数据展示
//Goods.vue
<el-table :data="list" border style="width: 100%">
<el-table-column prop="id" label="ID" width="180" />
<el-table-column prop="title" label="标题" width="180" />
<el-table-column prop="introduce" label="详情" />
</el-table>
getGoodsList().then((res) => {
// console.log(res);
// data.list接受后台返回过来的数据
data.list = res.data;
});
4.分页处理
<el-pagination layout="prev, pager, next" :total="selectData.count" />
getGoodsList().then((res) => {
data.selectData.count=res.data.length // 赋值总条数
});
1.切割
<el-table :data="dataList.comList" border style="width: 100%">
import { computed} from "vue";
// list会随着currentChange,sizeChange而更改
const dataList = reactive({
comList: computed(() => {
// 切割
// 1-- [1-10]
// 2-- [11-20]
// 3-- [21-30]
// (slice从0开始的(截取下标),page从1开始的)
// 第一条-1 * 展示的页数
return data.list.slice(
(data.selectData.page - 1) * data.selectData.pagesize, //page=1-->0,page=2-->10
data.selectData.page * data.selectData.pagesize
); //page=1-->10,page=2-->20
// 包头不包尾,0-9,10-19
}),
});
const currentChange = (page: number) => {
data.selectData.page = page;
};
const sizeChange = (pagesize: number) => {
data.selectData.pagesize = pagesize;
};
return { currentChange, sizeChange,dataList };
点击第n页的时候,触发currentChange事件,把当前的page传过来,传过来后把page赋值给 data.selectData.page,这个page就和要展示的数据有关系,(computed:当依赖值发生改变时,属性comList就会发生改变)
2.改为显示5页
//Goods.vue
<el-pagination
:total="selectData.count*2" //总页数*2
/>
//type/goods.ts
pagesize: 5, //设置默认展示10条数据
5.查询功能
import { InitData,ListInt } from "../type/goods";
//查询
const onSubmit = () => {
// console.log(data.selectData.title);
// console.log(data.selectData.introduce);
let arr: ListInt[] = []; //定义数组,用来接收查询过后的要展示的数据
// 将查询的数据,赋值给它
if (data.selectData.title || data.selectData.introduce) {
//判断两个是否其中一个有值
if (data.selectData.title) {
// value:数组list里的每一个对象
arr = data.list.filter((value) => {//将过滤出来的数组赋值给arr
// 查找输入进来的关键字,不等于-1,说明有这个关键字
return value.title.indexOf(data.selectData.title)!==-1;
});
}
if (data.selectData.introduce) {
arr = data.list.filter((value) => {
return value.introduce.indexOf(data.selectData.introduce)!==-1;
});
}
}else{
// 输入为空,让arr等于原本数组即可
arr=data.list
}
data.selectData.count=arr.length//分页总数改变
// 过滤之后的数组
data.list=arr
};
return { onSubmit };
arr过滤之后的数组,赋值给list,data.list.slice发生改变,comList也就发生改变,:data="dataList.comList"就发生改变了。分页的总数也要发生改变。
1.删除复原
删除搜索后,需要恢复原状。监听
import { watch } from "vue";
//监听多个值,以数组形式存在
// 监听输入框的两个属性
watch(
[() => data.selectData.title, () => data.selectData.introduce],
() => {
// 只有属性发生改变,进行判断
if (data.selectData.title == "" && data.selectData.introduce == "") {
// 为空,则重新赋值(1.重新获取数组);2.也可以重新定义数组,去接收它
getGoodsList().then((res) => {
data.list = res.data; //赋值数据
data.selectData.count = res.data.length; // 赋值总条数
});
}
}
);
6.封装函数,进行优化
原先为直接调用,优化为在生命周期去调用,获取数据。
onMounted(() => {
getGoods()
});
// 封装到一个函数里面
const getGoods = () => {
getGoodsList().then((res) => {
// console.log(res);
// data.list接受后台返回过来的数据
data.list = res.data; //赋值数据
data.selectData.count = res.data.length; // 赋值总条数
});
};
watch(
[() => data.selectData.title, () => data.selectData.introduce],
() => {
// 只有属性发生改变,进行判断
if (data.selectData.title == "" && data.selectData.introduce == "") {
// 为空,则重新赋值(1.重新获取数组);2.也可以重新定义数组,去接收它
getGoods()
}
}
);
5.用户列表子路由
1.获取用户列表数据
- 用户列表接口
- 地址:/getUserList
- 方式:get
- 角色列表接口
- 地址:/getRoleList
- 方式:get
//request/api.ts
// 用户列表接口
export function getUserList() {
return service({
url: '/getUserList',
method: 'get',
})
}
// 角色列表接口
export function getRoleList() {
return service({
url: '/getRoleList',
method: 'get',
})
}
import { defineComponent, onMounted } from "vue";
import { getUserList, getRoleList } from "../request/api";
export default defineComponent({
setup() {
onMounted(() => {
getUser();
getRole();
});
const getUser = () => {
getUserList().then((res) => {
console.log(res);
});
};
const getRole = () => {
getRoleList().then((res) => {
console.log(res);
});
};
return {};
},
});
2.定义用户列表的数据类型
用接口来规范对象。
//src/type/user.ts
//id: 1
//nickName: "小明"
//role: (2) [{…}, {…}]
//userName: "小明"
export interface ListInt {
id: number
nickName: string
role: RoleInt[]
userName: string
}
// 为role写的接口
interface RoleInt {
role: number
roleName: string
}
// 查询,通过role选择管理员
interface SelectDataInt {
role: number
nickName: string
}
interface RoleListInt {
authority: number[]
roleId: number
roleName: string
}
// 写类,得到一个对象
export class InitData {
selectData: SelectDataInt = {
nickName: '',
role: 0,
}
list: ListInt[] = [] //接收用户信息的列表
roleList: RoleListInt[] = [] // 接收角色信息的列表
}
3.编写用户列表内容
//User.vue
import {InitData} from '../type/user'
const data=reactive(new InitData())
return {...toRefs(data)};
角色:选择器选择时管理员还是普通用户。
import { defineComponent, onMounted, reactive, toRefs } from "vue";
import { getUserList, getRoleList } from "../request/api";
setup() {
const data = reactive(new InitData());
onMounted(() => {
getUser();
getRole();
});
const getUser = () => {
getUserList().then((res) => {
console.log(res);
data.list=res.data
});
};
const getRole = () => {
getRoleList().then((res) => {
console.log(res);
data.roleList=res.data
});
};
},
<div class="select-box">
<el-form :inline="true" :model="selectData" class="demo-form-inline">
<el-form-item label="姓名">
<el-input v-model="selectData.nickName" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="角色">
<el-select
v-model="selectData.role"
class="m-2"
placeholder="请选择"
size="large"
>
<!-- 提供默认的,如果为0是显示 -->
<el-option label="全部" :value="0" />
<el-option
v-for="item in roleList"
:key="item.roleId"
:label="item.roleName"
:value="item.roleId"
/>
<!-- 根据value来决定显示的是管理员还是普通用户 -->
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="list" border style="width: 100%">
<el-table-column prop="id" label="ID" width="180" />
<el-table-column prop="nickName" label="姓名" width="180" />
<!-- role:数组里面的内容进行展示,使用插槽 -->
<el-table-column prop="role" label="角色">
<!-- 展示管理员还是用户信息 -->
<template #default="scope">
<el-button
v-for="item in scope.row.role"
:key="item.role"
// type="text"//vue3不支持,使用link
link
size="small"
>
{{ item.roleName }}
</el-button>
</template>
</el-table-column>
</el-table>
了解scope
<el-button type="primary" size="small" @click="deleteRow(scope)">
Remove
</el-button>
const deleteRow = (row: any) => {
console.log(row);
};
return { deleteRow };
有选择器
有插槽
4.用户列表的查询实现
User查询跟Goods的查询相似,复制过来进行更改即可。
import { InitData,ListInt } from "../type/user";
import { defineComponent, onMounted, reactive, toRefs, watch } from "vue";
// 查询
const onSubmit = () => {
// console.log(data.selectData.role);
// console.log(data.selectData.nickName);
let arr: ListInt[] = []; //定义数组,用来接收查询过后的要展示的数据
// 将查询的数据,赋值给它
if (data.selectData.nickName || data.selectData.role) {
//判断两个是否其中一个有值
if (data.selectData.nickName) {
// value:数组list里的每一个对象
arr = data.list.filter((value) => {
//将过滤出来的数组赋值给arr
// 查找输入进来的关键字,不等于-1,说明有这个关键字
return value.nickName.indexOf(data.selectData.nickName) !== -1;
});
}
// 输入小明,又要有管理员,即两个都要成立才行,所以这里过滤的值,是上面已经过滤过的值
if (data.selectData.role) {
arr = (data.selectData.nickName ? arr : data.list).filter((value) => {
// 将过滤出来的数组赋值给arr
// 将role跟选择器中的role进行对比,如果相等就返回出去
return value.role.find(
(item) => item.role === data.selectData.role
);
});
}
} else {
// 输入为空,让arr等于原本数组即可
arr = data.list;
}
// 过滤之后的数组
data.list = arr;
};
// 监听输入框的两个属性
watch([() => data.selectData.nickName, () => data.selectData.role], () => {
// 只有属性发生改变,进行判断
if (data.selectData.nickName == "" || data.selectData.role == 0) {
getUser();
}
});
return { ...toRefs(data), deleteRow, onSubmit };
5.用户列表的编辑页面
1.编辑栏的样式
<el-table :data="list" border style="width: 100%">
<el-table-column prop="id" label="ID" width="180" />
<el-table-column prop="nickName" label="姓名" width="180" />
<!-- role:数组里面的内容进行展示,使用插槽 -->
<el-table-column prop="role" label="角色">
<!-- 展示管理员还是用户信息 -->
<template #default="scope">
<el-button
v-for="item in scope.row.role"
:key="item.role"
link
size="small"
>
{{ item.roleName }}
</el-button>
</template>
</el-table-column>
<el-table-column prop="role" label="操作">
<template #default="scope">
<el-button
link
type="primary"
size="small"
@click="changeUser(scope.row)"
>
编辑
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog v-model="isShow" title="编辑信息">
<el-form :model="active">
<el-form-item label="姓名" label-width="50px">
<el-input v-model="active.nickName" autocomplete="off" />
</el-form-item>
<el-form-item label="角色" label-width="50px">
<el-select multiple v-model="active.role" placeholder="请选择角色">
<el-option
v-for="item in roleList"
:key="item.roleId"
:label="item.roleName"
:value="item.roleId"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogFormVisible = false"
>Confirm</el-button
>
</span>
</template>
</el-dialog>
2.user.ts添加
// 接收名字和角色
interface ActiveInt {
id: number
nickName: string
// [1,2]
role: number[]
userName: string
}
export class InitData {
isShow = false
active: ActiveInt = {
//选中的对象
id: 0,
nickName: '',
role: [],
userName: '',
}
3.编辑页面数据的获取
const changeUser = (row: ListInt) => {
console.log(row);
data.active = {
id: row.id,
nickName: row.nickName,
userName: row.userName,
role: row.role.map((value) => value.role),
};
data.isShow = true;
};
return { changeUser };
4.编辑功能的实现
const changeUser = (row: ListInt) => {
role: row.role.map((value: any) => value.role || value.roleId),
};
data.isShow = true;
};
const updateUser = () => {
// console.log(data.active);
let obj: any = data.list.find((value) => value.id == data.active.id);
obj.nickName = data.active.nickName;
// data.active.role-->[1,2],选中管理员和用户
// roleList-->roleId-->1,2 既选中管理员又用户.这里有1,上面才会有1.
obj.role = data.roleList.filter(
(value) => data.active.role.indexOf(value.roleId) !== -1
);
console.log(obj.role); //改变之后的值拿到了
// 将选中的内容进行赋值
data.list.forEach((item, i) => {
if (item.id == obj.id) {
data.list[i] = obj;
}
});
data.isShow = false;
};
return { changeUser, updateUser };
实际开发时,点击更改是会调用数据库里面的值,进行更改的。这里是更改的渲染。
6.角色列表
1.创建Role.vue,在router/index.ts添加Role子路由
{
path: '/role',
name: 'role',
meta: {
isShow: true,
title: '角色列表',
},
component: () => import('../views/RoleView.vue'),
},
// 角色列表接口
export function getRoleList() {
return service({
url: '/getRoleList',
method: 'get',
})
}
import { getRoleList } from "@/request/api";
export default defineComponent({
setup() {
onMounted(() => {
getRoleList().then((res) => {
console.log(res);
});
});
return {};
},
});
2.角色列表的类型规范
role.ts
export interface ListInt{
authority:number[],
roleId:number,
roleName:string
}
export class InitData{
list:ListInt[]=[]
}
3.写数据
import { defineComponent, onMounted, reactive } from "vue";
import { getRoleList } from "@/request/api";
import { InitData } from "@/type/role";
export default defineComponent({
setup() {
const data=reactive(new InitData())
onMounted(() => {
getRoleList().then((res) => {
console.log(res);
data.list=res.data
});
});
return {...toRefs(data) };
},
});
4.页面的编写
<template>
<div>
<el-form :inline="true" class="demo-form-inline">
<el-form-item>
<el-button type="primary" @click="addRole">添加角色</el-button>
</el-form-item>
</el-form>
<el-table :data="list" border style="width: 100%">
<el-table-column prop="roleId" label="ID" width="180" />
<el-table-column prop="roleName" label="角色名" width="180" />
<el-table-column prop="role" label="操作">
<template #default="scope">
<el-button link size="small" @click="changeRole(scope.row)">
修改权限
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
5.添加角色功能
import { ElMessage, ElMessageBox } from "element-plus";
export default defineComponent({
setup() {
const addRole = () => {
ElMessageBox.prompt("请输入角色名称", "添加", {
confirmButtonText: "确定",
cancelButtonText: "取消",
})
.then(({ value }) => {
//value表示你在输入框中填写的值
if (value) {
//判断输入框中有值,就将对应的值添加到列表中
// 数组的长度会发生改变,
data.list.push({
roleId: data.list.length + 1,
roleName: value,
authority: [],
});
}
ElMessage({
type: "success",
message: `${value}角色添加成功`,
});
})
.catch(() => {
ElMessage({
type: "info",
message: "取消操作",
});
});
};
return { ...toRefs(data), addRole };
},
});
弹框出错,改用全局引入element-plus,无问题
MessageBox 消息弹框 | Element Plus
6.创建Authority.vue权限列表
是首页的子路由。
{
path: '/authority',
name: 'authority',
meta: {
isShow: false,
title: '权限列表',
},
component: () => import('../views/AuthorityView.vue'),
},
7.点击修改权限跳转页面
import { InitData, ListInt } from "../type/role";
import { useRouter } from "vue-router";
const router=useRouter()
const changeRole = (row: ListInt) => {
router.push({
path: "authority",
query: {
id: row.roleId,
// 把数组变成字符串,给它分隔开
authority: row.authority.join(','),
},
});
};
return { changeRole };
http://localhost:8080/authority?id=1&authority=1,2,4,5,6,7,8,9,11,13,14,15,16
8.Role.vue修改跳转
const changeRole = (row: ListInt) => {
router.push({
name: "authority", //name跟path后面的值设的是一样的
params: {
id: row.roleId,
authority: row.authority,
},
});
};
9.获取路由参数
// 当前活跃的路由,路径显示的是哪一个,通过这个函数去获取,就可以得到哪一个对象
import { useRoute } from "vue-router";
export default defineComponent({
setup() {
const route = useRoute();
console.log(route);
return {};
},
});
10.接收authority和id
authority.ts
export class InitData{
id:number
authority:number[]
// 赋值,这两个是从params中来的,需要进行传参
constructor(id:number,authority:number[]){
this.id=id
this.authority=authority
}
}
import { defineComponent, reactive, toRefs } from "vue";
// 当前活跃的路由,路径显示的是哪一个,通过这个函数去获取,就可以得到哪一个对象
import { useRoute } from "vue-router";
import { InitData } from "../type/authority";
export default defineComponent({
setup() {
const route = useRoute();
console.log(route);
// 不知道传过来的属性是什么,所以使用any
const params: any = route.params;
const data = reactive(new InitData(params.id, params.authority));
return { ...toRefs(data) };
},
});
<el-tree
:data="data"
show-checkbox
node-key="id"
:default-expanded-keys="[2, 3]"
:default-checked-keys="[5]"
:props="defaultProps"
/>
- 权限列表接口
- 地址:/getAuthorityList
- 方式:get
api.ts
// 权限列表接口
export function getAuthorityList() {
return service({
url: '/getAuthorityList',
method: 'get',
})
}
11.获取数据
import { getAuthorityList } from "../request/api";
onMounted(() => {
getAuthorityList().then((res) => {
console.log(res);
});
});
12.定义类型
export interface ListInt {
name: string
roleId: number
// 是可选的?
roleList?: ListInt[]
viewRole?: string
}
export class InitData {
list:ListInt[]=[]
}
13.接收数据
onMounted(() => {
getAuthorityList().then((res) => {
console.log(res);
data.list=res.data
});
});
父级勾选上了,子级就会自动勾选上,这里不需要,即设置它们不互相关联
<el-tree
:data="list"
show-checkbox
node-key="roleId"
:check-strictly="true"
:default-checked-keys="authority"
:props="{
children: 'roleList',
label: 'name',
}"
/>
14.修改功能
定义Ref,可以通过Ref去获取DOM的注册信息
//authority.ts
treeRef:any
//Authority.vue
<el-tree
ref="treeRef"
<el-button @click="changeAuthority">确认修改</el-button>
const changeAuthority = () => {
console.log(data.treeRef);
console.log(data.treeRef.getCheckedKeys());
};
return { changeAuthority };
获取选中的状态,可以拿到拿一下是选中的
// 获取升序
console.log(data.treeRef.getCheckedKeys().sort(function(a:number,b:number){return a-b}));
// 传给后台,后台去进行修改
7.解决侧边栏刷新后,无高亮
<el-menu
:default-active="active"
>
import { useRouter,useRoute } from "vue-router";
setup() {
const route = useRoute();
return { active:route.path };
},
8.解决进入首页,空白
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
redirect:"goods",//设置重定向
}
]
9.路由守卫
如果没有登录,需要登录后才能进入详情页面。设置全局路由守卫
//src/router/index.ts
router.beforeEach((to, from, next) => {
// 给token定义类型,登录了的话为string,未登录则是null
// 从localStorge里进行获取
const token: string | null = localStorage.getItem('token')
// 如果没有token,跳转到登录页面去;去往的路径本来就是登录页面的话,不需要跳转,直接进入token页面。
// 如果去往的页面不是token页面,并且没有token;则让他跳转到token页面
if (!token && to.path !== '/login') {
next('/login')
}
// 如果有token,则让它next就可以了
else {
next()
}
})
10.退出登录
将HomeView.vue退出换成按钮形式
<el-col :span="4" class="col-token">
<el-button @click="delToken">退出登录</el-button>
</el-col>
const delToken =()=>{
// 删除token
localStorage.removeItem('token')
// 跳转到登陆页面去
router.push('/login')
}
return { delToken };
.col-token {
height: 80px;
line-height: 80px;
}