赞
踩
vue create vue3-admin

更改前

更改后

npm install element-plus --save
cnpm install -D unplugin-vue-components unplugin-auto-import
- const AutoImport = require('unplugin-auto-import/webpack')
- const Components = require('unplugin-vue-components/webpack')
- const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
-
- module.exports = {
- intOnSave:false,
- devServer:{
- open:true,
- port:9000,
- },
- configureWebpack: {
- plugins: [
- AutoImport({
- resolvers: [ElementPlusResolver()],
- }),
- Components({
- resolvers: [ElementPlusResolver()],
- }),
- ]
- },
- }


tips:在main.js中引入样式(避免弹出框等组件样式错乱)
import 'element-plus/dist/index.css'
cnpm install @element-plus/icons-vue
- //main.js
- import * as ELIcons from '@element-plus/icons-vue'
- for (const iconName in ELIcons) {
- app.component(iconName, ELIcons[iconName])
- }
- <template #title>
- <el-icon>
- <component :is="iconList[index]"></component>
- </el-icon>
- <span>{{ item.authName }}</span>
- </template>


import '@/styles/index.scss'
- css: {
- loaderOptions: {
- sass: {
- // 8版本用prependData:
- prependData: `
- @import "@/styles/variables.scss"; // scss文件地址
- @import "@/styles/mixin.scss"; // scss文件地址
- `
- }
- }
- }

- <template>
- <svg class="svg-icon" aria-hidden="true">
- <use :xlink:href="iconName"></use>
- </svg>
- </template>
-
- <script setup>
- import { defineProps, computed } from 'vue'
- const props = defineProps({
- icon: {
- type: String,
- required: true
- }
- })
-
- const iconName = computed(() => {
- return `#icon-${props.icon}`
- })
- </script>
-
- <style lang="scss" scoped>
- .svg-icon {
- width: 1em;
- height: 1em;
- vertical-align: -0.15em;
- fill: currentColor;
- overflow: hidden;
- }
- </style>

- import SvgIcon from '@/components/SvgIcon'
-
- const svgRequired = require.context('./svg', false, /\.svg$/)
- svgRequired.keys().forEach((item) => svgRequired(item))
-
- export default (app) => {
- app.component('svg-icon', SvgIcon)
- }
- import SvgIcon from '@/icons'
- const app=createApp(App)
- SvgIcon(app)
cnpm i --save-dev svg-sprite-loader@6.0.9
- const path = require('path')
- function resolve(dir) {
- return path.join(__dirname, dir)
- }
- const webpack = require('webpack')
-
- module.exports = {
- chainWebpack(config) {
- // 设置 svg-sprite-loader
- // config 为 webpack 配置对象
- // config.module 表示创建一个具名规则,以后用来修改规则
- config.module
- // 规则
- .rule('svg')
- // 忽略
- .exclude.add(resolve('src/icons'))
- // 结束
- .end()
- // config.module 表示创建一个具名规则,以后用来修改规则
- config.module
- // 规则
- .rule('icons')
- // 正则,解析 .svg 格式文件
- .test(/\.svg$/)
- // 解析的文件
- .include.add(resolve('src/icons'))
- // 结束
- .end()
- // 新增了一个解析的loader
- .use('svg-sprite-loader')
- // 具体的loader
- .loader('svg-sprite-loader')
- // loader 的配置
- .options({
- symbolId: 'icon-[name]'
- })
- // 结束
- .end()
- config
- .plugin('ignore')
- .use(
- new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn$/)
- )
- config.module
- .rule('icons')
- .test(/\.svg$/)
- .include.add(resolve('src/icons'))
- .end()
- .use('svg-sprite-loader')
- .loader('svg-sprite-loader')
- .options({
- symbolId: 'icon-[name]'
- })
- .end()
- },
- }

