Vue+TS+Element-plus项目

目录

后台管理系统实现

1. 项目搭建                             

1.创建项目

 2.启动项目

3.搭建第三方库element-plus

1.安装

2.完整引入

3.按需导入

2.登录页面

1.下载插件

2.配置路由

3.样式配置

4.设置背景图片

5.表单展示

6.使用ts对数据类型限制

7.封装axios和重置

1.安装

2.封装axios

8.登录的逻辑实现

3.首页

1.首页的头部编写

1.首页布局

2.头部布局 

2.侧边栏的基本样式

3.侧边栏的动态路由

1.创建Goods.vue页面

2.写入Home.vue的子路由中

3.生成动态路由

 4.创建子路由:用户列表User.vue

5.把title渲染到侧边栏

6.index改为动态颜色

7.开启路由模式

8.右边路由内容展示

4.商品列表子路由

1.商品列表的表单搜索页面

2.展示res中的数据

1.复制Form表单

2.规范数据

3.使用

4.外界就可以使用selectData的属性

3.商品列表的数据展示

4.分页处理

1.切割

2.改为显示5页

5.查询功能

1.删除复原

6.封装函数,进行优化

5.用户列表子路由

1.获取用户列表数据

2.定义用户列表的数据类型

3.编写用户列表内容

4.用户列表的查询实现

5.用户列表的编辑页面

1.编辑栏的样式

2.user.ts添加

3.编辑页面数据的获取

 4.编辑功能的实现

6.角色列表

1.创建Role.vue,在router/index.ts添加Role子路由     

2.角色列表的类型规范

3.写数据

4.页面的编写

5.添加角色功能

6.创建Authority.vue权限列表

7.点击修改权限跳转页面

8.Role.vue修改跳转

9.获取路由参数

10.接收authority和id

11.获取数据

12.定义类型

13.接收数据

14.修改功能

7.解决侧边栏刷新后,无高亮

8.解决进入首页,空白

9.路由守卫

10.退出登录


后台管理系统实现

1. 项目搭建                             

1.创建项目

 2.启动项目

cd vue3-ts-demo
npm run serve

3.搭建第三方库element-plus

1.安装

 NPM
npm install element-plus --save

安装 | Element Plus

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>

Form 表单 | Element Plus

Form 表单 | Element Plus

外边距重叠

样式下拉时,出现父样式随着子样式移动的情况,跟上面外边距一起了(外边距重叠)父元素受子元素影响。可以给使用父元素使用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.首页布局

 Container 布局容器 | Element Plus

2.头部布局 

Layout 布局 | Element Plus

//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);
  }
}

Menu 菜单 | Element Plus

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>

Form 表单 | Element Plus

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;
    });

Table 表格 | Element Plus

4.分页处理

<el-pagination layout="prev, pager, next" :total="selectData.count" />

 getGoodsList().then((res) => {
     data.selectData.count=res.data.length // 赋值总条数
    });

Pagination 分页 | Element Plus

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就会发生改变)

Pagination 分页 | Element Plus

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 };

 有选择器

Select 选择器 | Element Plus

有插槽

Table 表格 | Element Plus

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>

Dialog 对话框 | Element Plus

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"
  />

Tree 树形控件 | Element Plus

  • 权限列表接口
  • 地址:/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',
      }"
    />

Tree 树形控件 | Element Plus

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;
  }

https://gitee.com/gru77/vue-ts-demo.git

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值