Vue框架学习(后台管理系统)

1、vue介绍

 Vue.js - 渐进式 JavaScript 框架 | Vue.js

2、node等环境安装

node安装参考博文:

Node.js安装与配置(详细步骤)_node安装_普通网友的博客-CSDN博客

npm install -g cnpm --registry=https://registry.npm.taobao.org

注意:需要把路径放在环境变量中

cnpm i yarn -g

3、项目创建

vue create flask_works_li_front

添加上面一行代码,可以避免每编译修改代码实时启动审查!

HomeView.vue:

<template>
  <h1> Hello World </h1>
  <p v-text="name"></p>
  <!-- v-text的简写 -->
  <p>{
   
   {name}}</p>
</template>

<script>
  import {reactive,toRefs} from "vue"
  export default {
    name: "home",
    setup() {
      // beforeCreate和created 两个生命周期
      const data = reactive(
        {
          name: "小红",
          age: 25
        }
      )
      return {
        ...toRefs(data)
      }
    }
  }
</script>

4、模板语法 Trim

HomeView.vue:

<template>
  <h1> Hello World </h1>
  <p v-text="name"></p>
  <!-- v-text的简写 -->
  <p>{
   
   {name}}</p>

  <!-- v-html -->
  <p>{
   
   {info}}</P>
  <p v-html="info"></p>

  <!-- v-bind:属性名="变量名" 绑定动态的标签属性  简写:  :属性名="变量名"-->
  <p v-bind:data="dataVul">我有属性data</p>

  <!-- class类名绑定 -->
  <p :class="{'red': isRed}">我是红色</p>
  <p :class="{'red': !isRed}">我是红色</p>

  <!-- class类名绑定 可以叠加使用-->
  <p class="text" :class="{'red': isRed}">我是红色</p>

  <!-- 判断语句 v-if  false的时候 是元素未渲染在页面-->
  <!-- v-show :false的时候 是样式上的隐藏-->
  <p v-if="isTrue">我是if存在</p>
  <p v-show="isTrue">我是show展示</p>
  <p v-if="isFalse">我是if存在</p>
  <p v-show="isFalse">我是show展示</p>

  <p v-if="isFalse">if</p>
  <p v-else>else</p>
</template>

<script>
  import {reactive,toRefs} from "vue"
  export default {
    name: "home",
    setup() {
      // beforeCreate和created 两个生命周期
      const data = reactive(
        {
          name: "小红",
          age: 25,
          info: "<i>我是斜体字</i>",
          dataVul: 2,
          isRed: true,
          isTrue: true,
          isFalse: false
        }
      )
      return {
        ...toRefs(data)
      }
    }
  }
</script>
<style>
  .red{
    color: red
  }
</style>

5、for循环

HomeView.vue:

<template>
  <h1> Hello World </h1>
  <p v-text="name"></p>
  <!-- v-text的简写 -->
  <p>{
   
   {name}}</p>

  <!-- v-html -->
  <p>{
   
   {info}}</P>
  <p v-html="info"></p>

  <!-- v-bind:属性名="变量名" 绑定动态的标签属性  简写:  :属性名="变量名"-->
  <p v-bind:data="dataVul">我有属性data</p>

  <!-- class类名绑定 -->
  <p :class="{'red': isRed}">我是红色</p>
  <p :class="{'red': !isRed}">我是红色</p>

  <!-- class类名绑定 可以叠加使用-->
  <p class="text" :class="{'red': isRed}">我是红色</p>

  <!-- 判断语句 v-if  false的时候 是元素未渲染在页面-->
  <!-- v-show :false的时候 是样式上的隐藏-->
  <p v-if="isTrue">我是if存在</p>
  <p v-show="isTrue">我是show展示</p>
  <p v-if="isFalse">我是if存在</p>
  <p v-show="isFalse">我是show展示</p>

  <p v-if="isFalse">if</p>
  <p v-else>else</p>

  <!-- for循环 -->
  <ul>
    <!-- 循环此数组userList -->
    <!-- v-for="(每一个对象的变量, 下标) in 数组" -->
    <li v-for="(item, index) in userList" :key="index" >
      学生姓名:{
   
   {item.username}}
      学生年龄:{
   
   {item.userage}}
    </li>
  </ul>
</template>

<script>
  import {reactive,toRefs} from "vue"
  export default {
    name: "home",
    setup() {
      // beforeCreate和created 两个生命周期
      const data = reactive(
        {
          name: "小红",
          age: 25,
          info: "<i>我是斜体字</i>",
          dataVul: 2,
          isRed: true,
          isTrue: true,
          isFalse: false,
          userList:[
            {
              username: "小红",
              userage:15
            },
            {
              username: "小花",
              userage:16
            },
            {
              username: "小文",
              userage:17
            }
          ]
        }
      )
      return {
        ...toRefs(data)
      }
    }
  }
</script>
<style>
  .red{
    color: red
  }
</style>

6、生命周期

AboutView.vue

<template>
  <div>
    <h1>vue3的生命周期</h1>
    <div id="dom">{
   
   {msg}}--{
   
   {num}}</div>
  </div>
</template>
<script>
  import {reactive,toRefs, onBeforeMount, onMounted, onBeforeUpdate, onUpdated} from "vue"
  export default {
    name: "about",
    setup() {
      const data = reactive(
        {
          msg: "您好!",
          msg2: "hello",
          num: 0
        }
      )
      // 数据渲染前
      onBeforeMount(()=>{
        console.log("onBeforeMount", document.querySelector("#dom"))
      })
      // 数据渲染后
      onMounted(()=>{
        console.log("onMounted", document.querySelector("#dom"))
        setTimeout(()=>{
          data.msg="hello world"
          //data.msg2="hello world"
        }, 2000)
      })
      // dom更新前
      onBeforeUpdate(()=>{
        console.log("onBeforeUpdate")
      })
      // dom更新前
      onUpdated(()=>{
        console.log("onUpdated")
        //data.num += 1 // 触发死循环
      })

      return {
        ...toRefs(data)
      }
    }
  }
</script>

7、事件绑定

AboutView.vue:

<template>
  <div>
    <h1>vue3的生命周期</h1>
    <div id="dom">{
   
   {msg}}--{
   
   {num}}</div>

    <!-- v-on:事件名="事件方法"  绑定事件  简写@:事件名="事件方法" -->
    <!-- 事件及方法直接声明在 setup内 -->
    <button v-on:click="handleClick">click me</button>

    <hr>
    <!-- v-model 双向绑定 -->
    <!-- input:输入事件
      blur:失去焦点
      focus:获取焦点
      change:内容更改
    -->
    <input type="text" placeholder="请输入姓名" v-model="userName"><br/>
    <input type="text" placeholder="请输入电话" v-model="userPhone" @focus="handleFocus" @blur="handleBlur" @input="handleInput"><br/>
    <textarea placeholder="请输入您的建议" cols="30" rows="10" v-model="userInput"></textarea>
    <p>{
   
   {userName}}--{
   
   {userInput}}</P>
    <button @click="submit">提交</button>
  </div>
</template>
<script>
  import {reactive,toRefs, onBeforeMount, onMounted, onBeforeUpdate, onUpdated} from "vue"
  export default {
    name: "about",
    setup() {
      const data = reactive(
        {
          msg: "您好!",
          msg2: "hello",
          num: 0,
          userName: "",
          userInput: "",
          userPhone: ""
        }
      )
      // 数据渲染前
      onBeforeMount(()=>{
        console.log("onBeforeMount", document.querySelector("#dom"))
      })
      // 数据渲染后
      onMounted(()=>{
        console.log("onMounted", document.querySelector("#dom"))
        setTimeout(()=>{
          data.msg="hello world"
          //data.msg2="hello world"
        }, 2000)
      })
      // dom更新前
      onBeforeUpdate(()=>{
        console.log("onBeforeUpdate")
      })
      // dom更新前
      onUpdated(()=>{
        console.log("onUpdated")
        //data.num += 1 // 触发死循环
      })
      //事件及方法
      const handleClick=()=>{
        alert("您好")
      }

      const submit=()=>{
        alert(`${data.userName}的建议是${data.userInput}`)  //注意:这儿不是单引号,是Tab键上面的符号
      }

      const handleFocus=()=>{
        console.log("获取焦点了")
      }

      const handleBlur=()=>{
        console.log("失去焦点了")
        if(!data.userPhone){
          alert("手机号必填!")
        }
      }

      const handleInput=()=>{
        //正则验证手机号
        if(!/^[1][3,4,5,6,7,8][0-9]{9}$/.test(data.userPhone)){
          console.log("不符合手机号!")
        }
      }

      return {
        ...toRefs(data),
        handleClick,  //所有的事件及方法都需要return,否则捕捉不到
        submit,
        handleFocus,
        handleBlur,
        handleInput
      }
    }
  }
</script>

8、路由配置

createWebHistory模式:

createWebHashHistory模式:

index.js:

