feat: adding user manager function

This commit is contained in:
Wzp-2008 2023-04-22 15:53:54 +08:00
parent 71384106a7
commit 0638732c73
22 changed files with 555 additions and 253 deletions

View File

@ -4,7 +4,7 @@ NODE_ENV = development
# 下面是自定义的环境变量,可以修改(命名必须以 VITE_ 开头)
## 后端接口公共路径(如果解决跨域问题采用反向代理就只需写公共路径)
VITE_BASE_API = '/api/v1'
VITE_BASE_API = 'http://192.168.1.1:8001'
## 路由模式 hash 或 html5
VITE_ROUTER_HISTORY = 'hash'

View File

@ -36,8 +36,10 @@
"path-to-regexp": "^6.2.1",
"pinia": "^2.0.33",
"screenfull": "^6.0.2",
"ts-md5": "^1.3.1",
"vue": "^3.2.47",
"vue-router": "^4.1.6",
"vue3-slide-verify": "^1.1.4",
"vxe-table": "^4.3.10",
"vxe-table-plugin-element": "^3.0.6",
"xe-utils": "^3.5.7"

17
pnpm-lock.yaml generated
View File

@ -37,12 +37,18 @@ dependencies:
screenfull:
specifier: ^6.0.2
version: 6.0.2
ts-md5:
specifier: ^1.3.1
version: 1.3.1
vue:
specifier: ^3.2.47
version: 3.2.47
vue-router:
specifier: ^4.1.6
version: 4.1.6(vue@3.2.47)
vue3-slide-verify:
specifier: ^1.1.4
version: 1.1.4
vxe-table:
specifier: ^4.3.10
version: 4.3.10(vue@3.2.47)(xe-utils@3.5.7)
@ -4762,6 +4768,11 @@ packages:
resolution: {integrity: sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==}
dev: true
/ts-md5@1.3.1:
resolution: {integrity: sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==}
engines: {node: '>=12'}
dev: false
/tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
dev: true
@ -5151,6 +5162,12 @@ packages:
typescript: 4.9.5
dev: true
/vue3-slide-verify@1.1.4:
resolution: {integrity: sha512-er2d9TSPsF5CcmoxBfP6eSkc4IHROwXu6Ytghkwf52apXpXy2ZtW3tOgHnmFWb3GHESKxZ9bsFXqSX+fJ6hjrg==}
dependencies:
vue: 3.2.47
dev: false
/vue@3.2.47:
resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==}
dependencies:

View File

@ -1,36 +0,0 @@
/** 模拟接口响应数据 */
const SELECT_RESPONSE_DATA = {
code: 0,
data: [
{
label: "苹果",
value: 1
},
{
label: "香蕉",
value: 2
},
{
label: "橘子",
value: 3,
disabled: true
}
],
message: "获取 Select 数据成功"
}
/** 模拟接口 */
export function getSelectDataApi() {
return new Promise<typeof SELECT_RESPONSE_DATA>((resolve, reject) => {
// 模拟接口响应时间 2s
setTimeout(() => {
// 模拟接口调用成功
if (Math.random() < 0.8) {
resolve(SELECT_RESPONSE_DATA)
} else {
// 模拟接口调用出错
reject(new Error("接口发生错误"))
}
}, 2000)
})
}

View File

@ -1,24 +0,0 @@
/** 模拟接口响应数据 */
const SUCCESS_RESPONSE_DATA = {
code: 0,
data: {},
message: "获取成功"
}
/** 模拟请求接口成功 */
export function getSuccessApi() {
return new Promise<typeof SUCCESS_RESPONSE_DATA>((resolve) => {
setTimeout(() => {
resolve(SUCCESS_RESPONSE_DATA)
}, 1000)
})
}
/** 模拟请求接口失败 */
export function getErrorApi() {
return new Promise((_resolve, reject) => {
setTimeout(() => {
reject(new Error("发生错误"))
}, 1000)
})
}

View File

