feat(账号管理): 新增账号管理模块及相关功能

- 添加账号管理API接口
- 实现账号列表、添加/编辑、删除、启用/禁用功能
- 新增账号管理路由和页面
- 添加账号类型用户权限处理
- 密码输入框增加类型保护
- 实现账号管理相关组件和样式
This commit is contained in:
34701892@qq.com 2025-12-13 11:54:48 +08:00
parent 80f428d694
commit ca6621bbda
6 changed files with 320 additions and 11 deletions

37
src/api/jnh/index.ts Normal file
View File

@ -0,0 +1,37 @@
import request from '/@/utils/request';
// 获取账号列表
export const getAccounts = (params: any) => {
return request({
url: '/jnh/accounts/page',
method: 'post',
params,
});
};
// 添加账号
export const addOrUpdate = (params: any) => {
return request({
url: '/jnh/accounts/addOrUpdate',
method: 'post',
params,
});
};
// 删除账号
export const deleteAccount = (params: any) => {
return request({
url: '/jnh/accounts/remove',
method: 'post',
params,
});
};
// 账号启用/禁用
export const updateStatus = (params: any) => {
return request({
url: '/jnh/accounts/status',
method: 'post',
params,
});
};

View File

@ -62,6 +62,21 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
icon: 'iconfont ele-Edit', icon: 'iconfont ele-Edit',
}, },
}, },
{
path: '/jindex',
name: 'jindex',
component: () => import('/@/views/pages/jnh/index.vue'),
meta: {
title: '账号管理',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: true,
isIframe: false,
roles: ['jnh'],
icon: 'ele-Edit',
},
},
{ {
path: '/index', path: '/index',
name: 'index', name: 'index',
@ -74,7 +89,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
isAffix: true, isAffix: true,
isIframe: false, isIframe: false,
roles: ['admin', 'common', 'secondCommon'], roles: ['admin', 'common', 'secondCommon'],
icon: 'iconfont ele-Edit', icon: 'ele-Edit',
}, },
}, },
{ {

View File

@ -32,16 +32,19 @@ export const useUserInfo = defineStore('userInfo', {
async getApiUserInfo() { async getApiUserInfo() {
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
let rolesLocal = '' let rolesLocal = '';
if (Local.get('userInfoLocal').userType == '00') { if (Local.get('userInfoLocal').userType == '00') {
Cookies.set('userName', 'common') Cookies.set('userName', 'common');
rolesLocal = 'common' rolesLocal = 'common';
} else if (Local.get('userInfoLocal').userType == '02') { } else if (Local.get('userInfoLocal').userType == '02') {
Cookies.set('userName', 'secondCommon') Cookies.set('userName', 'secondCommon');
rolesLocal = 'secondCommon' rolesLocal = 'secondCommon';
} else if (Local.get('userInfoLocal').userType == 'jnh') {
Cookies.set('userName', 'jnh');
rolesLocal = 'jnh';
} else { } else {
Cookies.set('userName', 'admin') Cookies.set('userName', 'admin');
rolesLocal = 'admin' rolesLocal = 'admin';
} }
// 模拟数据,请求接口时,记得删除多余代码及对应依赖的引入 // 模拟数据,请求接口时,记得删除多余代码及对应依赖的引入
@ -56,6 +59,7 @@ export const useUserInfo = defineStore('userInfo', {
// test 页面权限标识,对应路由 meta.roles用于控制路由的显示/隐藏 // test 页面权限标识,对应路由 meta.roles用于控制路由的显示/隐藏
let testRoles: Array<string> = ['common']; let testRoles: Array<string> = ['common'];
let secondCommonRoles: Array<string> = ['secondCommon']; let secondCommonRoles: Array<string> = ['secondCommon'];
let jnhRoles: Array<string> = ['jnh'];
// test 按钮权限标识 // test 按钮权限标识
let testAuthBtnList: Array<string> = ['btn.add', 'btn.link']; let testAuthBtnList: Array<string> = ['btn.add', 'btn.link'];
// 不同用户模拟不同的用户权限 // 不同用户模拟不同的用户权限
@ -65,12 +69,14 @@ export const useUserInfo = defineStore('userInfo', {
} else if (rolesLocal == 'secondCommon') { } else if (rolesLocal == 'secondCommon') {
defaultRoles = secondCommonRoles; defaultRoles = secondCommonRoles;
defaultAuthBtnList = testAuthBtnList; defaultAuthBtnList = testAuthBtnList;
} else if (rolesLocal == 'jnh') {
defaultRoles = jnhRoles;
defaultAuthBtnList = testAuthBtnList;
} else { } else {
defaultRoles = testRoles; defaultRoles = testRoles;
defaultAuthBtnList = testAuthBtnList; defaultAuthBtnList = testAuthBtnList;
} }
console.log("🚀 ~ getApiUserInfo ~ rolesLocal:", rolesLocal) console.log('🚀 ~ getApiUserInfo ~ rolesLocal:', rolesLocal);
// 用户信息模拟数据 // 用户信息模拟数据
const userInfos = { const userInfos = {

View File

@ -9,7 +9,7 @@
</el-form-item> </el-form-item>
<el-form-item class="login-animation1"> <el-form-item class="login-animation1">
<el-input text class="code_input" placeholder="请输入密码" v-model="state.ruleForm.captcha" clearable> <el-input text class="code_input" type="password" placeholder="请输入密码" v-model="state.ruleForm.captcha" clearable>
<!-- <template #append> <!-- <template #append>
<view @click="doSendCode"> <view @click="doSendCode">
<text v-if="!isSendCode" class="sendcode">发送验证码</text> <text v-if="!isSendCode" class="sendcode">发送验证码</text>
@ -189,6 +189,8 @@ const signInSuccess = (isNoPower: boolean | undefined) => {
// //
router.push('/richeditMobile'); router.push('/richeditMobile');
} else if (Session.get('userInfoLocal').userType == 'jnh') {
router.push('/jindex');
} else { } else {
// PC // PC
if (route.query?.redirect && route.query?.redirect != '/richeditMobile') { if (route.query?.redirect && route.query?.redirect != '/richeditMobile') {

View File

@ -0,0 +1,79 @@
<template>
<div class="index">
<el-dialog v-model="dialogTableVisible" :title="form.id ? '账号编辑' : '账号创建'" width="600" @closed="closeDialog">
<el-form :model="form" label-width="auto" :rules="rules" ref="ruleFormRef">
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="部门名称" prop="department">
<el-input v-model="form.department" placeholder="请输入部门名称" />
</el-form-item>
<el-form-item label="手机号码" prop="mobile">
<el-input v-model="form.mobile" placeholder="请输入手机号码" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="submit()"> 确认 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="loginIndex">
import { onMounted, reactive, ref } from 'vue';
import { NextLoading } from '/@/utils/loading';
import { ElMessage, ElMessageBox } from 'element-plus';
import { addOrUpdate } from '/@/api/jnh';
const emit = defineEmits(['close']);
const rules = reactive({
name: [{ required: true, message: '必须填写姓名', trigger: 'blur' }],
mobile: [{ required: true, message: '必须填写手机号码', trigger: 'blur' }],
});
const form = ref({});
const dialogTableVisible = ref(false);
function open(data) {
console.log('🚀 ~ open ~ data:', data);
dialogTableVisible.value = true;
if (data) {
form.value = data;
}
}
function close() {
dialogTableVisible.value = false;
emit('close');
}
const ruleFormRef = ref(null);
async function submit() {
await ruleFormRef.value.validate();
addOrUpdate(form.value).then((res) => {
if (res.code === 200) {
ElMessage.success('操作成功');
close();
} else {
ElMessage.error(res.msg || '操作失败');
}
});
}
//
onMounted(() => {
NextLoading.done();
});
defineExpose({
open,
close,
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,170 @@
<template>
<div class="index">
<div class="form">
<div class="form_button">
<el-button type="primary" icon="Plus" @click="addOpen">创建</el-button>
</div>
<el-form class="form_content">
<el-form-item label="手机号" prop="mobile">
<el-input v-model="form.mobile" placeholder="输入手机号进行搜索" class="input">
<template #append>
<el-button icon="Search" @click="getData" />
</template>
</el-input>
</el-form-item>
<el-form-item label="部门" prop="department">
<el-input v-model="form.department" placeholder="输入部门进行搜索" class="input">
<template #append>
<el-button icon="Search" @click="seagetDatarch" />
</template>
</el-input>
</el-form-item>
<el-form-item label="账号状态" prop="status">
<el-select v-model="form.status" placeholder="请选择账号状态" class="input" @change="getData">
<el-option label="禁用" :value="0" />
<el-option label="启用" :value="1" />
</el-select>
</el-form-item>
</el-form>
</div>
<tableComponents :tableData="tableData">
<el-table-column prop="name" label="姓名" align="center" />
<el-table-column prop="mobile" label="手机号" align="center" />
<el-table-column prop="createTime" label="创建时间" align="center" />
<el-table-column prop="updateTime" label="更新时间" align="center" />
<el-table-column prop="status" label="账号状态" align="center" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="text" @click="addOpen(scope.row)">编辑</el-button>
<el-button type="text" @click="doDeleteAccount(scope.row)">删除</el-button>
<el-button type="text" v-if="scope.row.status == 0" @click="doUpdateStatus(scope.row)">启用</el-button>
<el-button type="text" v-else @click="doUpdateStatus(scope.row)">禁用</el-button>
</template>
</el-table-column>
</tableComponents>
<addDialog ref="addDialogRef" @close="getData"></addDialog>
</div>
</template>
<script setup lang="ts" name="loginIndex">
import { onMounted, ref, reactive } from 'vue';
import { NextLoading } from '/@/utils/loading';
import { getAccounts, deleteAccount, updateStatus } from '/@/api/jnh';
import tableComponents from '/@/components/tableComponents/index.vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import addDialog from '/@/views/pages/jnh/add.vue';
const form = reactive({
keyword: '',
});
const tableData = reactive({
data: [
{
name: 'string',
mobile: 'string',
createTime: 'string',
updateTime: 'string',
status: '0',
department: 'sdfa',
},
{
name: 'string',
mobile: 'string',
createTime: 'string',
updateTime: 'string',
status: '1',
department: 'sdfa',
},
],
total: 0,
page: 1,
size: 20,
});
const addDialogRef = ref(addDialog);
//
function addOpen(data) {
addDialogRef.value.open(data);
}
async function getData() {
let { code, data } = await getAccounts({
page: tableData.page,
size: tableData.size,
...form,
});
if (code == 200) {
tableData.data = data.list;
tableData.total = data.total;
}
}
async function doDeleteAccount(row) {
let { code, data } = await deleteAccount({
id: row.id,
});
if (code == 200) {
ElMessage.success('删除成功');
getData();
}
}
async function doUpdateStatus(row) {
let { code, data } = await updateStatus({
id: row.id,
status: row.status == 0 ? 1 : 0,
});
if (code == 200) {
ElMessage.success('操作成功');
getData();
}
}
//
onMounted(() => {
NextLoading.done();
getData();
});
</script>
<style scoped lang="scss">
.index {
background-color: white;
padding: 30px;
border-radius: 10px;
}
.form {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
.input {
min-width: 150px;
}
.form_content {
display: flex;
gap: 20px;
align-items: center;
}
:deep(.el-form-item--large) {
display: flex;
align-items: center;
margin-bottom: 0;
}
:deep(.el-form-item--label-right .el-form-item__label) {
font-weight: bold;
color: #3e3e3e;
}
}
</style>