<svg-icon icon="user" class="svg-container"></svg-icon>
cnpm i axios --save
- const TOKEN_TIME = 'tokenTime'
-
- const TOKEN_TIME_VALUE = 2 * 60 * 60 * 1000
-
- // 登录时设置时间
- export const setTokenTime = () => {
- localStorage.setItem(TOKEN_TIME, Date.now())
- }
-
- // 获取
- export const getTokenTime = () => {
- return localStorage.getItem(TOKEN_TIME)
- }
-
- // 是否已经过期
- export const diffTokenTime = () => {
- const currentTime = Date.now()
- const tokenTime = getTokenTime()
- return currentTime - tokenTime > TOKEN_TIME_VALUE
- }

- import axios from 'axios'
- import { ElMessage } from 'element-plus'
- import { diffTokenTime } from '@/utils/auth'
- import store from '@/store'
- const service = axios.create({
- baseURL: process.env.VUE_APP_BASE_API,
- timeout: 5000
- })
-
- service.interceptors.request.use(
- (config) => {
- if (localStorage.getItem('token')) {
- if (diffTokenTime()) {
- store.dispatch('app/logout')
- return Promise.reject(new Error('token 失效了'))
- }
- }
- config.headers.Authorization = localStorage.getItem('token')
- return config
- },
- (error) => {
- return Promise.reject(new Error(error))
- }
- )
-
- service.interceptors.response.use(
- (response) => {
- const { data, meta } = response.data
- if (meta.status === 200 || meta.status === 201) {
- return data
- } else {
- ElMessage.error(meta.msg)
- return Promise.reject(new Error(meta.msg))
- }
- },
- (error) => {
- console.log(error.response)
- error.response && ElMessage.error(error.response.data)
- return Promise.reject(new Error(error.response.data))
- }
- )
- export default service

- devServer: {
- https: false,
- hotOnly: false,
- open:true,
- port:9000,
- proxy: {
- '/api': {
- target: 'https://*******/api/private/v1/',
- changeOrigin: true,
- pathRewrite: {
- '^/api': ''
- }
- }
- }
- },
新建 .env.development、.env.production文件
- ENV = 'development'
-
- VUE_APP_BASE_API = '/api'
- ENV = 'production'
-
- VUE_APP_BASE_API = '/prod-api'
- import { createRouter, createWebHashHistory } from 'vue-router'
-
- const routes = [
- {
- path: '/login',
- name: 'Login',
- component: () => import('../views/login/index.vue')
- },
- {
- path: '/',
- name: '/',
- component: () => import('../layout'),
- redirect: '/users',
- children: [
- {
- path: 'users',
- name: 'users',
- component: () => import('@/views/users/index.vue')
- },
- {
- path: 'categories',
- name: 'categories',
- component: () => import('@/views/categories/index.vue')
- },
- {
- path: 'goods',
- name: 'goods',
- component: () => import('@/views/goods/index.vue')
- },
- {
- path: 'orders',
- name: 'orders',
- component: () => import('@/views/orders/index.vue')
- },
- {
- path: 'params',
- name: 'params',
- component: () => import('@/views/params/index.vue')
- },
- {
- path: 'reports',
- name: 'reports',
- component: () => import('@/views/reports/index.vue')
- },
- {
- path: 'rights',
- name: 'rights',
- component: () => import('@/views/rights/index.vue')
- },
- {
- path: 'roles',
- name: 'roles',
- component: () => import('@/views/roles/index.vue')
- }
- ]
- }
- ]
-
-
- const router = createRouter({
- history: createWebHashHistory(),
- routes
- })
-
- export default router

- import router from './index'
- import store from '@/store'
-
- const whiteList = ['/login']
- router.beforeEach((to, from, next) => {
- if (store.getters.token) {
- if (to.path === '/login') {
- next('/')
- } else {
- next()
- }
- } else {
- if (whiteList.includes(to.path)) {
- next()
- } else {
- next('/login')
- }
- }
- })

