新增用户管理功能
- 新增用户列表页面,支持用户查询和筛选 - 完善侧边栏菜单导航功能 - 添加用户管理相关API接口 - 更新权限系统支持用户管理路由 - 新增用户管理国际化文案
This commit is contained in:
parent
2b4b9a2e9c
commit
aa512e6154
@ -49,6 +49,30 @@
|
||||
"errors": {
|
||||
"label_error": "错误页",
|
||||
"text_back_to_home": "返回首页"
|
||||
},
|
||||
"user_management": {
|
||||
"label_user_management": "用户管理",
|
||||
"label_user_list": "用户列表",
|
||||
"label_phone": "手机号",
|
||||
"label_wechat": "微信号",
|
||||
"label_register_time": "注册时间",
|
||||
"label_remark": "备注",
|
||||
"label_data_count": "创建数据数量",
|
||||
"label_status": "状态",
|
||||
"label_actions": "操作",
|
||||
"button_detail": "详情",
|
||||
"button_freeze": "冻结",
|
||||
"button_unfreeze": "解冻",
|
||||
"button_add_user": "新建用户",
|
||||
"placeholder_phone": "请输入手机号",
|
||||
"placeholder_wechat": "请输入微信号",
|
||||
"placeholder_remark": "请输入备注",
|
||||
"message_phone_required": "请输入手机号",
|
||||
"message_phone_format_error": "请输入正确的手机号格式",
|
||||
"message_freeze_success": "已冻结该用户",
|
||||
"message_unfreeze_success": "已解冻该用户",
|
||||
"text_status_normal": "正常",
|
||||
"text_status_frozen": "冻结"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
|
||||
@ -39,4 +39,10 @@ export default {
|
||||
deleteDept: (params = {}) => request.delete('/dept/delete', { params }),
|
||||
// auditlog
|
||||
getAuditLogList: (params = {}) => request.get('/auditlog/list', { params }),
|
||||
// app users (客户端用户管理) - 使用现有的后端接口
|
||||
getAppUserList: (params = {}) => request.get('/app-user/list', { params }),
|
||||
getAppUserById: (params = {}) => request.get('/app-user/detail', { params }),
|
||||
createAppUser: (data = {}) => request.post('/app-user/register', data),
|
||||
updateAppUser: (data = {}) => request.post('/app-user/update', data),
|
||||
deleteAppUser: (params = {}) => request.delete('/app-user/delete', { params }),
|
||||
}
|
||||
|
||||
@ -95,9 +95,11 @@ function getIcon(meta) {
|
||||
}
|
||||
|
||||
function handleMenuSelect(key, item) {
|
||||
console.log("🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥 ~ handleMenuSelect ~ key, item:", key, item);
|
||||
if (isExternal(item.path)) {
|
||||
window.open(item.path)
|
||||
} else {
|
||||
console.log("🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥 ~ handleMenuSelect ~ item.path === curRoute.path:", item.path === curRoute.path);
|
||||
if (item.path === curRoute.path) {
|
||||
appStore.reloadPage()
|
||||
} else {
|
||||
|
||||
@ -6,10 +6,17 @@ import api from '@/api'
|
||||
// * 后端路由相关函数
|
||||
// 根据后端传来数据构建出前端路由
|
||||
|
||||
function getRouteName(route) {
|
||||
if (route?.id) return `menu-${route.id}`
|
||||
if (route?.path) return route.path.replace(/\//g, '-') || route.name
|
||||
return route?.name
|
||||
}
|
||||
|
||||
function buildRoutes(routes = []) {
|
||||
return routes.map((e) => {
|
||||
const routeName = getRouteName(e)
|
||||
const route = {
|
||||
name: e.name,
|
||||
name: routeName,
|
||||
path: e.path,
|
||||
component: shallowRef(Layout),
|
||||
isHidden: e.is_hidden,
|
||||
@ -26,7 +33,7 @@ function buildRoutes(routes = []) {
|
||||
if (e.children && e.children.length > 0) {
|
||||
// 有子菜单
|
||||
route.children = e.children.map((e_child) => ({
|
||||
name: e_child.name,
|
||||
name: getRouteName(e_child),
|
||||
path: e_child.path,
|
||||
component: vueModules[`/src/views${e_child.component}/index.vue`],
|
||||
isHidden: e_child.is_hidden,
|
||||
@ -40,7 +47,7 @@ function buildRoutes(routes = []) {
|
||||
} else {
|
||||
// 没有子菜单,创建一个默认的子路由
|
||||
route.children.push({
|
||||
name: `${e.name}Default`,
|
||||
name: `${routeName}Default`,
|
||||
path: '',
|
||||
component: vueModules[`/src/views${e.component}/index.vue`],
|
||||
isHidden: true,
|
||||
@ -49,6 +56,7 @@ function buildRoutes(routes = []) {
|
||||
icon: e.icon,
|
||||
order: e.order,
|
||||
keepAlive: e.keepalive,
|
||||
activeMenu: routeName,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
274
web/src/views/user-management/user-list/index.vue
Normal file
274
web/src/views/user-management/user-list/index.vue
Normal file
@ -0,0 +1,274 @@
|
||||
<script setup>
|
||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
||||
import {
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NSpace,
|
||||
NSwitch,
|
||||
NTag,
|
||||
NPopconfirm,
|
||||
} from 'naive-ui'
|
||||
|
||||
import CommonPage from '@/components/page/CommonPage.vue'
|
||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
||||
import CrudModal from '@/components/table/CrudModal.vue'
|
||||
import CrudTable from '@/components/table/CrudTable.vue'
|
||||
|
||||
import { formatDate, renderIcon } from '@/utils'
|
||||
import { useCRUD } from '@/composables'
|
||||
import api from '@/api'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
defineOptions({ name: '用户管理' })
|
||||
|
||||
const $table = ref(null)
|
||||
const queryItems = ref({})
|
||||
const vPermission = resolveDirective('permission')
|
||||
|
||||
const {
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
modalAction,
|
||||
modalLoading,
|
||||
handleSave,
|
||||
modalForm,
|
||||
modalFormRef,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleAdd,
|
||||
} = useCRUD({
|
||||
name: '用户',
|
||||
initForm: { is_active: true },
|
||||
doCreate: (data) => api.createAppUser({ phone: data.phone }),
|
||||
doUpdate: (data) => api.updateAppUser(data),
|
||||
doDelete: (data) => api.deleteAppUser({ id: data.id }),
|
||||
refresh: () => $table.value?.handleSearch(),
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
$table.value?.handleSearch()
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
key: 'id',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
key: 'phone',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
key: 'email',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
render(row) {
|
||||
return row.email || '-'
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '注册时间',
|
||||
key: 'created_at',
|
||||
align: 'center',
|
||||
width: 160,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'text', ghost: true },
|
||||
{
|
||||
default: () => (row.created_at ? formatDate(row.created_at) : '-'),
|
||||
icon: renderIcon('mdi:calendar', { size: 16 }),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '最后登录',
|
||||
key: 'last_login',
|
||||
align: 'center',
|
||||
width: 160,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'text', ghost: true },
|
||||
{
|
||||
default: () => (row.last_login ? formatDate(row.last_login) : '未登录'),
|
||||
icon: renderIcon('mdi:login', { size: 16 }),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'is_active',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NSwitch, {
|
||||
size: 'small',
|
||||
rubberBand: false,
|
||||
value: row.is_active,
|
||||
loading: !!row.publishing,
|
||||
checkedValue: true,
|
||||
uncheckedValue: false,
|
||||
onUpdateValue: () => handleUpdateStatus(row),
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
render(row) {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'info',
|
||||
style: 'margin-right: 8px;',
|
||||
onClick: () => handleViewDetail(row),
|
||||
},
|
||||
{
|
||||
default: () => '详情',
|
||||
icon: renderIcon('material-symbols:info', { size: 16 }),
|
||||
}
|
||||
),
|
||||
[[vPermission, 'get/api/v1/app_user/detail']]
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: row.is_active ? 'warning' : 'success',
|
||||
style: 'margin-right: 8px;',
|
||||
onClick: () => handleUpdateStatus(row),
|
||||
},
|
||||
{
|
||||
default: () => (row.is_active ? '冻结' : '解冻'),
|
||||
icon: renderIcon(row.is_active ? 'material-symbols:block' : 'material-symbols:check-circle', { size: 16 }),
|
||||
}
|
||||
),
|
||||
[[vPermission, 'post/api/v1/app_user/update']]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// 修改用户状态
|
||||
async function handleUpdateStatus(row) {
|
||||
if (!row.id) return
|
||||
|
||||
// 由于现有后端API限制,暂时只显示提示信息
|
||||
$message.info('用户状态修改功能需要后端支持')
|
||||
}
|
||||
|
||||
// 查看用户详情
|
||||
function handleViewDetail(row) {
|
||||
// 这里可以跳转到详情页面或打开详情弹窗
|
||||
$message.info('查看用户详情功能待实现')
|
||||
}
|
||||
|
||||
const validateForm = {
|
||||
phone: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入手机号',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
{
|
||||
trigger: ['blur'],
|
||||
validator: (rule, value, callback) => {
|
||||
const phoneRegex = /^1[3-9]\d{9}$/
|
||||
if (!phoneRegex.test(value)) {
|
||||
callback('请输入正确的手机号格式')
|
||||
return
|
||||
}
|
||||
callback()
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPage show-footer title="用户列表">
|
||||
<template #action>
|
||||
<NButton v-permission="'post/api/v1/app_user/create'" type="primary" @click="handleAdd">
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建用户
|
||||
</NButton>
|
||||
</template>
|
||||
|
||||
<!-- 表格 -->
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:columns="columns"
|
||||
:get-data="api.getAppUserList"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="手机号" :label-width="50">
|
||||
<NInput
|
||||
v-model:value="queryItems.phone"
|
||||
clearable
|
||||
type="text"
|
||||
placeholder="请输入手机号"
|
||||
@keypress.enter="$table?.handleSearch()"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="邮箱" :label-width="50">
|
||||
<NInput
|
||||
v-model:value="queryItems.email"
|
||||
clearable
|
||||
type="text"
|
||||
placeholder="请输入邮箱"
|
||||
@keypress.enter="$table?.handleSearch()"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
|
||||
<!-- 新增/编辑 弹窗 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
@save="handleSave"
|
||||
>
|
||||
<NForm
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:rules="validateForm"
|
||||
>
|
||||
<NFormItem label="手机号" path="phone">
|
||||
<NInput v-model:value="modalForm.phone" clearable placeholder="请输入手机号" />
|
||||
</NFormItem>
|
||||
<NFormItem v-if="modalAction === 'add'" label="说明">
|
||||
<span style="color: #999; font-size: 12px;">
|
||||
注册后默认密码为手机号后6位
|
||||
</span>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
Loading…
x
Reference in New Issue
Block a user