import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import ABC from '../views/ABC.vue'
// 哈希路由和历史路由

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    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(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  },
  {
    path: '/abc',  //路由地址
    name: 'ABC',  //路由名字
    //component: ABC //页面组件
    component: () => import('../views/ABC.vue')  //建议使用懒加载的模式,大量路由下可以提交效率
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

ABC.vue:

<template>
  <div>
    <h1>路由ABC测试</h1>
  </div>
</template>
<script>
  import {reactive,toRefs} from "vue"
  export default {
    name: "abc"
  }
</script>

App.vue:

<template>
  <nav>
    <!-- 相当于a标签,to="路由path" -->
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </nav>
  <!-- router-view:展示路由对应的组件内容 -->
  <router-view/>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
</style>

9、LayOut

router.index.js:

import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
//import ABC from '../views/ABC.vue'
// 哈希路由和历史路由

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    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(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  },
  {
    path: '/index',  //路由地址
    name: 'layOut',  //路由名字
    //component: layOut //页面组件
    component: () => import('../views/LayOut/LayOut.vue')  //建议使用懒加载的模式,大量路由下可以提交效率
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

一个 Vue 3 UI 框架 | Element Plus

APP.vue:

<template>
  <!--
  <nav>
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </nav>
  -->
  <!-- router-view:展示路由对应的组件内容 -->
  <router-view/>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
/* flex布局类名 */
.flex-float{
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.flex{
  display: flex;
  align-items: center;
}
</style>

main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
// element icon注册
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.use(store).use(router).use(ElementPlus).mount('#app')

router.index.js

import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
//import ABC from '../views/ABC.vue'
// 哈希路由和历史路由

const routes = [
  /* {
    path: '/',
    name: 'home',
    component: HomeView
  }, */
  {
    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(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  },
  {
    path: '/',  //路由地址
    name: 'layout',  //路由名字
    redirect: '/users',
    //component: LayOut //页面组件
    component: () => import('../views/LayOut/LayOut.vue'),  //建议使用懒加载的模式,大量路由下可以提交效率
    // 子路由|嵌套路由
    children: [
      {
        path: '/users',
        name: 'users',
        component:()=>import("../views/pages/usersList.vue")
      },
      {
        path: "/roles",
        name: "roles",
        component:()=>import("../views/pages/rolesList.vue")
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

LayOut.vue:

<template>
  <div class="common-layout">
    <el-container>
      <el-header class="common-header flex-float">
        <div class="flex">
          <img class="logo" src="../../assets/logo.png" alt />
          <h1 class="title">后台管理系统</h1>
        </div>
        <el-button type="danger">退出</el-button>
      </el-header>
      <el-container>
        <el-aside class="common-aside" width="200px">
          <el-menu
            background-color="none"
            text-color="#fff"
            :router="true"
          >
            <el-sub-menu index="1">
              <template #title>
                <el-icon><Avatar /></el-icon>
                <span>账号管理</span>
              </template>
              <el-menu-item-group>
                <el-menu-item index="/users">账号列表</el-menu-item>
              </el-menu-item-group>
            </el-sub-menu>
            <el-sub-menu index="2">
              <template #title>
                <el-icon><UserFilled /></el-icon>
                <span>角色管理</span>
              </template>
              <el-menu-item-group>
                <el-menu-item index="/roles">角色列表</el-menu-item>
              </el-menu-item-group>
            </el-sub-menu>
          </el-menu>
        </el-aside>
        <el-main>
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
import { reactive, toRefs } from "vue";
export default {
  name: "layout"
};
</script>
<style>
.el-container {
  height: 100vh;
  overflow: hidden;
}
.common-header {
  background: rgb(26, 30, 36);
  display: flex;
}
.common-aside {
  background: rgb(59, 117, 199);
}
.logo {
  width: 80px;
}
.title {
  color: #ffffff;
}
</style>

usersList.vue:

<template>
  <div>
    <h1>用户列表</h1>
  </div>
</template>
<script>
  export default {
    name: "users"
  }
</script>

rolesList.vue:

<template>
  <div>
    <h1>角色列表</h1>
  </div>
</template>
<script>
  export default {
    name: "roles"
  }
</script>

10、登录框页面

router.index.js

import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
//import ABC from '../views/ABC.vue'
// 哈希路由和历史路由

const routes = [
  // 登录页面
  {
    path: '/login',
    name: 'login',
    component: () => import("../views/pages/login.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(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  },
  {
    path: '/page',  //路由地址
    name: 'layout',  //路由名字
    // redirect: '/users',
    //component: LayOut //页面组件
    component: () => import('../views/LayOut/LayOut.vue'),  //建议使用懒加载的模式,大量路由下可以提交效率
    // 子路由|嵌套路由
    children: [
      {
        path: '/users',
        name: 'users',
        component:()=>import("../views/pages/usersList.vue")
      },
      {
        path: "/roles",
        name: "roles",
        component:()=>import("../views/pages/rolesList.vue")
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

login.vue:

<template>
  <div class="login_wrap">
    <div class="form_wrap">
      <el-form ref="formRef" :model="loginData" label-width="80px" class="demo-dynamic">
        <el-form-item
          prop="username"
          label="用户名"
          :rules="[
        {
          required: true,
          message: '此项为必填项',
          trigger: 'blur',
        },
      ]"
        >
          <el-input v-model="loginData.username" />
        </el-form-item>
        <el-form-item
          prop="password"
          label="密码"
          :rules="[
        {
          required: true,
          message: '此项为必填项',
          trigger: 'blur',
        },
      ]"
        >
          <el-input type="password" v-model="loginData.password" />
        </el-form-item>
      </el-form>
      <el-button type="primary" class="login_btn">登录</el-button>
      <el-button type="primary" class="reset_btn">重置</el-button>
    </div>
  </div>
</template>
<script>
import { reactive, toRefs } from "vue";
export default {
  name: "login",
  setup() {
    const data = reactive({
      loginData: {
        username: "",
        password: ""
      }
    });
    return {
      ...toRefs(data)
    };
  }
};
</script>
<style scoped>
.login_wrap {
  width: 100%;
  height: 100vh;
  background: rgb(75, 187, 187);
  position: relative;
}
.form_wrap {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #ffffff;
  padding: 30px 50px;
  border-radius: 5px;
}
.login_btn{
    display: inline-block;
    /* margin: 10px auto; */
}
.reset_btn{
    display: inline-block;
    /* margin: 10px auto; */
    margin-right: 10px;
}
</style>>

11、vuex

store.index.js:

import { createStore } from 'vuex'
import number from "./state/num.state.js"

export default createStore({
  // 数据比较多时,分模块
  modules: {
    number
  }
})

store.state.num.state.js:

export default {
    namespaced: true,
    // 全局的状态初始值
    state: {
        count: 1,
    },
    // 计算state,获取对应的值
    getters: {
        countStatus(status) {
            return status.count <= 1
        }
    },
    // 更新状态的方法-更新state的唯一方法,commit mutations
    mutations: {
        setCount(state, num) {
            state.count = num
        }
    },
    // 可以异步操作,可以返回promise,更改数据还是传递到mutations去更改
    actions: {
        setCountPromise(context, num) {
            return new Promise((resolve, reject) => {
                if (num > 100) {
                    reject("值不能大于100")
                } else {
                    context.commit("setCount", num)
                    resolve()
                }
            })
        }
    },
}

views.page.login.vue:

<template>
  <div class="login_wrap">
    <div class="form_wrap">
      <el-form ref="formRef" :model="loginData" label-width="80px" class="demo-dynamic">
        <el-form-item
          prop="username"
          label="用户名"
          :rules="[
        {
          required: true,
          message: '此项为必填项',
          trigger: 'blur',
        },
      ]"
        >
          <el-input v-model="loginData.username" />
        </el-form-item>
        <el-form-item
          prop="password"
          label="密码"
          :rules="[
        {
          required: true,
          message: '此项为必填项',
          trigger: 'blur',
        },
      ]"
        >
          <el-input type="password" v-model="loginData.password" />
        </el-form-item>
      </el-form>
      <el-button type="primary" class="login_btn" @click="handleLogin">登录</el-button>
      <el-button type="primary" class="reset_btn">重置</el-button>
      <p>{
   
   {num}}</p>
    </div>
  </div>
</template>
<script>
import { reactive, toRefs } from "vue";
import {useStore } from "vuex";
export default {
  name: "login",
  setup() {
    const store = useStore()
    const count = store.state.count
    const data = reactive({
      loginData: {
        username: "",
        password: ""
      },
      num: count,
      numStatus: store.getters.countStatus
    });
    console.log("修改前getters:", store.getters['number/countStatus'])  // 变量不符合语法时,使用中括号就可以了
    const handleLogin=()=>{
      // store.commit('number/setCount', 100);
      store.dispatch("number/setCountPromise", 101).then(res=>{
        alert("修改成功")
      }).catch(err=>{
        alert(err)
      })
      console.log(store.state.number.count)
      console.log("修改后getters:", store.getters['number/countStatus'])  // 变量不符合语法时,使用中括号就可以了
    }
    return {
      ...toRefs(data),
      handleLogin
    };
  }
};
</script>
<style scoped>
.login_wrap {
  width: 100%;
  height: 100vh;
  background: rgb(75, 187, 187);
  position: relative;
}
.form_wrap {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #ffffff;
  padding: 30px 50px;
  border-radius: 5px;
}
.login_btn{
    display: inline-block;
    /* margin: 10px auto; */
}
.reset_btn{
    display: inline-block;
    /* margin: 10px auto; */
    margin-right: 10px;
}
</style>>

12、路由守卫及登录状态存储

store.index.js:

import { createStore } from 'vuex'
import number from "./state/num.state.js"
import uInfo from "./state/userinfo.state.js"

export default createStore({
  // 数据比较多时,分模块
  modules: {
    number,
    uInfo
  }
})

router.index.js:

import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import store from "../store/index.js"
//import ABC from '../views/ABC.vue'
// 哈希路由和历史路由

const routes = [
  // 登录页面
  {
    path: '/login',
    name: 'login',
    component: () => import("../views/pages/login.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(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  },
  {
    path: '/page',  //路由地址
    name: 'layout',  //路由名字
    // redirect: '/users',
    //component: LayOut //页面组件
    component: () => import('../views/LayOut/LayOut.vue'),  //建议使用懒加载的模式,大量路由下可以提交效率
    // 子路由|嵌套路由
    children: [
      {
        path: '/users',
        name: 'users',
        component:()=>import("../views/pages/usersList.vue")
      },
      {
        path: "/roles",
        name: "roles",
        component:()=>import("../views/pages/rolesList.vue")
      }
    ]
  }
]

// 生成历史路由
const router = createRouter({
  history: createWebHistory(),
  routes
})

router.beforeEach((to,from,next)=>{
  /**
   * to:从哪个页面
   * from:到哪个页面
   * next:只有执行next()页面才会进行跳转
   */
  // 判断用户是否登录
  console.log("store", store.state.uInfo)
  const uInfo = store.state.uInfo.userInfo
  if(!uInfo.username){
    // 未登录,跳转到login
    if(to.path=="/login"){
      next()
      return
    }
    next("/login")
  }else{
    next()
  }
})

// 暴露路由对象
export default router

views.LayOut.LayOut.vue:

<template>
  <div class="common-layout">
    <el-container>
      <el-header class="common-header flex-float">
        <div class="flex">
          <img class="logo" src="../../assets/logo.png" alt />
          <h1 class="title">后台管理系统</h1>
        </div>
        <el-button type="danger" @click="loginOut">退出</el-button>
      </el-header>
      <el-container>
        <el-aside class="common-aside" width="200px">
          <el-menu
            background-color="none"
            text-color="#fff"
            :router="true"
          >
            <el-sub-menu index="1">
              <template #title>
                <el-icon><Avatar /></el-icon>
                <span>账号管理</span>
              </template>
              <el-menu-item-group>
                <el-menu-item index="/users">账号列表</el-menu-item>
              </el-menu-item-group>
            </el-sub-menu>
            <el-sub-menu index="2">
              <template #title>
                <el-icon><UserFilled /></el-icon>
                <span>角色管理</span>
              </template>
              <el-menu-item-group>
                <el-menu-item index="/roles">角色列表</el-menu-item>
              </el-menu-item-group>
            </el-sub-menu>
          </el-menu>
        </el-aside>
        <el-main>
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
// import { reactive, toRefs } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router"
export default {
  name: "layout",
  setup(){
    const store = useStore()
    const router = useRouter()
    const loginOut=()=>{
      localStorage.removeItem("loginData")
      store.commit("setUserInfo", {});
      router.push({
        path:"/login"
      })
    }
    return{
      loginOut
    }
  }
};
</script>
<style>
.el-container {
  height: 100vh;
  overflow: hidden;
}
.common-header {
  background: rgb(26, 30, 36);
  display: flex;
}
.common-aside {
  background: rgb(59, 117, 199);
}
.logo {
  width: 80px;
}
.title {
  color: #ffffff;
}
</style>

views.pages.login.vue:

<template>
  <div class="login_wrap">
    <div class="form_wrap">
      <el-form ref="formRef" :model="loginData" label-width="80px" class="demo-dynamic">
        <el-form-item
          prop="username"
          label="用户名"
          :rules="[
        {
          required: true,
          message: '此项为必填项',
          trigger: 'blur',
        },
      ]"
        >
          <el-input v-model="loginData.username" />
        </el-form-item>
        <el-form-item
          prop="password"
          label="密码"
          :rules="[
        {
          required: true,
          message: '此项为必填项',
          trigger: 'blur',
        },
      ]"
        >
          <el-input type="password" v-model="loginData.password" />
        </el-form-item>
      </el-form>
      <el-button type="primary" class="login_btn" @click="handleLogin">登录</el-button>
      <el-button type="primary" class="reset_btn">重置</el-button>
      <!-- <p>{
   
   {num}}</p> -->
    </div>
  </div>
</template>
<script>
import { reactive, toRefs } from "vue";
import {useStore } from "vuex";
import {useRouter} from "vue-router"
export default {
  name: "login",
  setup() {
    const store = useStore()
    const router = useRouter()
    const count = store.state.count
    const data = reactive({
      loginData: {
        username: "",
        password: ""
      },
      num: count,
    });
    const handleLogin=()=>{
      store.commit('setUserInfo', data.loginData)
      localStorage.setItem("loginData", JSON.stringify(data.loginData))
      // 跳转/user
      router.push({
        path:"/users"
      })
    }
    // vuex 修改语法
    // console.log("修改前getters:", store.getters['number/countStatus'])  // 变量不符合语法时,使用中括号就可以了
    // const handleLogin=()=>{
    //   // store.commit('number/setCount', 100);
    //   store.dispatch("number/setCountPromise", 101).then(res=>{
    //     alert("修改成功")
    //   }).catch(err=>{
    //     alert(err)
    //   })
    //   console.log(store.state.number.count)
    //   console.log("修改后getters:", store.getters['number/countStatus'])  // 变量不符合语法时,使用中括号就可以了
    // }
    return {
      ...toRefs(data),
      handleLogin
    };
  }
};
</script>
<style scoped>
.login_wrap {
  width: 100%;
  height: 100vh;
  background: rgb(75, 187, 187);
  position: relative;
}
.form_wrap {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #ffffff;
  padding: 30px 50px;
  border-radius: 5px;
}
.login_btn{
    display: inline-block;
    /* margin: 10px auto; */
}
.reset_btn{
    display: inline-block;
    /* margin: 10px auto; */
    margin-right: 10px;
}
</style>>

store.state.userinfo.state.js:

export default{
    state:{
        // userInfo:{}
        userInfo:(localStorage.getItem("loginData")&&JSON.parse(localStorage.getItem("loginData")))||{}
    },
    mutations:{
        setUserInfo(state,uInfo){
            state.userInfo=uInfo
        }
    }
}

13、axios封装

新建:

utils.service.js:

import axios from "axios"
import { ElLoading } from "element-plus"
import { ElMessage } from "element-plus"

// 使用create创建axios实例
let loadingObj = null
const Service = axios.create({
    timeout: 8000,
    baseURL: "http://XXX",
    headers:{
        "Content-type": "application/json;charset=utf-8"
    }
})
// 请求拦截-增加loading,对请求做统一处理
Service.interceptors.request.use(config=>{
    loadingObj=ElLoading.service({
        lock: true,
        text: "Loading",
        background: "rgba(0, 0, 0, 0.7)",
    })
    return config
})

// 响应拦截-对返回值做统一处理
Service.interceptors.response.use(response=>{
    loadingObj.close()
    return  response.data

},error=>{
    loadingObj.close()
    ElMessage({
        message: "服务器错误",
        type: "error",
        duration: "2000"
    })
})

14、登录接口及用户列表展示

utils.service.js:

import axios from "axios"
import { ElLoading } from "element-plus"
import { ElMessage } from "element-plus"
import store from "../store/index.js"

// 使用create创建axios实例
let loadingObj = null
const Service = axios.create({
    timeout: 8000,
    baseURL: "http://127.0.0.1:5000/",
    headers:{
        "Content-type": "application/json;charset=utf-8",
        // "Authorization": store.state.uInfo.userInfo.token
        "Authorization": "token"
    }
})
// 请求拦截-增加loading,对请求做统一处理
Service.interceptors.request.use(config=>{
    loadingObj=ElLoading.service({
        lock: true,
        text: "Loading",
        background: "rgba(0, 0, 0, 0.7)",
    })
    return config
})

// 响应拦截-对返回值做统一处理
Service.interceptors.response.use(response=>{
    loadingObj.close()
    const data = response.data
    if(!data.data){
        // 请求出错
        ElMessage.error(data.meta.msg || "服务器出错")
        return data
    }
    return  data

},error=>{
    loadingObj.close()
    ElMessage({
        message: "服务器错误",
        type: "error",
        duration: "2000"
    })
})

// post请求
export const post=config=>{
    return Service({
        ...config,
        method: "post",
        data: config.data
    })
}

// get请求
export const get=config=>{
    return Service({
        ...config,
        method: "get",
        params: config.data
    })
}

views.pages.index.vue:

<template>
  <div>
    <h1>欢迎来到用户管理系统</h1>
  </div>
</template>
<script>
  export default {
    name: "index"
  }
</script>

router.index.js:

import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import store from "../store/index.js"
//import ABC from '../views/ABC.vue'
// 哈希路由和历史路由

const routes = [
  // 登录页面
  {
    path: '/login',
    name: 'login',
    component: () => import("../views/pages/login.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(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  },
  {
    path: '/',  //路由地址
    name: 'layout',  //路由名字
    redirect: '/index',
    //component: LayOut //页面组件
    component: () => import('../views/LayOut/LayOut.vue'),  //建议使用懒加载的模式,大量路由下可以提交效率
    // 子路由|嵌套路由
    children: [
      {
        path: '/index',
        name: 'index',
        component:()=>import("../views/pages/index.vue")
      },
      {
        path: '/users',
        name: 'users',
        component:()=>import("../views/pages/usersList.vue")
      },
      {
        path: "/roles",
        name: "roles",
        component:()=>import("../views/pages/rolesList.vue")
      }
    ]
  }
]

// 生成历史路由
const router = createRouter({
  history: createWebHistory(),
  routes
})

router.beforeEach((to,from,next)=>{
  /**
   * to:从哪个页面
   * from:到哪个页面
   * next:只有执行next()页面才会进行跳转
   */
  // 判断用户是否登录
  console.log("store", store.state.uInfo)
  const uInfo = store.state.uInfo.userInfo
  if(!uInfo.username){
    // 未登录,跳转到login
    if(to.path=="/login"){
      next()
      return
    }
    next("/login")
  }else{
    next()
  }
})

// 暴露路由对象
export default router

views.pages.userList.vue:

<template>
  <div>
    <!-- 面包屑-->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">账号管理</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/">账号列表</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 白色内容区域 -->
    <div class="page_content">
      <div class="input_box">
        <div class="flex">
          <el-input v-model="keyWord" placeholder="搜索关键字" class="input-with-select">
            <template #append>
              <el-button @click="searchList">
                <el-icon>
                  <Search />
                </el-icon>
              </el-button>
            </template>
          </el-input>
          <el-button type="primary" @click="addUser">新建用户</el-button>
        </div>
      </div>
      <!-- 表格 -->
      <!--
          el-table 的 data:要展示的数据数组
          el-table-column: 列 prop每条数据的对应属性
          label: 列标题
          scope.row:相当于一条数据
      -->
      <el-table :data="userList" style="width: 100%">
        <el-table-column prop="username" label="姓名" width="180" />
        <el-table-column prop="role_name" label="角色" width="180" />
        <el-table-column prop="age" label="年龄" width="180" />
        <el-table-column prop="email" label="邮箱" width="180" />
        <el-table-column prop="mobile" label="手机号" />
        <el-table-column prop="ms_state" label="状态">
          <template #default="scope">
            <el-switch v-model="scope.row.ms_state" />
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button type="primary">编辑</el-button>
            <el-button type="danger">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>
<script>
import { toRefs, reactive } from "vue";
import { userListApi } from "@/utils/request.js";
export default {
  name: "users",
  setup() {
    const data = reactive({
      keyWord: "",
      searchParams: {
        query: "",
        pagesize: 5,
        pagenum: 1
      },
      userList: []
    });
    const searchList = () => {
      userListApi(data.searchParams).then(res => {
        if (res.data) {
          //
          // console.log("用户数据", res);
          data.userList = res.data.users;
        }
      });
    };
    const addUser = () => {};
    // 方法初始化
    searchList();
    return {
      ...toRefs(data),
      searchList,
      addUser
    };
  }
};
</script>
<style scoped>
.input_box {
  width: 250px;
  margin-right: 15x;
}
</style>

views.pages.roleList.vue:

<template>
  <div>
    <!-- 面包屑-->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">角色管理</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/">角色列表</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 白色内容区域 -->
    <div class="page_content">
      <div class="input_box">
        <div class="flex">
          <el-input v-model="input3" placeholder="搜索关键字" class="input-with-select">
            <template #append>
              <el-button>
                <el-icon>
                  <Search />
                </el-icon>
              </el-button>
            </template>
          </el-input>
          <el-button type="primary">新建角色</el-button>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "roles"
};
</script>
<style scoped>
.input_box {
  width: 280px;
  margin-right: 15px;
}
</style>

views.pages.login.vue:

<template>
  <div class="login_wrap">
    <div class="form_wrap">
      <el-form ref="formRef" :model="loginData" label-width="80px" class="demo-dynamic">
        <el-form-item
          prop="username"
          label="用户名"
          :rules="[
        {
          required: true,
          message: '此项为必填项',
          trigger: 'blur',
        },
      ]"
        >
          <el-input v-model="loginData.username" />
        </el-form-item>
        <el-form-item
          prop="password"
          label="密码"
          :rules="[
        {
          required: true,
          message: '此项为必填项',
          trigger: 'blur',
        },
      ]"
        >
          <el-input type="password" v-model="loginData.password" />
        </el-form-item>
      </el-form>
      <el-button type="primary" class="login_btn" @click="handleLogin">登录</el-button>
      <el-button type="primary" class="reset_btn">重置</el-button>
      <!-- <p>{
   
   {num}}</p> -->
    </div>
  </div>
</template>
<script>
import { reactive, toRefs } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { loginApi } from "@/utils/request.js";
export default {
  name: "login",
  setup() {
    const store = useStore();
    const router = useRouter();
    const count = store.state.count;
    const data = reactive({
      loginData: {
        username: "",
        password: ""
      },
      num: count
    });
    const handleLogin=()=> {
      // 请求后台接口
      loginApi(data.loginData).then(res => {
        if(res.data) {
          store.commit("setUserInfo", res.data);
          localStorage.setItem("loginData", JSON.stringify(data.loginData));
          // 跳转/user
          router.push({
            path: "/index"
          });
        }
      });
    };
    // vuex 修改语法
    // console.log("修改前getters:", store.getters['number/countStatus'])  // 变量不符合语法时,使用中括号就可以了
    // const handleLogin=()=>{
    //   // store.commit('number/setCount', 100);
    //   store.dispatch("number/setCountPromise", 101).then(res=>{
    //     alert("修改成功")
    //   }).catch(err=>{
    //     alert(err)
    //   })
    //   console.log(store.state.number.count)
    //   console.log("修改后getters:", store.getters['number/countStatus'])  // 变量不符合语法时,使用中括号就可以了
    // }
    return {
      ...toRefs(data),
      handleLogin
    };
  }
};
</script>
<style scoped>
.login_wrap {
  width: 100%;
  height: 100vh;
  background: rgb(75, 187, 187);
  position: relative;
}
.form_wrap {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #ffffff;
  padding: 30px 50px;
  border-radius: 5px;
}
.login_btn {
  display: inline-block;
  /* margin: 10px auto; */
}
.reset_btn {
  display: inline-block;
  /* margin: 10px auto; */
  margin-right: 10px;
}
</style>>

utils.request.js:

import { post, get } from "./service"

export const loginApi=data=>{
    return post({
        url: "/login",
        data
    })
}
// 获取用户列表
export const userListApi=data=>{
    return get({
        url: "/users",
        data
    })
}

App.vue:

<template>
  <!--
  <nav>
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </nav>
  -->
  <!-- router-view:展示路由对应的组件内容 -->
  <router-view/>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
/* flex布局类名 */
.flex-float{
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.flex{
  display: flex;
  align-items: center;
}
.page_content{
  box-sizing: border-box;
  display: block;
  width: 100%;
  padding: 20px;
  background: #ffffff;
  margin-top: 30px;
}
</style>

views.LayOut.LayOut.vue:

<template>
  <div class="common-layout">
    <el-container>
      <el-header class="common-header flex-float">
        <div class="flex">
          <img class="logo" src="../../assets/logo.png" alt />
          <h1 class="title">后台管理系统</h1>
        </div>
        <el-button type="danger" @click="loginOut">退出</el-button>
      </el-header>
      <el-container>
        <el-aside class="common-aside" width="200px">
          <el-menu
            background-color="none"
            text-color="#fff"
            :router="true"
          >
            <el-sub-menu index="1">
              <template #title>
                <el-icon><Avatar /></el-icon>
                <span>账号管理</span>
              </template>
              <el-menu-item-group>
                <el-menu-item index="/users">账号列表</el-menu-item>
              </el-menu-item-group>
            </el-sub-menu>
            <el-sub-menu index="2">
              <template #title>
                <el-icon><UserFilled /></el-icon>
                <span>角色管理</span>
              </template>
              <el-menu-item-group>
                <el-menu-item index="/roles">角色列表</el-menu-item>
              </el-menu-item-group>
            </el-sub-menu>
          </el-menu>
        </el-aside>
        <el-main>
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
// import { reactive, toRefs } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router"
export default {
  name: "layout",
  setup(){
    const store = useStore()
    const router = useRouter()
    const loginOut=()=>{
      localStorage.removeItem("loginData")
      store.commit("setUserInfo", {});
      router.push({
        path:"/login"
      })
    }
    return{
      loginOut
    }
  }
};
</script>
<style>
.el-container {
  height: 100vh;
  overflow: hidden;
}
.common-header {
  background: rgb(26, 30, 36);
  display: flex;
}
.common-aside {
  background: rgb(59, 117, 199);
}
.logo {
  width: 80px;
}
.title {
  color: #ffffff;
}
.el-main{
  background: #efefef;
}
</style>

15、新建用户表单及正则验证

utils.request.js:

import { post, get } from "./service"

export const loginApi=data=>{
    return post({
        url: "/login",
        data
    })
}
// 获取用户列表
export const userListApi=data=>{
    return get({
        url: "/users",
        data
    })
}
// 新增用户
export const userAddApi=data=>{
    return post({
        url: "/users",
        data
    })
}

views.pages.userList.vue:

<template>
  <div>
    <!-- 面包屑-->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">账号管理</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/">账号列表</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 白色内容区域 -->
    <div class="page_content">
      <div class="input_box">
        <div class="flex">
          <el-input v-model="keyWord" placeholder="搜索关键字" class="input-with-select">
            <template #append>
              <el-button @click="searchList">
                <el-icon>
                  <Search />
                </el-icon>
              </el-button>
            </template>
          </el-input>
          <el-button type="primary" @click="addUser">新建用户</el-button>
        </div>
      </div>
      <!-- 表格 -->
      <!--
          el-table 的 data:要展示的数据数组
          el-table-column: 列 prop每条数据的对应属性
          label: 列标题
          scope.row:相当于一条数据
      -->
      <el-table :data="userList" style="width: 100%">
        <el-table-column prop="username" label="姓名" width="180" />
        <el-table-column prop="role_name" label="角色" width="180" />
        <el-table-column prop="age" label="年龄" width="180" />
        <el-table-column prop="email" label="邮箱" width="180" />
        <el-table-column prop="mobile" label="手机号" />
        <el-table-column prop="ms_state" label="状态">
          <template #default="scope">
            <el-switch v-model="scope.row.ms_state" />
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button type="primary">编辑</el-button>
            <el-button type="danger">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <!-- 弹窗 -->
    <el-dialog v-model="dialogFormVisible" title="新建用户">
      <!-- 表单 -->
      <el-form ref="userForm" :model="formData" :rules="rules">
        <el-form-item label="用户名称" prop="username">
          <el-input v-model="formData.username" placeholder="请输入用户名" />
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="formData.password" placeholder="请输入密码" />
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData.email" placeholder="请输入邮箱" />
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData.mobile" placeholder="请输入手机号" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitForm(userForm)">确定</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script>
import { toRefs, reactive, ref } from "vue";
import { userListApi, userAddApi } from "@/utils/request.js";
export default {
  name: "users",
  setup() {
    const data = reactive({
      keyWord: "",
      searchParams: {
        query: "",
        pagesize: 5,
        pagenum: 1
      },
      userList: [],
      dialogFormVisible: false,
      formData: {
        username: "",
        password: "",
        email: "",
        mobile: ""
      },
      rules: {
        username: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        password: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        email: [
          {
            required: false,
            pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
            message: "请填写正确的邮箱",
            trigger: "blur"
          }
        ],
        mobile: [
          {
            required: false,
            pattern: /^[1][3,4,5,6,7,8,9][0-9]{9}$/,
            message: "请填写正确的手机号",
            trigger: "blur"
          }
        ]
      }
    });
    const searchList = () => {
      userListApi(data.searchParams).then(res => {
        if (res.data) {
          //
          // console.log("用户数据", res);
          data.userList = res.data.users;
        }
      });
    };
    const addUser = () => {
      data.dialogFormVisible = true;
    };
    const submitForm = formEl => {
      // validate
      formEl.validate(res => {
        if (!res) {
          return;
        }
        // 表单验证通过请求接口
        // alert("通过")
        userAddApi(data.formData).then(res => {
          if (res.data) {
            // 隐藏弹框
            data.dialogFormVisible = false;
            // 清空弹框数据
            data.formData = {
              username: "",
              password: "",
              email: "",
              mobile: ""
            };
            // 重新更新列表
            searchList()
          }
        });
      });
    };
    // 方法初始化
    searchList();
    const userForm = ref();
    return {
      ...toRefs(data),
      searchList,
      addUser,
      submitForm,
      userForm
    };
  }
};
</script>
<style scoped>
.input_box {
  width: 250px;
  margin-right: 15x;
}
</style>

 16、用户列表分页

userList.vue:

<template>
  <div>
    <!-- 面包屑-->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">账号管理</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/">账号列表</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 白色内容区域 -->
    <div class="page_content">
      <div class="input_box">
        <div class="flex">
          <el-input v-model="keyWord" placeholder="搜索关键字" class="input-with-select">
            <template #append>
              <el-button @click="searchList">
                <el-icon>
                  <Search />
                </el-icon>
              </el-button>
            </template>
          </el-input>
          <el-button type="primary" @click="addUser">新建用户</el-button>
        </div>
      </div>
      <!-- 表格 -->
      <!--
          el-table 的 data:要展示的数据数组
          el-table-column: 列 prop每条数据的对应属性
          label: 列标题
          scope.row:相当于一条数据
      -->
      <el-table :data="userList" style="width: 100%">
        <el-table-column prop="username" label="姓名" width="180" />
        <el-table-column prop="role_name" label="角色" width="180" />
        <el-table-column prop="age" label="年龄" width="180" />
        <el-table-column prop="email" label="邮箱" width="180" />
        <el-table-column prop="mobile" label="手机号" />
        <el-table-column prop="ms_state" label="状态">
          <template #default="scope">
            <el-switch v-model="scope.row.ms_state" />
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button type="primary">编辑</el-button>
            <el-button type="danger">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <el-pagination 
      background 
      layout="prev, pager, sizes, next, jumper, ->, total"
      v-model:currentPage="searchParams.pagenum" 
      v-model:page-size="searchParams.pagesize" 
      :page-sizes="[2,3,5,10,20]"
      :total="total"
      @size-change="searchList"
      @current-change="searchList"
      />
    </div>
    <!-- 弹窗 -->
    <el-dialog v-model="dialogFormVisible" title="新建用户">
      <!-- 表单 -->
      <el-form ref="userForm" :model="formData" :rules="rules">
        <el-form-item label="用户名称" prop="username">
          <el-input v-model="formData.username" placeholder="请输入用户名" />
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="formData.password" placeholder="请输入密码" />
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData.email" placeholder="请输入邮箱" />
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData.mobile" placeholder="请输入手机号" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitForm(userForm)">确定</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script>
import { toRefs, reactive, ref } from "vue";
import { userListApi, userAddApi } from "@/utils/request.js";
export default {
  name: "users",
  setup() {
    const data = reactive({
      keyWord: "",
      searchParams: {
        query: "",
        pagesize: 5,
        pagenum: 2
      },
      total: 0,
      userList: [],
      dialogFormVisible: false,
      formData: {
        username: "",
        password: "",
        email: "",
        mobile: ""
      },
      rules: {
        username: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        password: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        email: [
          {
            required: false,
            pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
            message: "请填写正确的邮箱",
            trigger: "blur"
          }
        ],
        mobile: [
          {
            required: false,
            pattern: /^[1][3,4,5,6,7,8,9][0-9]{9}$/,
            message: "请填写正确的手机号",
            trigger: "blur"
          }
        ]
      }
    });
    const searchList = () => {
      userListApi(data.searchParams).then(res => {
        if (res.data) {
          //
          // console.log("用户数据", res);
          data.userList = res.data.users;
          data.total = res.data.total
        }
      });
    };
    const addUser = () => {
      data.dialogFormVisible = true;
    };
    const submitForm = formEl => {
      // validate
      formEl.validate(res => {
        if (!res) {
          return;
        }
        // 表单验证通过请求接口
        // alert("通过")
        userAddApi(data.formData).then(res => {
          if (res.data) {
            // 隐藏弹框
            data.dialogFormVisible = false;
            // 清空弹框数据
            data.formData = {
              username: "",
              password: "",
              email: "",
              mobile: ""
            };
            // 重新更新列表
            searchList()
          }
        });
      });
    };
    // 方法初始化
    searchList();
    const userForm = ref();
    return {
      ...toRefs(data),
      searchList,
      addUser,
      submitForm,
      userForm
    };
  }
};
</script>
<style scoped>
.input_box {
  width: 250px;
  margin-right: 15x;
}
</style>

17、用户状态更改

utils.service.js:

import axios from "axios"
import { ElLoading } from "element-plus"
import { ElMessage } from "element-plus"
import store from "../store/index.js"

// 使用create创建axios实例
let loadingObj = null
const Service = axios.create({
    timeout: 8000,
    baseURL: "http://127.0.0.1:5000/",
    headers:{
        "Content-type": "application/json;charset=utf-8",
        // "Authorization": store.state.uInfo.userInfo.token
        "Authorization": "token"
    }
})
// 请求拦截-增加loading,对请求做统一处理
Service.interceptors.request.use(config=>{
    loadingObj=ElLoading.service({
        lock: true,
        text: "Loading",
        background: "rgba(0, 0, 0, 0.7)",
    })
    return config
})

// 响应拦截-对返回值做统一处理
Service.interceptors.response.use(response=>{
    loadingObj.close()
    const data = response.data
    if(!data.data){
        // 请求出错
        ElMessage.error(data.meta.msg || "服务器出错")
        return data
    }
    return  data

},error=>{
    loadingObj.close()
    ElMessage({
        message: "服务器错误",
        type: "error",
        duration: "2000"
    })
})

// post请求
export const post=config=>{
    return Service({
        ...config,
        method: "post",
        data: config.data
    })
}

// get请求
export const get=config=>{
    return Service({
        ...config,
        method: "get",
        params: config.data
    })
}

// put请求
export const put=config=>{
    return Service({
        ...config,
        method: "put",
        data: config.data
    })
}

utils.request.js:

import { post, get, put } from "./service"

export const loginApi=data=>{
    return post({
        url: "/login",
        data
    })
}
// 获取用户列表
export const userListApi=data=>{
    return get({
        url: "/users",
        data
    })
}
// 新增用户
export const userAddApi=data=>{
    return post({
        url: "/users",
        data
    })
}
// 更改用户状态
export const userChangeStateApi=data=>{
    return put({
        url: `/users/${data.id}/state?mg_state=${data.mg_state}`,
        data
    })
}

views.pages.userList.vue:

<template>
  <div>
    <!-- 面包屑-->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">账号管理</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/">账号列表</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 白色内容区域 -->
    <div class="page_content">
      <div class="input_box">
        <div class="flex">
          <el-input v-model="keyWord" placeholder="搜索关键字" class="input-with-select">
            <template #append>
              <el-button @click="searchList">
                <el-icon>
                  <Search />
                </el-icon>
              </el-button>
            </template>
          </el-input>
          <el-button type="primary" @click="addUser">新建用户</el-button>
        </div>
      </div>
      <!-- 表格 -->
      <!--
          el-table 的 data:要展示的数据数组
          el-table-column: 列 prop每条数据的对应属性
          label: 列标题
          scope.row:相当于一条数据
      -->
      <el-table :data="userList" style="width: 100%">
        <el-table-column prop="username" label="姓名" width="180" />
        <el-table-column prop="role_name" label="角色" width="180" />
        <el-table-column prop="age" label="年龄" width="180" />
        <el-table-column prop="email" label="邮箱" width="180" />
        <el-table-column prop="mobile" label="手机号" />
        <el-table-column prop="mg_state" label="状态">
          <template #default="scope">
            <el-switch v-model="scope.row.mg_state" @change="swichChange(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button type="primary">编辑</el-button>
            <el-button type="danger">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <el-pagination
        background
        layout="prev, pager, sizes, next, jumper, ->, total"
        v-model:currentPage="searchParams.pagenum"
        v-model:page-size="searchParams.pagesize"
        :page-sizes="[2,3,5,10,20]"
        :total="total"
        @size-change="searchList"
        @current-change="searchList"
      />
    </div>
    <!-- 弹窗 -->
    <el-dialog v-model="dialogFormVisible" title="新建用户">
      <!-- 表单 -->
      <el-form ref="userForm" :model="formData" :rules="rules">
        <el-form-item label="用户名称" prop="username">
          <el-input v-model="formData.username" placeholder="请输入用户名" />
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="formData.password" placeholder="请输入密码" />
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData.email" placeholder="请输入邮箱" />
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData.mobile" placeholder="请输入手机号" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitForm(userForm)">确定</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script>
import { toRefs, reactive, ref } from "vue";
import {
  userListApi,
  userAddApi,
  userChangeStateApi
} from "@/utils/request.js";
export default {
  name: "users",
  setup() {
    const data = reactive({
      keyWord: "",
      searchParams: {
        query: "",
        pagesize: 5,
        pagenum: 2
      },
      total: 0,
      userList: [],
      dialogFormVisible: false,
      formData: {
        username: "",
        password: "",
        email: "",
        mobile: ""
      },
      rules: {
        username: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        password: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        email: [
          {
            required: false,
            pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
            message: "请填写正确的邮箱",
            trigger: "blur"
          }
        ],
        mobile: [
          {
            required: false,
            pattern: /^[1][3,4,5,6,7,8,9][0-9]{9}$/,
            message: "请填写正确的手机号",
            trigger: "blur"
          }
        ]
      }
    });
    const searchList = () => {
      userListApi(data.searchParams).then(res => {
        if (res.data) {
          //
          // console.log("用户数据", res);
          data.userList = res.data.users;
          data.total = res.data.total;
        }
      });
    };
    const addUser = () => {
      data.dialogFormVisible = true;
    };
    const submitForm = formEl => {
      // validate
      formEl.validate(res => {
        if (!res) {
          return;
        }
        // 表单验证通过请求接口
        // alert("通过")
        userAddApi(data.formData).then(res => {
          if (res.data) {
            // 隐藏弹框
            data.dialogFormVisible = false;
            // 清空弹框数据
            data.formData = {
              username: "",
              password: "",
              email: "",
              mobile: ""
            };
            // 重新更新列表
            searchList();
          }
        });
      });
    };
    // 状态更改
    const swichChange = row => {
      console.log("操作的那个数据是:", row);
      userChangeStateApi(row).then(res => {
        if (res.data) {
          searchList()
        }
      });
    };
    // 方法初始化
    searchList();
    const userForm = ref();
    return {
      ...toRefs(data),
      searchList,
      addUser,
      submitForm,
      userForm,
      swichChange
    };
  }
};
</script>
<style scoped>
.input_box {
  width: 250px;
  margin-right: 15x;
}
</style>

17、用户修改

views.pages.userList.vue:

<template>
  <div>
    <!-- 面包屑-->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">账号管理</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/">账号列表</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 白色内容区域 -->
    <div class="page_content">
      <div class="input_box">
        <div class="flex">
          <el-input v-model="searchParams.query" placeholder="搜索关键字" class="input-with-select">
            <template #append>
              <el-button @click="searchList">
                <el-icon>
                  <Search />
                </el-icon>
              </el-button>
            </template>
          </el-input>
          <el-button type="primary" @click="addUser">新建用户</el-button>
        </div>
      </div>
      <!-- 表格 -->
      <!--
          el-table 的 data:要展示的数据数组
          el-table-column: 列 prop每条数据的对应属性
          label: 列标题
          scope.row:相当于一条数据
      -->
      <el-table :data="userList" style="width: 100%">
        <el-table-column prop="username" label="姓名" width="180" />
        <el-table-column prop="role_name" label="角色" width="180" />
        <el-table-column prop="age" label="年龄" width="180" />
        <el-table-column prop="email" label="邮箱" width="180" />
        <el-table-column prop="mobile" label="手机号" />
        <el-table-column prop="mg_state" label="状态">
          <template #default="scope">
            <el-switch v-model="scope.row.mg_state" @change="swichChange(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button type="primary" @click="editRow(scope.row)">编辑</el-button>
            <el-button type="danger">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <el-pagination
        background
        layout="prev, pager, sizes, next, jumper, ->, total"
        v-model:currentPage="searchParams.pagenum"
        v-model:page-size="searchParams.pagesize"
        :page-sizes="[2,3,5,10,20]"
        :total="total"
        @size-change="searchList"
        @current-change="searchList"
      />
    </div>
    <!-- 新增弹窗 -->
    <el-dialog v-model="dialogFormVisible" title="新建用户">
      <!-- 表单 -->
      <el-form ref="userForm" :model="formData" :rules="rules">
        <el-form-item label="用户名称" prop="username">
          <el-input v-model="formData.username" placeholder="请输入用户名" />
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="formData.password" placeholder="请输入密码" />
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData.email" placeholder="请输入邮箱" />
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData.mobile" placeholder="请输入手机号" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitForm(userForm)">确定</el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 编辑弹窗 -->
    <el-dialog v-model="dialogFormEVisible" title="编辑用户">
      <!-- 表单 -->
      <el-form ref="userForm2" :model="formData2" :rules="rules2">
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData2.email" placeholder="请输入邮箱" />
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData2.mobile" placeholder="请输入手机号" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitEForm(userForm2)">确定</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script>
import { toRefs, reactive, ref } from "vue";
import {
  userListApi,
  userAddApi,
  userChangeStateApi,
  userChangeInfoApi
} from "@/utils/request.js";
export default {
  name: "users",
  setup() {
    const data = reactive({
      keyWord: "",
      searchParams: {
        query: "",
        pagesize: 5,
        pagenum: 2
      },
      total: 0,
      userList: [],
      dialogFormVisible: false,
      dialogFormEVisible: false,
      formData: {
        username: "",
        password: "",
        email: "",
        mobile: ""
      },
      formData2: {
        id: "",
        email: "",
        mobile: ""
      },
      rules: {
        username: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        password: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        email: [
          {
            required: false,
            pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
            message: "请填写正确的邮箱",
            trigger: "blur"
          }
        ],
        mobile: [
          {
            required: false,
            pattern: /^[1][3,4,5,6,7,8,9][0-9]{9}$/,
            message: "请填写正确的手机号",
            trigger: "blur"
          }
        ]
      },
      rules2: {
        email: [
          {
            required: false,
            pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
            message: "请填写正确的邮箱",
            trigger: "blur"
          }
        ],
        mobile: [
          {
            required: false,
            pattern: /^[1][3,4,5,6,7,8,9][0-9]{9}$/,
            message: "请填写正确的手机号",
            trigger: "blur"
          }
        ]
      }
    });
    const searchList = () => {
      userListApi(data.searchParams).then(res => {
        if (res.data) {
          //
          // console.log("用户数据", res);
          data.userList = res.data.users;
          data.total = res.data.total;
        }
      });
    };
    const addUser = () => {
      data.dialogFormVisible = true;
    };
    // 新增提交
    const submitForm = formEl => {
      // validate
      formEl.validate(res => {
        if (!res) {
          return;
        }
        // 表单验证通过请求接口
        // alert("通过")
        userAddApi(data.formData).then(res => {
          if (res.data) {
            // 隐藏弹框
            data.dialogFormVisible = false;
            // 清空弹框数据
            data.formData = {
              username: "",
              password: "",
              email: "",
              mobile: ""
            };
            // 重新更新列表
            searchList();
          }
        });
      });
    };
    // 修改提交
    const submitEForm = formEl => {
      formEl.validate(res => {
        if (!res) {
          return;
        }
        // 修改提交
        userChangeInfoApi(data.formData2).then(res => {
          if (res.data) {
            data.dialogFormEVisible=false;
            searchList();
          }
        });
      });
    };

    // 状态更改
    const swichChange = row => {
      console.log("操作的那个数据是:", row);
      userChangeStateApi(row).then(res => {
        if (res.data) {
          searchList();
        }
      });
    };
    // 用户信息编辑
    const editRow = row => {
      console.log("编辑的那个数据是:", row);
      // 展示编辑表单
      const { email, mobile, id } = row;
      data.dialogFormEVisible = true;
      data.formData2.id = id;
      data.formData2.email = email;
      data.formData2.mobile = mobile;
      // data.formData2.email=row.email
      // data.formData2.mobile=row.mobile
      // userChangeStateApi(row).then(res => {
      //   if (res.data) {
      //     searchList()
      //   }
      // });
    };
    // 方法初始化
    searchList();
    const userForm = ref();
    const userForm2 = ref();
    return {
      ...toRefs(data),
      searchList,
      addUser,
      submitForm,
      submitEForm,
      userForm,
      userForm2,
      swichChange,
      editRow
    };
  }
};
</script>
<style scoped>
.input_box {
  width: 250px;
  margin-right: 15x;
}
</style>

utils.request.js:

import { post, get, put } from "./service"

export const loginApi=data=>{
    return post({
        url: "/login",
        data
    })
}
// 获取用户列表
export const userListApi=data=>{
    return get({
        url: "/users",
        data
    })
}
// 新增用户
export const userAddApi=data=>{
    return post({
        url: "/users",
        data
    })
}
// 更改用户状态
export const userChangeStateApi=data=>{
    return put({
        url: `/users/${data.id}/state?mg_state=${data.mg_state}`,
        data
    })
}

// 编辑用户信息
export const userChangeInfoApi=data=>{
    return put({
        url: `/users/${data.id}`,
        data
    })
}

utils.service.js:

import axios from "axios"
import { ElLoading } from "element-plus"
import { ElMessage } from "element-plus"
import store from "../store/index.js"

// 使用create创建axios实例
let loadingObj = null
const Service = axios.create({
    timeout: 8000,
    baseURL: "http://127.0.0.1:5000/",
    headers:{
        "Content-type": "application/json;charset=utf-8",
        // "Authorization": store.state.uInfo.userInfo.token
        "Authorization": "token"
    }
})
// 请求拦截-增加loading,对请求做统一处理
Service.interceptors.request.use(config=>{
    loadingObj=ElLoading.service({
        lock: true,
        text: "Loading",
        background: "rgba(0, 0, 0, 0.7)",
    })
    return config
})

// 响应拦截-对返回值做统一处理
Service.interceptors.response.use(response=>{
    loadingObj.close()
    const data = response.data
    if(!data.data){
        // 请求出错
        ElMessage.error(data.meta.msg || "服务器出错")
        return data
    }
    return  data

},error=>{
    loadingObj.close()
    ElMessage({
        message: "服务器错误",
        type: "error",
        duration: "2000"
    })
})

// post请求
export const post=config=>{
    return Service({
        ...config,
        method: "post",
        data: config.data
    })
}

// get请求
export const get=config=>{
    return Service({
        ...config,
        method: "get",
        params: config.data
    })
}

// put请求
export const put=config=>{
    return Service({
        ...config,
        method: "put",
        data: config.data
    })
}

18、用户删除

utils.service.js:

import axios from "axios"
import { ElLoading } from "element-plus"
import { ElMessage } from "element-plus"
import store from "../store/index.js"

// 使用create创建axios实例
let loadingObj = null
const Service = axios.create({
    timeout: 8000,
    baseURL: "http://127.0.0.1:5000/",
    headers:{
        "Content-type": "application/json;charset=utf-8",
        // "Authorization": store.state.uInfo.userInfo.token
        "Authorization": "token"
    }
})
// 请求拦截-增加loading,对请求做统一处理
Service.interceptors.request.use(config=>{
    loadingObj=ElLoading.service({
        lock: true,
        text: "Loading",
        background: "rgba(0, 0, 0, 0.7)",
    })
    return config
})

// 响应拦截-对返回值做统一处理
Service.interceptors.response.use(response=>{
    loadingObj.close()
    const data = response.data
    if(data.meta.status!=200){
        // 请求出错
        ElMessage.error(data.meta.msg || "服务器出错")
        return data
    }
    return  data

},error=>{
    loadingObj.close()
    ElMessage({
        message: "服务器错误",
        type: "error",
        duration: "2000"
    })
})

// post请求
export const post=config=>{
    return Service({
        ...config,
        method: "post",
        data: config.data
    })
}

// get请求
export const get=config=>{
    return Service({
        ...config,
        method: "get",
        params: config.data
    })
}

// put请求
export const put=config=>{
    return Service({
        ...config,
        method: "put",
        data: config.data
    })
}

// delete请求
export const del=config=>{
    return Service({
        ...config,
        method: "delete"
    })
}

utils.request.js

import { post, get, put, del } from "./service"

export const loginApi=data=>{
    return post({
        url: "/login",
        data
    })
}
// 获取用户列表
export const userListApi=data=>{
    return get({
        url: "/users",
        data
    })
}
// 新增用户
export const userAddApi=data=>{
    return post({
        url: "/users",
        data
    })
}
// 更改用户状态
export const userChangeStateApi=data=>{
    return put({
        url: `/users/${data.id}/state?mg_state=${data.mg_state}`,
        data
    })
}

// 编辑用户信息
export const userChangeInfoApi=data=>{
    return put({
        url: `/users/${data.id}`,
        data
    })
}

// 删除用户信息
export const userDeleteApi=data=>{
    return del({
        url: `/users/${data.id}`
    })
}

views.pages.userList.vue

<template>
  <div>
    <!-- 面包屑-->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">账号管理</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/">账号列表</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 白色内容区域 -->
    <div class="page_content">
      <div class="input_box">
        <div class="flex">
          <el-input v-model="searchParams.query" placeholder="搜索关键字" class="input-with-select">
            <template #append>
              <el-button @click="searchList">
                <el-icon>
                  <Search />
                </el-icon>
              </el-button>
            </template>
          </el-input>
          <el-button type="primary" @click="addUser">新建用户</el-button>
        </div>
      </div>
      <!-- 表格 -->
      <!--
          el-table 的 data:要展示的数据数组
          el-table-column: 列 prop每条数据的对应属性
          label: 列标题
          scope.row:相当于一条数据
      -->
      <el-table :data="userList" style="width: 100%">
        <el-table-column prop="username" label="姓名" width="180" />
        <el-table-column prop="role_name" label="角色" width="180" />
        <el-table-column prop="age" label="年龄" width="180" />
        <el-table-column prop="email" label="邮箱" width="180" />
        <el-table-column prop="mobile" label="手机号" />
        <el-table-column prop="mg_state" label="状态">
          <template #default="scope">
            <el-switch v-model="scope.row.mg_state" @change="swichChange(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button type="primary" @click="editRow(scope.row)">编辑</el-button>
            <el-button type="danger" @click="deleteRow(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <el-pagination
        background
        layout="prev, pager, sizes, next, jumper, ->, total"
        v-model:currentPage="searchParams.pagenum"
        v-model:page-size="searchParams.pagesize"
        :page-sizes="[2,3,5,10,20]"
        :total="total"
        @size-change="searchList"
        @current-change="searchList"
      />
    </div>
    <!-- 新增弹窗 -->
    <el-dialog v-model="dialogFormVisible" title="新建用户">
      <!-- 表单 -->
      <el-form ref="userForm" :model="formData" :rules="rules">
        <el-form-item label="用户名称" prop="username">
          <el-input v-model="formData.username" placeholder="请输入用户名" />
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="formData.password" placeholder="请输入密码" />
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData.email" placeholder="请输入邮箱" />
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData.mobile" placeholder="请输入手机号" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitForm(userForm)">确定</el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 编辑弹窗 -->
    <el-dialog v-model="dialogFormEVisible" title="编辑用户">
      <!-- 表单 -->
      <el-form ref="userForm2" :model="formData2" :rules="rules2">
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData2.email" placeholder="请输入邮箱" />
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData2.mobile" placeholder="请输入手机号" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitEForm(userForm2)">确定</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script>
import { toRefs, reactive, ref } from "vue";
import {
  userListApi,
  userAddApi,
  userChangeStateApi,
  userChangeInfoApi,
  userDeleteApi
} from "@/utils/request.js";
export default {
  name: "users",
  setup() {
    const data = reactive({
      keyWord: "",
      searchParams: {
        query: "",
        pagesize: 5,
        pagenum: 2
      },
      total: 0,
      userList: [],
      dialogFormVisible: false,
      dialogFormEVisible: false,
      formData: {
        username: "",
        password: "",
        email: "",
        mobile: ""
      },
      formData2: {
        id: "",
        email: "",
        mobile: ""
      },
      rules: {
        username: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        password: [
          { required: true, message: "此项为必填项", trigger: "blur" }
        ],
        email: [
          {
            required: false,
            pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
            message: "请填写正确的邮箱",
            trigger: "blur"
          }
        ],
        mobile: [
          {
            required: false,
            pattern: /^[1][3,4,5,6,7,8,9][0-9]{9}$/,
            message: "请填写正确的手机号",
            trigger: "blur"
          }
        ]
      },
      rules2: {
        email: [
          {
            required: false,
            pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
            message: "请填写正确的邮箱",
            trigger: "blur"
          }
        ],
        mobile: [
          {
            required: false,
            pattern: /^[1][3,4,5,6,7,8,9][0-9]{9}$/,
            message: "请填写正确的手机号",
            trigger: "blur"
          }
        ]
      }
    });
    const searchList = () => {
      userListApi(data.searchParams).then(res => {
        if (res.data) {
          //
          // console.log("用户数据", res);
          data.userList = res.data.users;
          data.total = res.data.total;
        }
      });
    };
    const addUser = () => {
      data.dialogFormVisible = true;
    };
    // 新增提交
    const submitForm = formEl => {
      // validate
      formEl.validate(res => {
        if (!res) {
          return;
        }
        // 表单验证通过请求接口
        // alert("通过")
        userAddApi(data.formData).then(res => {
          if (res.data) {
            // 隐藏弹框
            data.dialogFormVisible = false;
            // 清空弹框数据
            data.formData = {
              username: "",
              password: "",
              email: "",
              mobile: ""
            };
            // 重新更新列表
            searchList();
          }
        });
      });
    };
    // 修改提交
    const submitEForm = formEl => {
      formEl.validate(res => {
        if (!res) {
          return;
        }
        // 修改提交
        userChangeInfoApi(data.formData2).then(res => {
          if (res.data) {
            data.dialogFormEVisible=false;
            searchList();
          }
        });
      });
    };

    // 状态更改
    const swichChange = row => {
      console.log("操作的那个数据是:", row);
      userChangeStateApi(row).then(res => {
        if (res.data) {
          searchList();
        }
      });
    };
    // 用户信息编辑
    const editRow = row => {
      console.log("编辑的那个数据是:", row);
      // 展示编辑表单
      const { email, mobile, id } = row;
      data.dialogFormEVisible = true;
      data.formData2.id = id;
      data.formData2.email = email;
      data.formData2.mobile = mobile;
      // data.formData2.email=row.email
      // data.formData2.mobile=row.mobile
      // userChangeStateApi(row).then(res => {
      //   if (res.data) {
      //     searchList()
      //   }
      // });
    };

    // 删除数据
    const deleteRow = row => {
      console.log("删除的那个数据是:", row);
      userDeleteApi(row).then(res=>{
        if(res.data){
          searchList()
        }
      })

    };

    // 方法初始化
    searchList();
    const userForm = ref();
    const userForm2 = ref();
    return {
      ...toRefs(data),
      searchList,
      addUser,
      submitForm,
      submitEForm,
      userForm,
      userForm2,
      swichChange,
      editRow,
      deleteRow
    };
  }
};
</script>
<style scoped>
.input_box {
  width: 250px;
  margin-right: 15x;
}
</style>

19、角色列表

utils.service.js

import axios from "axios"
import { ElLoading } from "element-plus"
import { ElMessage } from "element-plus"
import store from "../store/index.js"

// 使用create创建axios实例
let loadingObj = null
const Service = axios.create({
    timeout: 8000,
    baseURL: "http://127.0.0.1:5000/",
    headers:{
        "Content-type": "application/json;charset=utf-8",
        // "Authorization": store.state.uInfo.userInfo.token
        "Authorization": "token"
    }
})
// 请求拦截-增加loading,对请求做统一处理
Service.interceptors.request.use(config=>{
    loadingObj=ElLoading.service({
        lock: true,
        text: "Loading",
        background: "rgba(0, 0, 0, 0.7)",
    })
    return config
})

// 响应拦截-对返回值做统一处理
Service.interceptors.response.use(response=>{
    loadingObj.close()
    const data = response.data
    if(data.meta.status!=200 && data.data.status!=201){
        // 请求出错
        ElMessage.error(data.meta.msg || "服务器出错")
        return data
    }
    return  data

},error=>{
    loadingObj.close()
    ElMessage({
        message: "服务器错误",
        type: "error",
        duration: "2000"
    })
})

// post请求
export const post=config=>{
    return Service({
        ...config,
        method: "post",
        data: config.data
    })
}

// get请求
export const get=config=>{
    return Service({
        ...config,
        method: "get",
        params: config.data
    })
}

// put请求
export const put=config=>{
    return Service({
        ...config,
        method: "put",
        data: config.data
    })
}

// delete请求
export const del=config=>{
    return Service({
        ...config,
        method: "delete"
    })
}

utils.request.js:

import { post, get, put, del } from "./service"

export const loginApi=data=>{
    return post({
        url: "/login",
        data
    })
}
// 获取用户列表
export const userListApi=data=>{
    return get({
        url: "/users",
        data
    })
}

// 新增用户
export const userAddApi=data=>{
    return post({
        url: "/users",
        data
    })
}
// 更改用户状态
export const userChangeStateApi=data=>{
    return put({
        url: `/users/${data.id}/state?mg_state=${data.mg_state}`,
        data
    })
}

// 编辑用户信息
export const userChangeInfoApi=data=>{
    return put({
        url: `/users/${data.id}`,
        data
    })
}

// 删除用户信息
export const userDeleteApi=data=>{
    return del({
        url: `/users/${data.id}`
    })
}

// 获取角色列表
export const getRolesApi=data=>{
    return get({
        url: "/roles",
        data
    })
}

// 新建用户角色
export const addRolesApi=data=>{
    return post({
        url: "/roles",
        data
    })
}

// 编辑用户角色
export const editRolesApi=data=>{
    return put({
        url: `/roles/${data.id}`,
        data
    })
}

// 删除角色信息
export const roleDeleteApi=data=>{
    return del({
        url: `/roles/${data.id}`
    })
}

views.pages.roleList.vue:

<template>
  <div>
    <!-- 面包屑-->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">角色管理</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/">角色列表</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 白色内容区域 -->
    <div class="page_content">
      <div class="input_box">
        <div class="flex">
          <el-input v-model="searchParams.query" placeholder="搜索关键字" class="input-with-select">
            <template #append>
              <el-button @click="searchList">
                <el-icon>
                  <Search />
                </el-icon>
              </el-button>
            </template>
          </el-input>
          <el-button type="primary" @click="addRole">新建角色</el-button>
        </div>
      </div>
      <!-- 表格 -->
      <!--
          el-table 的 data:要展示的数据数组
          el-table-column: 列 prop每条数据的对应属性
          label: 列标题
          scope.row:相当于一条数据
      -->
      <el-table :data="rolesList" style="width: 100%">
        <el-table-column prop="rolename" label="角色名" width="180" />
        <el-table-column prop="roledes" label="角色描述" width="180" />
        <el-table-column label="操作">
          <template #default="scope">
            <el-button type="primary" @click="editRow(scope.row)">编辑</el-button>
            <el-button type="danger" @click="deleteRow(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <el-pagination
        background
        layout="prev, pager, sizes, next, jumper, ->, total"
        v-model:currentPage="searchParams.pagenum"
        v-model:page-size="searchParams.pagesize"
        :page-sizes="[2,3,5,10,20]"
        :total="total"
        @size-change="getList"
        @current-change="getList"
      />
    </div>

    <!-- 新增弹窗 -->
    <el-dialog v-model="dialogFormVisible" @close="clearForm" :title="editRow.id?'编辑角色':'新建角色'">
      <!-- 表单 -->
      <el-form ref="roleForm" :model="formData" :rules="rules">
        <el-form-item label="角色名" prop="rolename">
          <el-input v-model="formData.rolename" placeholder="请输入角色名" />
        </el-form-item>
        <el-form-item label="角色描述" prop="roledes">
          <el-input v-model="formData.roledes" placeholder="请输入角色描述" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitForm(roleForm)">确定</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 编辑弹窗 -->
    <!-- <el-dialog v-model="dialogFormEVisible" title="编辑角色"> -->
      <!-- 表单 -->
      <!-- <el-form ref="roleForm2" :model="formData2" :rules="rules">
        <el-form-item label="角色名" prop="rolename">
          <el-input v-model="formData2.rolename" placeholder="请输入角色名" />
        </el-form-item>
        <el-form-item label="角色描述" prop="roledes">
          <el-input v-model="formData2.roledes" placeholder="请输入角色描述" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div>
          <el-button>取消</el-button>
          <el-button type="primary" @click="submitEForm(roleForm2)">确定</el-button>
        </div>
      </template>
    </el-dialog> -->
  </div>
</template>
<script>
import { reactive, toRefs, ref } from "vue";
import { getRolesApi, addRolesApi, editRolesApi, roleDeleteApi } from "@/utils/request.js";
export default {
  name: "roles",
  setup() {
    const data = reactive({
      searchParams: {
        query: "",
        pagesize: 5,
        pagenum: 1
      },
      total: 0,
      rolesList: [],
      dialogFormVisible: false,
      formData: {
        id:"",
        rolename: "",
        roledes: ""
      },
      formData2: {
        id: "",
        rolename: "",
        roledes: ""
      },
      rules: {
        rolename: {
          required: true,
          message: "此项必填",
          trigger: "blur"
        }
      }
    });
    // 此时没有分页,不需要传参数
    const getList = () => {
      getRolesApi(data.searchParams).then(res => {
        console.log("用户数据", res.data);
        if (res.data) {
          data.rolesList = res.data.roles;
          data.total = res.data.total
        }
      });
    };
    // 新建角色
    const addRole = () => {
      data.dialogFormVisible = true;
    };
    // 新增提交
    const submitForm = formEl => {
      // validate
      formEl.validate(res => {
        if (!res) {
          return;
        }
        // 提交表单
        // 表单验证通过请求接口
        // alert("通过")
        if(data.formData.id){
          editRolesApi(data.formData).then(res => {
          if (res.data) {
            // 隐藏弹框
            data.dialogFormVisible = false;
            // // 清空弹框数据
            // data.formData = {
            //   rolename: "",
            //   roledes: ""
            // };
            // 重新更新列表
            getList();
          }
        });
        }else{
          addRolesApi(data.formData).then(res => {
          if (res.data) {
            // 隐藏弹框
            data.dialogFormVisible = false;
            // // 清空弹框数据
            // data.formData = {
            //   rolename: "",
            //   roledes: ""
            // };
            // 重新更新列表
            getList();
          }
        });
        }
      });
    };
    const editRow = row => {
      data.dialogFormVisible=true;
      const { rolename, roledes, id } = row
      data.formData={
        id,
        rolename,
        roledes
      }
    };
    const deleteRow = row => {
      roleDeleteApi(row).then(res=>{
        getList()
      })
    };
    // 清除表单
    const clearForm=()=>{
      data.formData={
        rolename:"",
        roledes:""
      }
    }
    // 初始化
    getList();
    const roleForm = ref();
    // const roleForm2 = ref();
    return {
      ...toRefs(data),
      getList,
      editRow,
      deleteRow,
      addRole,
      roleForm,
      submitForm,
      clearForm
    };
  }
};
</script>
<style scoped>
.input_box {
  width: 280px;
  margin-right: 15px;
}
</style>

学习视频链接:2023Vue3商店后台管理系统项目实战【已完结】_哔哩哔哩_bilibili

猜你喜欢

转载自blog.csdn.net/weixin_44799217/article/details/131256623