import '@/router/permission'
- <template>
- <div class="login-container">
- <el-form ref="loginFormRef" :model="loginForm" class="login-form" :rules="rules">
- <div class="title-container">
- <h3 class="title">后台管理系统</h3>
- </div>
- <el-form-item prop="username">
- <svg-icon icon="user" class="svg-container"></svg-icon>
- <el-input v-model="loginForm.username"></el-input>
- </el-form-item>
- <el-form-item prop="password">
- <svg-icon icon="password" class="svg-container"></svg-icon>
- <el-input v-model="loginForm.password" :type="passwordType"></el-input>
- <svg-icon
- class="svg-container"
- :icon="passwordType === 'password' ? 'eye' : 'eye-open'"
- @click="changeType"
- ></svg-icon>
- </el-form-item>
- <el-button type="primary" class="login-button" @click="handleLogin">登录</el-button>
- </el-form>
- </div>
- </template>
-
- <script setup>
- import { ref,reactive} from 'vue'
- import { useStore } from 'vuex'
- const store = useStore()
- const loginForm = reactive({
- username: 'admin',
- password: '123456'
- })
-
- const rules = reactive({
- username: [
- { required: true, message: '请输入用户名', trigger: 'blur' },
- ],
- password: [
- {
- required: true,
- message: '请输入密码',
- trigger: 'blur'
- }
- ]
- })
-
- const loginFormRef = ref(null)
- const handleLogin = () => {
- loginFormRef.value.validate(async (valid) => {
- if (valid) {
- store.dispatch('app/login', loginForm)
- } else {
- console.log('error submit!!')
- return false
- }
- })
- }
-
- const passwordType = ref('password')
- const changeType = () => {
- if (passwordType.value === 'password') {
- passwordType.value = 'text'
- } else {
- passwordType.value = 'password'
- }
- }
- </script>
-
- <style lang="scss" scoped>
- $bg: #2d3a4b;
- $dark_gray: #889aa4;
- $light_gray: #eee;
- $cursor: #fff;
-
- .login-container {
- min-height: 100%;
- width: 100%;
- background-color: $bg;
- overflow: hidden;
-
- .login-form {
- position: relative;
- width: 520px;
- max-width: 100%;
- padding: 160px 35px 0;
- margin: 0 auto;
- overflow: hidden;
-
- :deep(.el-form-item) {
- border: 1px solid rgba(255, 255, 255, 0.1);
- background: rgba(0, 0, 0, 0.1);
- border-radius: 5px;
- color: #454545;
- height: 50px;
- .el-input{
- flex:1;
- .el-input__wrapper{
- background-color:transparent;
- box-shadow: none;
- .el-input__inner{
- color: #fff;
- }
- }
- }
- }
- .login-button {
- width: 100%;
- box-sizing: border-box;
- }
- }
-
- .tips {
- font-size: 16px;
- line-height: 28px;
- color: #fff;
- margin-bottom: 10px;
-
- span {
- &:first-of-type {
- margin-right: 16px;
- }
- }
- }
-
- .svg-container {
- padding: 6px 5px 6px 15px;
- color: $dark_gray;
- vertical-align: middle;
- display: inline-block;
- }
-
- .title-container {
- position: relative;
-
- .title {
- font-size: 26px;
- color: $light_gray;
- margin: 0px auto 40px auto;
- text-align: center;
- font-weight: bold;
- }
-
- :deep(.lang-select) {
- position: absolute;
- top: 4px;
- right: 0;
- background-color: white;
- font-size: 22px;
- padding: 4px;
- border-radius: 4px;
- cursor: pointer;
- }
- }
-
- .show-pwd {
- // position: absolute;
- // right: 10px;
- // top: 7px;
- font-size: 16px;
- color: $dark_gray;
- cursor: pointer;
- user-select: none;
- }
- }
- </style>