@ -1,5 +1,8 @@
import { request } from "@/utils/service"
import type * as Login from "./types/login"
import type * as Requests from "@/api/types/requests"
import { Md5 } from "ts-md5"
import type * as AxiosType from "axios"
/** 获取登录验证码 */
export function getLoginCodeApi() {
@ -11,10 +14,20 @@ export function getLoginCodeApi() {
/** 登录并返回 Token */
export function loginApi(data: Login.ILoginRequestData) {
return request<Login.LoginResponseData>({
url: "users/login",
const md5er = new Md5()
md5er.appendStr(data.password)
data.password = md5er.end(false) as string
const urlSearchParams = new URLSearchParams()
urlSearchParams.append("username", data.username)
urlSearchParams.append("password", data.password)
return request<AxiosType.AxiosResponse<Requests.IResponseData<Login.ILoginResponseData>>>({
url: "api/auth/login",
method: "post",
data
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: urlSearchParams,
withCredentials: true
})
}

View File

@ -1,14 +1,11 @@
export interface ILoginRequestData {
/** admin 或 editor */
username: "admin" | "editor"
/** 密码 */
username: string
password: string
/** 验证码 */
code: string
}
export type LoginCodeResponseData = IApiResponseData<string>
export type LoginResponseData = IApiResponseData<{ token: string }>
export type UserInfoResponseData = IApiResponseData<{ username: string; roles: string[] }>
export interface ILoginResponseData {
message: string
id: number
role: number
cookie: string
}

26
src/api/register/index.ts Normal file
View File

@ -0,0 +1,26 @@
import * as Register from "@/api/register/types/register"
import { Md5 } from "ts-md5"
import { request } from "@/utils/service"
import * as AxiosType from "axios/index"
import * as Requests from "@/api/types/requests"
export function registerApi(data: Register.IRegisterRequestData) {
const md5er = new Md5()
md5er.appendStr(data.password)
data.password = md5er.end(false) as string
const requestData = new URLSearchParams()
requestData.append("username", data.username)
requestData.append("password", data.password)
requestData.append("relName", data.relName)
requestData.append("email", data.email)
requestData.append("roleId", data.roleId.toString())
return request<AxiosType.AxiosResponse<Requests.IResponseData<Register.IRegisterResponseData>>>({
url: "/api/auth/register",
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: requestData,
withCredentials: true
})
}

View File

@ -0,0 +1,7 @@
export interface IRegisterRequestData {
username: string
email: string
relName: string
password: string
roleId: number
}

11
src/api/types/requests.ts Normal file
View File

@ -0,0 +1,11 @@
export interface IResponseData<T> {
timestamp: number
message: string
status: number
data: T
}
export interface IPageResponseData<T> {
total: number
info: T[]
}

51
src/api/users/index.ts Normal file
View File

@ -0,0 +1,51 @@
import { request } from "@/utils/service"
import * as AxiosType from "axios"
import * as Requests from "@/api/types/requests"
import {
IChangeUserStatus,
IDelUserResponseData,
IExChangeAccountRequestsData,
IExChangeUserResponseData,
IGetAllUserResponseData
} from "@/api/users/types/users"
export function getAllUsersApi(page: number) {
return request<AxiosType.AxiosResponse<Requests.IResponseData<IGetAllUserResponseData>>>({
url: "/account/getUser",
method: "get",
params: {
page: page,
num: 10
},
withCredentials: true
})
}
export function delUserApi(userId: number) {
return request<AxiosType.AxiosResponse<Requests.IResponseData<IDelUserResponseData>>>({
url: "/account/delUser",
method: "get",
params: {
id: userId
},
withCredentials: true
})
}
export function exChangeUserDataApi(newUser: IExChangeAccountRequestsData) {
return request<AxiosType.AxiosResponse<Requests.IResponseData<IExChangeUserResponseData>>>({
url: "/account/changeAccount",
method: "post",
data: newUser,
withCredentials: true
})
}
export function changeUserStatusApi(userid: number, newStatus: boolean) {
return request<AxiosType.AxiosResponse<Requests.IResponseData<IChangeUserStatus>>>({
url: "/account/changeStatus",
method: "get",
params: {
userId: userid,
status: newStatus
},
withCredentials: true
})
}

View File

@ -0,0 +1,15 @@
import { IAccount } from "@/views/user/types"
import { IPageResponseData } from "@/api/types/requests"
export interface IExChangeAccountRequestsData {
id: number
username: string
email: string
relName: string
roleId: number
}
export type IDelUserResponseData = string
export type IExChangeUserResponseData = string
export type IChangeUserStatus = string
export type IGetAllUserResponseData = IPageResponseData<IAccount>

View File

@ -0,0 +1,3 @@
export interface OrderData {
id: number
}

View File

@ -55,16 +55,41 @@ export const constantRoutes: RouteRecordRaw[] = [
}
}
]
}
]
/**
*
* (Roles )
* Name
*/
export const asyncRoutes: RouteRecordRaw[] = [
{
path: "/user",
component: Layout,
redirect: "/user",
children: [
{
path: "user",
component: () => import("@/views/user/index.vue"),
name: "User",
meta: {
title: "用户管理",
elIcon: "User",
roles: ["admin"]
}
}
]
},
{
path: "/order",
component: Layout,
redirect: "/order/",
redirect: "/order/manager",
name: "Order",
meta: {
title: "订单",
elIcon: "Files",
roles: ["admin", "editor"],
roles: ["admin"],
alwaysShow: true
},
children: [
@ -74,7 +99,7 @@ export const constantRoutes: RouteRecordRaw[] = [
name: "CreateOrder",
meta: {
title: "创建订单",
roles: ["editor"],
roles: ["admin", "editor"],
elIcon: "FolderAdd"
}
},
@ -89,15 +114,7 @@ export const constantRoutes: RouteRecordRaw[] = [
}
}
]
}
]
/**
*
* (Roles )
* Name
*/
export const asyncRoutes: RouteRecordRaw[] = [
},
{
path: "/permission",
component: Layout,

View File

@ -15,7 +15,7 @@ router.beforeEach(async (to, _from, next) => {
const userStore = useUserStoreHook()
const permissionStore = usePermissionStoreHook()
// 判断该用户是否登录
if (getToken()) {
if (getToken() && getToken() !== -1) {
if (to.path === "/login") {
// 如果已经登录,并准备进入 Login 页面,则重定向到主页
next({ path: "/" })

View File

@ -5,13 +5,14 @@ import { usePermissionStore } from "./permission"
import { useTagsViewStore } from "./tags-view"
import { getToken, removeToken, setToken } from "@/utils/cache/cookies"
import router, { resetRouter } from "@/router"
import { loginApi, getUserInfoApi } from "@/api/login"
import { loginApi } from "@/api/login"
import { type ILoginRequestData } from "@/api/login/types/login"
import { type RouteRecordRaw } from "vue-router"
import asyncRouteSettings from "@/config/async-route"
export const useUserStore = defineStore("user", () => {
const token = ref<string>(getToken() || "")
/** Token即用户id */
const token = ref<number>(getToken() || -1)
const roles = ref<string[]>([])
const username = ref<string>("")
@ -25,15 +26,14 @@ export const useUserStore = defineStore("user", () => {
/** 登录 */
const login = (loginData: ILoginRequestData) => {
return new Promise((resolve, reject) => {
loginApi({
username: loginData.username,
password: loginData.password,
code: loginData.code
})
loginApi(loginData)
.then((res) => {
setToken(res.data.token)
token.value = res.data.token
resolve(true)
if (res.status == 202) {
setToken(res.data.data.id)
resolve(res.data.data)
return
}
reject(res.data.data)
})
.catch((error) => {
reject(error)
@ -42,30 +42,31 @@ export const useUserStore = defineStore("user", () => {
}
/** 获取用户详情 */
const getInfo = () => {
return new Promise((resolve, reject) => {
getUserInfoApi()
.then((res) => {
const data = res.data
username.value = data.username
// 验证返回的 roles 是否是一个非空数组
if (data.roles && data.roles.length > 0) {
roles.value = data.roles
} else {
// 塞入一个没有任何作用的默认角色,不然路由守卫逻辑会无限循环
roles.value = asyncRouteSettings.defaultRoles
}
resolve(res)
})
.catch((error) => {
reject(error)
})
})
roles.value = asyncRouteSettings.defaultRoles
roles.value = ["admin"]
console.log("getInfo()")
// return new Promise((resolve, reject) => {
// getUserInfoApi()
// .then((res) => {
// const data = res.data
// username.value = data.username
// // 验证返回的 roles 是否是一个非空数组
// if (data.roles && data.roles.length > 0) {
// roles.value = data.roles
// } else {
// // 塞入一个没有任何作用的默认角色,不然路由守卫逻辑会无限循环
// roles.value = asyncRouteSettings.defaultRoles
// }
// resolve(res)
// })
// .catch((error) => {
// reject(error)
// })
// })
}
/** 切换角色 */
const changeRoles = async (role: string) => {
const newToken = "token-" + role
token.value = newToken
setToken(newToken)
console.log(role)
await getInfo()
permissionStore.setRoutes(roles.value)
resetRouter()

View File

@ -3,11 +3,14 @@
import CacheKey from "@/constants/cacheKey"
import Cookies from "js-cookie"
export const getToken = () => {
return Cookies.get(CacheKey.TOKEN)
export const getToken = (): number | undefined => {
const tokenString = Cookies.get(CacheKey.TOKEN)
if (tokenString) {
return Number.parseInt(tokenString)
}
}
export const setToken = (token: string) => {
Cookies.set(CacheKey.TOKEN, token)
export const setToken = (token: number) => {
Cookies.set(CacheKey.TOKEN, token.toString())
}
export const removeToken = () => {
Cookies.remove(CacheKey.TOKEN)

View File

@ -1,8 +1,8 @@
import axios, { type AxiosInstance, type AxiosRequestConfig } from "axios"
import { useUserStoreHook } from "@/store/modules/user"
import { ElMessage } from "element-plus"
import { get } from "lodash-es"
import { getToken } from "./cache/cookies"
import { useRouter } from "vue-router"
import { useUserStore } from "@/store/modules/user"
import { ElMessage } from "element-plus"
/** 创建请求实例 */
function createService() {
@ -10,77 +10,25 @@ function createService() {
const service = axios.create()
// 请求拦截
service.interceptors.request.use(
(config) => config,
(config) => {
return config
},
// 发送失败
(error) => Promise.reject(error)
)
// 响应拦截(可根据具体业务作出相应的调整)
service.interceptors.response.use(
(response) => {
// apiData 是 API 返回的数据
const apiData = response.data as any
// 这个 Code 是和后端约定的业务 Code
const code = apiData.code
// 如果没有 Code, 代表这不是项目后端开发的 API
if (code === undefined) {
ElMessage.error("非本系统的接口")
return Promise.reject(new Error("非本系统的接口"))
} else {
switch (code) {
case 0:
// code === 0 代表没有错误
return apiData
default:
// 不是正确的 Code
ElMessage.error(apiData.message || "Error")
return Promise.reject(new Error("Error"))
}
}
return response
},
(error) => {
// Status 是 HTTP 状态码
const status = get(error, "response.status")
switch (status) {
case 400:
error.message = "请求错误"
break
case 401:
// Token 过期时,直接退出登录并强制刷新页面(会重定向到登录页)
useUserStoreHook().logout()
location.reload()
break
case 403:
error.message = "拒绝访问"
break
case 404:
error.message = "请求地址出错"
break
case 408:
error.message = "请求超时"
break
case 500:
error.message = "服务器内部错误"
break
case 501:
error.message = "服务未实现"
break
case 502:
error.message = "网关错误"
break
case 503:
error.message = "服务不可用"
break
case 504:
error.message = "网关超时"
break
case 505:
error.message = "HTTP 版本不受支持"
break
default:
break
if (error.response.status === 555) {
const router = useRouter()
const userStore = useUserStore()
userStore.logout()
router.push("/login").then((_) => ElMessage.warning("登录过期,请重新登录!"))
}
ElMessage.error(error.message)
return Promise.reject(error)
return error
}
)
return service
@ -91,13 +39,12 @@ function createRequestFunction(service: AxiosInstance) {
return function <T>(config: AxiosRequestConfig): Promise<T> {
const configDefault = {
headers: {
// 携带 Token
Authorization: "Bearer " + getToken(),
"Content-Type": get(config, "headers.Content-Type", "application/json")
},
timeout: 5000,
baseURL: import.meta.env.VITE_BASE_API,
data: {}
data: {},
widthCredentials: true
}
return service(Object.assign(configDefault, config))
}

View File

@ -2,76 +2,89 @@
import { reactive, ref } from "vue"
import { useRouter } from "vue-router"
import { useUserStore } from "@/store/modules/user"
import { User, Lock, Key, Picture, Loading } from "@element-plus/icons-vue"
import { User, Lock } from "@element-plus/icons-vue"
import ThemeSwitch from "@/components/ThemeSwitch/index.vue"
import { type FormInstance, FormRules } from "element-plus"
import { getLoginCodeApi } from "@/api/login"
import { ElMessage, type FormInstance, FormRules } from "element-plus"
import SlideVerify, { SlideVerifyInstance } from "vue3-slide-verify"
import "vue3-slide-verify/dist/style.css"
import { type ILoginRequestData } from "@/api/login/types/login"
const isShowVerifyDialog = ref<boolean>(false)
const router = useRouter()
const loginFormRef = ref<FormInstance | null>(null)
/** 登录按钮 Loading */
const loading = ref(false)
/** 验证码图片 URL */
const codeUrl = ref("")
/** 登录表单数据 */
const loginForm: ILoginRequestData = reactive({
username: "admin",
password: "12345678",
code: ""
username: "",
password: ""
})
/** 登录表单校验规则 */
const loginFormRules: FormRules = {
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
{ min: 3, max: 10, message: "长度在 3 到 10 个字符", trigger: "blur" },
{ pattern: "[A-Za-z0-9_-一-龥]+", message: "用户名不符合要求" }
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{ min: 8, max: 16, message: "长度在 8 到 16 个字符", trigger: "blur" }
],
code: [{ required: true, message: "请输入验证码", trigger: "blur" }]
]
}
/** 登录逻辑 */
const handleLogin = () => {
loginFormRef.value?.validate((valid: boolean) => {
if (valid) {
loading.value = true
useUserStore()
.login({
username: loginForm.username,
password: loginForm.password,
code: loginForm.code
})
.then(() => {
router.push({ path: "/" })
})
.catch(() => {
createCode()
loginForm.password = ""
})
.finally(() => {
loading.value = false
})
sliderVerify.value?.refresh()
isShowVerifyDialog.value = true
} else {
return false
}
})
}
/** 创建验证码 */
const createCode = () => {
//
loginForm.code = ""
//
codeUrl.value = ""
getLoginCodeApi().then((res) => {
codeUrl.value = res.data
})
const sliderVerify = ref<SlideVerifyInstance>()
const onVerifyRetry = () => {
ElMessage.warning("检测到非人为操作的哦! try again")
}
/** 初始化验证码 */
createCode()
const onVerifySuccess = (_: number) => {
isShowVerifyDialog.value = false
loading.value = true
useUserStore()
.login({
username: loginForm.username,
password: loginForm.password
})
.then(() => {
router.push({ path: "/" })
ElMessage.success("登录成功!")
})
.catch(() => {
loginForm.password = ""
ElMessage.error("登录失败,用户名或密码错误")
})
.finally(() => {
loading.value = false
})
}
const onVerifyFail = () => {
ElMessage.error("验证失败,请重试!")
}
</script>
<template>
<el-dialog v-model="isShowVerifyDialog" title="滑动验证" width="350px" align-center>
<slide-verify
ref="sliderVerify"
slider-text="向右滑动->"
:accuracy="4"
@again="onVerifyRetry"
@success="onVerifySuccess"
@fail="onVerifyFail"
/>
</el-dialog>
<div class="login-container">
<ThemeSwitch class="theme-switch" />
<div class="login-card">
@ -101,28 +114,6 @@ createCode()
show-password
/>
</el-form-item>
<el-form-item prop="code">
<el-input
v-model.trim="loginForm.code"
placeholder="验证码"
type="text"
tabindex="3"
:prefix-icon="Key"
maxlength="7"
size="large"
>
<template #append>
<el-image :src="codeUrl" @click="createCode" draggable="false">
<template #placeholder>
<el-icon><Picture /></el-icon>
</template>
<template #error>
<el-icon><Loading /></el-icon>
</template>
</el-image>
</template>
</el-input>
</el-form-item>
<el-button :loading="loading" type="primary" size="large" @click.prevent="handleLogin"> </el-button>
</el-form>
</div>

View File

@ -1,5 +1,24 @@
<template><div>OrderManger</div></template>
<template>
<el-table :data="tableShowData" style="width: 100%">
<el-table-column label="Date" prop="date" />
<el-table-column label="Name" prop="name" />
<el-table-column align="right">
<template #header>
<el-input v-model="search" size="small" placeholder="Type to search" />
</template>
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">Edit</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">Delete</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script lang="ts" setup></script>
<script lang="ts" setup>
import { ref } from "vue"
import { OrderData } from "@/entities/OrderData"
const tableShowData = ref<OrderData[]>([])
</script>
<style scoped></style>

233
src/views/user/index.vue Normal file
View File

@ -0,0 +1,233 @@
<template>
<div class="userDiv">
<div class="userActionDiv">
<el-button type="warning" size="default" @click="handleAddAccountButtonClick">添加</el-button>
</div>
<div class="userTableDiv">
<el-table :data="showTableData" style="width: 100%" table-layout="auto">
<el-table-column fixed prop="id" label="ID" />
<el-table-column prop="username" label="用户名" />
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="relName" label="真实姓名" />
<el-table-column label="状态">
<template #default="scope">
<el-switch :model-value="scope.row.status" @change="handleChangeUserStatus(scope.$index)" />
</template>
</el-table-column>
<el-table-column prop="roleId" label="权限等级" />
<el-table-column fixed="right" label="操作">
<template #default="scope">
<el-button
v-if="getToken() !== scope.row.id"
link
type="primary"
size="small"
@click="handleDelete(scope.row)"
>删除</el-button
>
<el-button link type="primary" size="small" @click="handleExchange(scope.row)">修改</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="userPaginationDiv">
<el-pagination
background
layout="prev, pager, next"
:total="totalUserCount"
:current-page="nowPage"
@update:current-page="handleChangePage"
/>
</div>
<el-dialog v-model="addAccountDialogShow" :title="getDialogTitle()" width="30%" align-center>
<el-form ref="addAccountFormRef" :model="addAccountData" status-icon :rules="addAccountRules" label-width="120px">
<el-form-item label="用户名" prop="username">
<el-input v-model="addAccountData.username" autocomplete="off" />
</el-form-item>
<el-form-item label="密码" prop="password" v-if="!modifyMode">
<el-input v-model="addAccountData.password" show-password autocomplete="off" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="addAccountData.email" autocomplete="off" />
</el-form-item>
<el-form-item label="真实姓名" prop="relName">
<el-input v-model="addAccountData.relName" autocomplete="off" />
</el-form-item>
<el-form-item label="权限等级" prop="roleId">
<el-input v-model.number="addAccountData.roleId" type="number" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="addAccountDialogShow = false">取消</el-button>
<el-button type="primary" @click="handleAddAccount">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue"
import { IAccount } from "@/views/user/types"
import { ElMessage, FormInstance, FormRules } from "element-plus"
import { IRegisterRequestData } from "@/api/register/types/register"
import { registerApi } from "@/api/register"
import { changeUserStatusApi, delUserApi, exChangeUserDataApi, getAllUsersApi } from "@/api/users"
import { getToken } from "@/utils/cache/cookies"
const showTableData = ref<IAccount[]>([])
const totalUserCount = ref<number>(1)
const nowPage = ref<number>(1)
const addAccountDialogShow = ref<boolean>(false)
const addAccountData = ref<IRegisterRequestData>({ username: "", password: "", email: "", relName: "", roleId: 0 })
const addAccountRules: FormRules = {
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
{ min: 3, max: 10, message: "长度在 3 到 10 个字符", trigger: "blur" },
{ pattern: "[A-Za-z0-9_-一-龥]+", message: "用户名不符合要求" }
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{ min: 8, max: 16, message: "长度在 8 到 16 个字符", trigger: "blur" }
],
email: [
{ required: true, message: "请输入邮箱", trigger: "blur" },
{ pattern: "\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}", message: "邮箱不符合要求" }
],
relName: [
{ required: true, message: "请输入真实姓名", trigger: "blur" },
{ min: 2, max: 6, message: "长度在 2 到 6 个字符", trigger: "blur" },
{ pattern: "[A-Za-z0-9_-一-龥]+", message: "真实姓名不符合要求" }
],
roleId: [
{ required: true, message: "请输入权限等级", trigger: "blur" },
{ type: "number", message: "权限等级必须为数字", trigger: "blur" }
]
}
const addAccountFormRef = ref<FormInstance>()
const modifyMode = ref<IAccount>()
const handleDelete = (account: IAccount) => {
delUserApi(account.id)
.then((response) => {
ElMessage.success("删除成功,请刷新!")
console.log(response)
})
.catch(() => {
ElMessage.error("删除失败,请重试!")
})
}
const handleExchange = (account: IAccount) => {
addAccountDialogShow.value = true
modifyMode.value = account
addAccountData.value.password = ""
addAccountData.value.username = account.username
addAccountData.value.email = account.email
addAccountData.value.relName = account.relName
addAccountData.value.roleId = account.roleId
}
const handleAddAccountButtonClick = () => {
addAccountDialogShow.value = true
modifyMode.value = undefined
addAccountData.value.password = ""
addAccountData.value.username = ""
addAccountData.value.email = ""
addAccountData.value.relName = ""
addAccountData.value.roleId = 0
}
const handleAddAccount = () => {
if (!addAccountFormRef.value?.validate()) {
ElMessage.error("数据存在错误!")
return
}
if (modifyMode.value) {
modifyMode.value.username = addAccountData.value.username
modifyMode.value.email = addAccountData.value.email
modifyMode.value.relName = addAccountData.value.relName
modifyMode.value.roleId = addAccountData.value.roleId
exChangeUserDataApi({
id: modifyMode.value.id,
username: modifyMode.value.username,
email: modifyMode.value.email,
relName: modifyMode.value.relName,
roleId: modifyMode.value.roleId
})
.then(() => {
ElMessage.success("修改成功")
addAccountDialogShow.value = false
})
.catch(() => {
ElMessage.error("修改失败!")
})
return
}
addAccountDialogShow.value = false
registerApi(addAccountData.value)
.then((_) => {
ElMessage.success("添加成功!")
})
.catch((err) => {
ElMessage.error("添加用户失败!,原因:" + err.message)
})
}
const handleChangePage = () => {
getAllUsersApi(nowPage.value)
.then((response) => {
showTableData.value = response.data.data.info
totalUserCount.value = response.data.data.total
})
.catch(() => {
ElMessage.error("获取用户数据失败!")
})
}
const getDialogTitle = (): string => {
if (modifyMode.value) {
return "修改用户"
}
return "添加用户"
}
const handleChangeUserStatus = (index: number) => {
if (getToken() !== showTableData.value[index].id) {
const userData = showTableData.value[index]
if (userData) {
showTableData.value[index].status = !showTableData.value[index].status
changeUserStatusApi(showTableData.value[index].id, showTableData.value[index].status)
.then(() => {
ElMessage.success("修改成功")
})
.catch((err) => {
console.log(err)
ElMessage.error("修改失败!")
})
}
} else {
ElMessage.error("用户不能修改自己的状态!")
}
}
onMounted(() => {
handleChangePage()
})
</script>
<style scoped>
.userDiv {
width: calc(100% - 20px);
height: calc(100% - 20px);
margin: 10px 10px 10px 10px;
}
.userTableDiv {
width: 100%;
height: calc(90% - 20px);
margin-top: 10px;
}
.userActionDiv {
height: 5%;
width: max-content;
min-height: 32px;
}
.userPaginationDiv {
height: 5%;
margin-top: 10px;
min-height: 32px;
}
</style>

View File

@ -0,0 +1,9 @@
export interface IAccount {
id: number
username: string
email: string
relName: string
password?: string
status: boolean
roleId: number
}