- import request from './request'
-
- export const login = (data) => {
- return request({
- url: '/login',
- method: 'POST',
- data
- })
- }
- import { login as loginApi } from '@/api/login'
- import router from '@/router'
- import { setTokenTime } from '@/utils/auth'
- export default {
- namespaced: true,
- state: () => ({
- token: localStorage.getItem('token') || '',
- siderType: true,
- }),
- mutations: {
- setToken(state, token) {
- state.token = token
- localStorage.setItem('token', token)
- },
- changeSiderType(state) {
- state.siderType = !state.siderType
- },
- },
- actions: {
- login({ commit }, userInfo) {
- return new Promise((resolve, reject) => {
- loginApi(userInfo)
- .then((res) => {
- console.log(res)
- commit('setToken', res.token)
- setTokenTime()
- router.replace('/')
- resolve()
- })
- .catch((err) => {
- reject(err)
- })
- })
- },
- // 退出
- logout({ commit }) {
- commit('setToken', '')
- localStorage.clear()
- router.replace('/login')
- }
- }
- }

- export default {
- token: (state) => state.app.token,
- siderType: (state) => state.app.siderType,
- }
- import { createStore } from 'vuex'
- import app from './modules/app'
- import getters from './getters'
- export default createStore({
- modules: {
- app
- },
- getters
- })



- <template>
- <el-container class="content_wrapper">
- <el-aside :width="asideWidth" class="sidebar-container">
- <Menu />
- </el-aside>
- <el-container
- class="right_container"
- :class="{ hidderContainer: !$store.getters.siderType }"
- >
- <el-header><Headers /></el-header>
- <el-main>
- <router-view />
- </el-main>
- </el-container>
- </el-container>
- </template>
-
-
- <script setup>
- import Menu from './Menu'
- import Headers from './headers'
- import { computed } from 'vue'
- import variables from '@/styles/variables.scss'
- import { useStore } from 'vuex'
- const store = useStore()
- // const asideWidth = ref(variables.sideBarWidth)
- const asideWidth = computed(() => {
- return store.getters.siderType
- ? variables.sideBarWidth
- : variables.hideSideBarWidth
- })
- </script>
-
- <style lang="scss" scoped>
-
- .content_wrapper{
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- :deep(.sidebar-container){
- flex-shrink: 0;
- }
- :deep(.right_container){
- flex:1;
- height: 100%;
- }
- }
-
- </style>

- <template>
- <el-menu
- active-text-color="#ffd04b"
- :background-color="variables.menuBg"
- class="el-menu-vertical-demo"
- :default-active="defaultActive"
- text-color="#fff"
- router
- unique-opened
- :collapse="!$store.getters.siderType"
- >
- <el-sub-menu
- :index="item.id+''"
- v-for="(item, index) in menusList"
- :key="item.id"
- >
- <template #title>
- <el-icon>
- <component :is="iconList[index]"></component>
- </el-icon>
- <span>{{ item.authName }}</span>
- </template>
- <el-menu-item
- :index="'/' + it.path"
- v-for="it in item.children"
- :key="it.id"
- @click="savePath(it.path)"
- >
- <template #title>
- <el-icon>
- <component :is="icon"></component>
- </el-icon>
- <span>{{ it.authName }}</span>
- </template>
- </el-menu-item>
- </el-sub-menu>
- </el-menu>
- </template>
-
- <script setup>
- import { menuList } from '@/api/menu'
- import { ref } from 'vue'
- import variables from '@/styles/variables.scss'
-
- const iconList = ref(['user', 'setting', 'shop', 'tickets', 'pie-chart'])
- const icon = ref('menu')
-
- const defaultActive = ref(sessionStorage.getItem('path') || '/users')
- const menusList = ref([])
- const initMenusList = async () => {
- menusList.value = await menuList()
- }
- initMenusList()
-
- const savePath = (path) => {
- sessionStorage.setItem('path', `/${path}`)
- }
- </script>
-
- <style lang="scss" scoped></style>

menu数据
- {
- "data": [
- {
- "id": 125,
- "authName": "用户管理",
- "path": "users",
- "children": [
- {
- "id": 110,
- "authName": "用户列表",
- "path": "users",
- "children": [
-
- ],
- "order": null
- }
- ],
- "order": 1
- },
- {
- "id": 103,
- "authName": "权限管理",
- "path": "rights",
- "children": [
- {
- "id": 111,
- "authName": "角色列表",
- "path": "roles",
- "children": [
-
- ],
- "order": null
- },
- {
- "id": 112,
- "authName": "权限列表",
- "path": "rights",
- "children": [
-
- ],
- "order": null
- }
- ],
- "order": 2
- },
- {
- "id": 101,
- "authName": "商品管理",
- "path": "goods",
- "children": [
- {
- "id": 104,
- "authName": "商品列表",
- "path": "goods",
- "children": [
-
- ],
- "order": 1
- },
- {
- "id": 115,
- "authName": "分类参数",
- "path": "params",
- "children": [
-
- ],
- "order": 2
- },
- {
- "id": 121,
- "authName": "商品分类",
- "path": "categories",
- "children": [
-
- ],
- "order": 3
- }
- ],
- "order": 3
- },
- {
- "id": 102,
- "authName": "订单管理",
- "path": "orders",
- "children": [
- {
- "id": 107,
- "authName": "订单列表",
- "path": "orders",
- "children": [
-
- ],
- "order": null
- }
- ],
- "order": 4
- },
- {
- "id": 145,
- "authName": "数据统计",
- "path": "reports",
- "children": [
- {
- "id": 146,
- "authName": "数据报表",
- "path": "reports",
- "children": [
-
- ],
- "order": null
- }
- ],
- "order": 5
- }
- ],
- "meta": {
- "msg": "获取菜单列表成功",
- "status": 200
- }
- }

- <template>
- <div class="navbar">
- <Hamburger />
- <Breadcrumb />
- <div class="navbar-right">
- <Driver class="navbar-item" />
- <screen-full class="navbar-item" />
- <Avatar class="navbar-item" />
- </div>
- </div>
- </template>
-
- <script setup>
- import Hamburger from './components/hamburger.vue'
- import Breadcrumb from './components/breadcrumb.vue'
- import Avatar from './components/avatar.vue'
- import ScreenFull from './components/screenFull.vue'
- import Driver from './components/driver'
- </script>
-
- <style lang="scss" scoped>
- .navbar {
- width: 100%;
- height: 60px;
- overflow: hidden;
- background-color: #fff;
- box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
- padding: 0 16px;
- display: flex;
- align-items: center;
- box-sizing: border-box;
- position: relative;
- .navbar-right {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: flex-end;
- ::v-deep .navbar-item {
- display: inline-block;
- margin-left: 18px;
- font-size: 22px;
- color: #5a5e66;
- box-sizing: border-box;
- cursor: pointer;
- }
- }
- }
- </style>

- <template>
- <div class="hamburger-container" @click="toggleClick" id="hamburger">
- <svg-icon :icon="icon"></svg-icon>
- </div>
- </template>
-
- <script setup>
- import { useStore } from 'vuex'
- import { computed } from 'vue'
- const store = useStore()
- const toggleClick = () => {
- store.commit('app/changeSiderType')
- }
- const icon = computed(() => {
- return store.getters.siderType ? 'hamburger-opened' : 'hamburger-closed'
- })
- </script>
-
- <style lang="scss" scoped>
- .hamburger-container {
- margin-right: 16px;
- box-sizing: border-box;
- cursor: pointer;
- }
- </style>

- <template>
- <el-breadcrumb separator="/">
- <el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="index">
- <span class="no-redirect" v-if="index === breadcrumbList.length - 1">{{
- item.name
- }}</span>
- <span class="redirect" v-else @click="handleRedirect(item.path)">{{
- item.name
- }}</span>
- </el-breadcrumb-item>
- </el-breadcrumb>
- </template>
-
- <script setup>
- import { watch, ref } from 'vue'
- import { useRoute, useRouter } from 'vue-router'
- const route = useRoute()
- const router = useRouter()
-
- const breadcrumbList = ref([])
-
- const initBreadcrumbList = () => {
- breadcrumbList.value = route.matched
- console.log(route.matched)
- }
- const handleRedirect = (path) => {
- router.push(path)
- }
-
- watch(
- route,
- () => {
- initBreadcrumbList()
- },
- { deep: true, immediate: true }
- )
- </script>
-
- <style lang="scss" scoped>
- .no-redirect {
- color: #97a8be;
- cursor: text;
- }
- .redirect {
- color: #666;
- font-weight: 600;
- cursor: pointer;
- &:hover {
- color: $menuBg;
- }
- }
- </style>

1、安装
cnpm i screenfull --save
2、使用
- <template>
- <div @click="handleFullScreen" id="screenFul">
- <svg-icon :icon="icon ? 'exit-fullscreen' : 'fullscreen'"></svg-icon>
- </div>
- </template>
-
- <script setup>
- import screenfull from 'screenfull'
- import { ref, onMounted, onBeforeMount } from 'vue'
-
- const icon = ref(screenfull.isFullscreen)
- const handleFullScreen = () => {
- if (screenfull.isEnabled) {
- screenfull.toggle()
- }
- }
-
- const changeIcon = () => {
- icon.value = screenfull.isFullscreen
- }
-
- onMounted(() => {
- screenfull.on('change', changeIcon)
- })
-
- onBeforeMount(() => {
- screenfull.off('change')
- })
- </script>
-
- <style lang="scss" scoped></style>

1、安装
cnpm i driver.js --save
2、使用
- <template>
- <div id="guide" @click.prevent.stop="handleGuide">
- <svg-icon icon="guide"></svg-icon>
- </div>
- </template>
-
- <script setup>
- import Driver from 'driver.js'
- import 'driver.js/dist/driver.min.css'
- import { onMounted } from 'vue'
- const steps=[
- {
- element: '#guide',
- popover: {
- title: 'guideBtn',
- description: 'Body of the popover',
- position: 'left'
- }
- },
- {
- element: '#hamburger',
- popover: {
- title: 'hamburgerBtn',
- description: 'Body of the popover',
- position: 'bottom'
- }
- },
- {
- element: '#screenFul',
- popover: {
- title: 'fullScreen',
- description: 'Body of the popover',
- position: 'left'
- }
- }
- ]
-
- let driver
- onMounted(() => {
- initDriver()
- })
- const initDriver = () => {
- driver = new Driver({
- animate: false, // Whether to animate or not
- opacity: 0.75, // Background opacity (0 means only popovers and without overlay)
- padding: 10, // Distance of element from around the edges
- allowClose: true, // Whether the click on overlay should close or not
- overlayClickNext: false, // Whether the click on overlay should move next
- doneBtnText: 'doneBtnText', // Text on the final button
- closeBtnText: 'closeBtnText', // Text on the close button for this step
- stageBackground: '#ffffff', // Background color for the staged behind highlighted element
- nextBtnText: 'nextBtnText', // Next button text for this step
- prevBtnText: 'prevBtnText' // Previous button text for this step
- })
- }
-
-
- const handleGuide = () => {
- driver.defineSteps(steps)
- driver.start()
- }
- </script>
-
- <style lang="scss" scoped></style>



- <template>
- <el-dropdown>
- <span class="el-dropdown-link">
- <el-avatar shape="square" :size="40" :src="squareUrl"></el-avatar>
- </span>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item @click="logout">退出</el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </template>
-
- <script setup>
- import { ref } from 'vue'
- import { useStore } from 'vuex'
- const store = useStore()
-
- const squareUrl = ref(
- 'https://img0.baidu.com/it/u=1056811702,4111096278&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500'
- )
-
- const logout = () => {
- store.dispatch('app/logout')
- }
- </script>
-
- <style lang="scss" scoped>
- ::v-deep .el-dropdown-menu__item {
- white-space: nowrap;
- }
- </style>

- <template>
- <el-card class="users">
- <div class="top">
- <div class="search_box">
- <el-input
- v-model="queryForm.query"
- clearable
- placeholder="请输入用户名"
- />
- </div>
- <el-button type="primary" :icon="Search" @click="getUsersTableData">查询</el-button>
- <el-button type="primary" :icon="Plus" @click="handleDialogValue()">新增用户</el-button>
- </div>
- <div class="bottom">
- <el-table class="users_table" :data="tableData" border style="width: 100%">
- <el-table-column align="center" :key="index" v-for="(item,index) in options" :prop="item.prop" :label="item.label" :width="item.width">
- <template v-slot="{row}" v-if="item.prop=='mg_state'">
- <el-switch v-model="row.mg_state" @change='changeState(row)' />
- </template>
- <template v-slot="{row}" v-else-if="item.prop=='create_time'">
- {{$filters.filterTimes(row.create_time)}}
- </template>
- <template #default="{row}" v-else-if="item.prop=='action'">
- <el-button size="small" :icon="Edit" type="primary" @click="handleDialogValue(row)"></el-button>
- <el-button size="small" :icon="Setting" type="warning"></el-button>
- <el-button size="small" :icon="Delete" type="danger" @click="delUser(row)"></el-button>
- </template>
- </el-table-column>
- </el-table>
- <el-pagination
- background
- v-model:currentPage="queryForm.pagenum"
- v-model:page-size="queryForm.pagesize"
- :page-sizes="[1, 2, 5, 10]"
- layout="total, sizes, prev, pager, next, jumper"
- :total="total"
- @size-change="handleSizeChange"
- @current-change="handleCurrentChange"
- />
- </div>
- </el-card>
- <Dialog v-model="dialogVisible" :dialogTableValue="dialogTableValue" @initUserList="getUsersTableData" :dialogTitle="dialogTitle" v-if="dialogVisible" />
- </template>
-
- <script setup>
- import { Search,Edit,Setting,Delete,Plus } from '@element-plus/icons-vue'
- import { ref,reactive,onMounted } from 'vue'
- import { getUsersList,changeUserState,deleteUser } from '@/api/users'
- import { ElMessage,ElMessageBox} from 'element-plus'
- import { isNull } from '@/utils/filters'
- import Dialog from './components/dialog'
- const options = [
- {
- label:'username',
- prop:'username'
- },
- {
- label:'email',
- prop:'email'
- },
- {
- label:'mobile',
- prop:'mobile'
- },
- {
- label:'role_name',
- prop:'role_name'
- },
- {
- label:'mg_state',
- prop:'mg_state'
- },
- {
- label:'create_time',
- prop:'create_time'
- },
- {
- label:'action',
- prop:'action',
- width:'200px'
- },
- ]
-
- let dialogVisible = ref(false)
- let dialogTitle = ref('新增用户')
- let queryForm = reactive({
- query:'',
- pagenum:1,
- pagesize:10
- })
- const total = ref(0)
- let tableData = ref([])
- let dialogTableValue= ref({})
- const getUsersTableData= async ()=>{
- let res = await getUsersList(queryForm)
- tableData.value=res.users
- total.value=res.total
- }
- getUsersTableData()
- const handleSizeChange = (pagesize)=>{
- queryForm.pagesize=pagesize;
- getUsersTableData()
- }
- const handleCurrentChange = (pagenum)=>{
- queryForm.pagenum=pagenum;
- getUsersTableData()
- }
- const changeState= async (row)=>{
- let {id,mg_state} = row
- let res = await changeUserState(id,mg_state)
- console.log(res,'更改状态')
- ElMessage({
- message: '更新成功',
- type: 'success',
- })
- }
- const handleDialogValue = (row)=>{
- if(isNull(row)){
- dialogTitle.value='新增用户'
- dialogTableValue.value={}
- }else{
- dialogTitle.value='编辑用户'
- dialogTableValue.value=JSON.parse(JSON.stringify(row))
- }
- dialogVisible.value=true
- }
- const delUser = (row) =>{
- ElMessageBox.confirm(
- '您确定要删除改数据吗?',
- '删除',
- {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- }
- )
- .then(async () => {
- await deleteUser(row.id)
- getUsersTableData()
- ElMessage({
- type: 'success',
- message: '删除成功!',
- })
- })
- .catch(() => {
- ElMessage({
- type: 'info',
- message: '取消删除!',
- })
- })
- }
- </script>
-
- <style lang="scss" scoped>
- .users{
- width: 100%;
- height: 100%;
- box-sizing: border-box;
- :deep(.el-card__body){
- display: flex;
- flex-direction: column;
- width: 100%;
- height: 100%;
- .top{
- flex-shrink: 0;
- width: 100%;
- height: 60px;
- display: flex;
- align-items: center;
- .search_box{
- width: 300px;
- margin-right: 15px;
- }
- }
- .bottom{
- width: 100%;
- flex:1;
- display: flex;
- flex-direction: column;
- .users_table{
- flex:1;
- }
- .el-pagination{
- justify-content: flex-end;
- }
- }
- }
-
- }
- </style>

- <template>
- <el-dialog
- :model-value="dialogVisible"
- :title="dialogTitle"
- width="30%"
- @close="handleClose"
- >
- <el-form ref="formRef" :model="form" label-width="70px" :rules="rules">
- <el-form-item label="用户名" prop="username">
- <el-input v-model="form.username" />
- </el-form-item>
- <el-form-item label="密码" prop="password" v-if="dialogTitle==='新增用户'">
- <el-input type="password" v-model="form.password" />
- </el-form-item>
- <el-form-item label="邮箱" prop="email">
- <el-input v-model="form.email" />
- </el-form-item>
- <el-form-item label="手机号" prop="mobile">
- <el-input type="number" v-model="form.mobile" />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="handleClose">取消</el-button>
- <el-button type="primary" @click="handleConfirm"
- >保存</el-button
- >
- </span>
- </template>
- </el-dialog>
- </template>
- <script setup>
- import { ref,reactive,defineEmits,defineProps,watch} from 'vue'
- import { addUser,editUser } from "@/api/users"
- import { ElMessage } from 'element-plus'
- const formRef = ref(null)
- let form = reactive({
- username:'',
- password:'',
- email:'',
- mobile:''
- })
- const rules = reactive({
- username:[
- { required: true, message: '请输入用户名', trigger: 'blur' },
- ],
- password:[
- { required: true, message: '请输入密码', trigger: 'blur' },
- ],
- email:[
- { required: true, message: '请输入邮箱', trigger: 'blur' },
- ],
- mobile:[
- { required: true, message: '请输入手机号', trigger: 'blur' },
- ],
- })
- const props = defineProps({
- dialogTitle:{
- type:String,
- default:'',
- required:true
- },
- dialogTableValue:{
- type:Object,
- default:()=>{}
- }
- })
-
- const emits= defineEmits(['update:modelValue','initUserList'])
- const handleClose =()=>{
- emits('update:modelValue',false)
- }
- const handleConfirm = ()=>{
- formRef.value.validate(async (valid) => {
- if (valid) {
- props.dialogTitle==='新增用户'?await addUser(form): await editUser(form)
- props.dialogTitle==='新增用户'? ElMessage({
- message: '创建成功',
- type: 'success',
- }):ElMessage({
- message: '更新成功',
- type: 'success',
- })
-
- emits('initUserList')
- handleClose()
- } else {
- console.log('error submit!!')
- return false
- }
- })
- }
- watch(()=>props.dialogTableValue,()=>{
- console.log('1',props.dialogTableValue);
- form=props.dialogTableValue
- },{deep:true,immediate:true})
- </script>
- <style lang='scss' scoped>
- </style>

cnpm i dayjs --save
-
- import dayjs from 'dayjs'
-
- const filterTimes = (val,format='YYYY-MM-DD') =>{
- if(!isNull(val)){
- val = parseInt(val)*1000
- return dayjs(val).format(format)
- }else{
- return '--'
- }
- }
-
- export const isNull = (val)=>{
- if(!val) return true
- if(JSON.stringify(val)==="{}") return true
- if(JSON.stringify(val)==="[]") return true
- }
-
- export default (app)=>{
- app.config.globalProperties.$filters={
- filterTimes
- }
- }

- import filters from './utils/filters'
- filters(app)
- <template v-slot="{row}" v-else-if="item.prop=='create_time'">
- {{$filters.filterTimes(row.create_time)}}
- </template>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。