fix: auth.ts API路径添加/api前缀
- /auth/login -> /api/auth/login - /auth/logout -> /api/auth/logout - /auth/me -> /api/auth/me - /auth/refresh -> /api/auth/refresh
This commit is contained in:
parent
ea1eabafb0
commit
f111f4a8d5
|
|
@ -2,17 +2,17 @@ import request from '@/utils/request'
|
|||
import type { LoginRequest, LoginResponse } from '@/types'
|
||||
|
||||
export const login = (data: LoginRequest) => {
|
||||
return request.post<LoginResponse>('/auth/login', data)
|
||||
return request.post<LoginResponse>('/api/auth/login', data)
|
||||
}
|
||||
|
||||
export const logout = () => {
|
||||
return request.post('/auth/logout')
|
||||
return request.post('/api/auth/logout')
|
||||
}
|
||||
|
||||
export const getCurrentUser = () => {
|
||||
return request.get('/auth/me')
|
||||
return request.get('/api/auth/me')
|
||||
}
|
||||
|
||||
export const refreshToken = () => {
|
||||
return request.post('/auth/refresh')
|
||||
return request.post('/api/auth/refresh')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN'
|
||||
|
||||
interface Props {
|
||||
current?: number
|
||||
|
|
@ -34,6 +35,7 @@ const handleChange = (page: number, size: number) => {
|
|||
|
||||
<template>
|
||||
<div class="table-pagination">
|
||||
<a-config-provider :locale="zhCN">
|
||||
<a-pagination
|
||||
v-model:current="current"
|
||||
:total="total"
|
||||
|
|
@ -44,6 +46,7 @@ const handleChange = (page: number, size: number) => {
|
|||
:show-total="(total: number) => `共 ${total} 条`"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</a-config-provider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,15 @@ interface Props {
|
|||
deleteDescription?: string
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
actions: () => [],
|
||||
showEdit: true,
|
||||
showDelete: true,
|
||||
editText: '编辑',
|
||||
deleteText: '删除',
|
||||
deleteTitle: '确认删除',
|
||||
deleteDescription: '删除后不可恢复,是否继续?'
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'edit'): void
|
||||
|
|
@ -87,6 +95,10 @@ const handleAction = (key: string) => {
|
|||
.table-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.table-actions :deep(.ant-btn) {
|
||||
padding: 0 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,32 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import { Table, Tag, message } from 'ant-design-vue'
|
||||
import { ref, onMounted, reactive, computed } from 'vue'
|
||||
import { Button, Drawer, Form, Space, message } from 'ant-design-vue'
|
||||
import { PlusOutlined, SearchOutlined } from '@ant-design/icons-vue'
|
||||
import type { Permission } from '@/types'
|
||||
import { getPermissions, createPermission, updatePermission, deletePermission } from '@/api/permission'
|
||||
import {
|
||||
TableToolbar,
|
||||
TableActions,
|
||||
Pagination
|
||||
} from '@/components'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const columns: any[] = [
|
||||
{ title: '权限编码', dataIndex: 'code', key: 'code', width: 150 },
|
||||
{ title: '权限名称', dataIndex: 'name', key: 'name', width: 150 },
|
||||
{ title: '类型', dataIndex: 'type', key: 'type', width: 100 },
|
||||
{ title: '资源', dataIndex: 'resource', key: 'resource', width: 180 },
|
||||
{ title: '方法', dataIndex: 'method', key: 'method', width: 80 },
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{ title: '权限编码', dataIndex: 'code', key: 'code', width: 140 },
|
||||
{ title: '权限名称', dataIndex: 'name', key: 'name', width: 120 },
|
||||
{ title: '类型', dataIndex: 'type', key: 'type', width: 80 },
|
||||
{ title: '资源', dataIndex: 'resource', key: 'resource', width: 160, ellipsis: true },
|
||||
{ title: '方法', dataIndex: 'method', key: 'method', width: 70 },
|
||||
{ title: '描述', dataIndex: 'description', key: 'description', ellipsis: true },
|
||||
{ title: '操作', key: 'action', width: 150, fixed: 'right' }
|
||||
{ title: '操作', key: 'action', width: 140, fixed: 'right' as const }
|
||||
]
|
||||
|
||||
const permissions = ref<Permission[]>([])
|
||||
const loading = ref(false)
|
||||
const modalVisible = ref(false)
|
||||
const modalTitle = ref('新建权限')
|
||||
const drawerVisible = ref(false)
|
||||
const drawerTitle = ref('')
|
||||
const formRef = ref()
|
||||
const submitting = ref(false)
|
||||
const editingId = ref<string | null>(null)
|
||||
|
||||
// 搜索相关
|
||||
|
|
@ -28,9 +36,14 @@ const searchKeyword = ref('')
|
|||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total: number) => `共 ${total} 条`
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 分页后的数据
|
||||
const paginatedData = computed(() => {
|
||||
const start = (pagination.current - 1) * pagination.pageSize
|
||||
const end = start + pagination.pageSize
|
||||
return permissions.value.slice(start, end)
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
|
|
@ -43,12 +56,6 @@ const formState = reactive({
|
|||
description: ''
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
code: [{ required: true, message: '请输入权限代码' }],
|
||||
name: [{ required: true, message: '请输入权限名称' }],
|
||||
type: [{ required: true, message: '请选择类型' }]
|
||||
}
|
||||
|
||||
const typeOptions = [
|
||||
{ value: 'MENU', label: '菜单' },
|
||||
{ value: 'BUTTON', label: '按钮' },
|
||||
|
|
@ -103,13 +110,13 @@ const resetForm = () => {
|
|||
editingId.value = null
|
||||
}
|
||||
|
||||
const openCreateModal = () => {
|
||||
const handleAdd = () => {
|
||||
resetForm()
|
||||
modalTitle.value = '新建权限'
|
||||
modalVisible.value = true
|
||||
drawerTitle.value = '新建权限'
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
const openEditModal = (record: Permission) => {
|
||||
const handleEdit = (record: Permission) => {
|
||||
editingId.value = record.id
|
||||
formState.code = record.code
|
||||
formState.name = record.name
|
||||
|
|
@ -117,24 +124,8 @@ const openEditModal = (record: Permission) => {
|
|||
formState.resource = record.resource || ''
|
||||
formState.method = record.method || 'GET'
|
||||
formState.description = record.description || ''
|
||||
modalTitle.value = '编辑权限'
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
if (editingId.value) {
|
||||
await updatePermission(editingId.value, formState)
|
||||
message.success('更新成功')
|
||||
} else {
|
||||
await createPermission(formState)
|
||||
message.success('创建成功')
|
||||
}
|
||||
modalVisible.value = false
|
||||
fetchPermissions()
|
||||
} catch {
|
||||
message.error(editingId.value ? '更新失败' : '创建失败')
|
||||
}
|
||||
drawerTitle.value = '编辑权限'
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
|
|
@ -147,9 +138,37 @@ const handleDelete = async (id: string) => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleTableChange = (pag: any) => {
|
||||
pagination.current = pag.current
|
||||
pagination.pageSize = pag.pageSize
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
submitting.value = true
|
||||
|
||||
if (editingId.value) {
|
||||
await updatePermission(editingId.value, formState)
|
||||
message.success('更新成功')
|
||||
} else {
|
||||
await createPermission(formState)
|
||||
message.success('创建成功')
|
||||
}
|
||||
|
||||
drawerVisible.value = false
|
||||
fetchPermissions()
|
||||
} catch (error: any) {
|
||||
if (error.errorFields) return
|
||||
message.error('操作失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
formRef.value?.resetFields()
|
||||
drawerVisible.value = false
|
||||
}
|
||||
|
||||
const handlePageChange = (page: number, pageSize: number) => {
|
||||
pagination.current = page
|
||||
pagination.pageSize = pageSize
|
||||
fetchPermissions()
|
||||
}
|
||||
|
||||
|
|
@ -158,6 +177,12 @@ const handleSearch = () => {
|
|||
fetchPermissions()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
pagination.current = 1
|
||||
fetchPermissions()
|
||||
}
|
||||
|
||||
onMounted(fetchPermissions)
|
||||
</script>
|
||||
|
||||
|
|
@ -166,123 +191,102 @@ onMounted(fetchPermissions)
|
|||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">权限管理</h2>
|
||||
<div class="page-header-actions">
|
||||
<Button type="primary" @click="handleAdd">
|
||||
<PlusOutlined /> 新建权限
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<div class="table-card">
|
||||
<div class="table-toolbar">
|
||||
<!-- 筛选区 -->
|
||||
<div class="filter-bar">
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model:value="searchKeyword"
|
||||
placeholder="搜索权限编码或名称"
|
||||
style="width: 240px"
|
||||
allow-clear
|
||||
@press-enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</a-input>
|
||||
/>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
<a-button @click="handleReset">重置</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
<!-- 表格 -->
|
||||
<div class="table-card">
|
||||
<TableToolbar @refresh="fetchPermissions" />
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="permissions"
|
||||
:data-source="paginatedData"
|
||||
:loading="loading"
|
||||
:row-key="(record: Permission) => record.id"
|
||||
:pagination="pagination"
|
||||
:scroll="{ x: 1000 }"
|
||||
@change="handleTableChange"
|
||||
:pagination="false"
|
||||
>
|
||||
<template #title>
|
||||
<div class="table-title">
|
||||
<span>权限列表</span>
|
||||
<a-button type="primary" @click="openCreateModal">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
新建
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'type'">
|
||||
<Tag :color="getTypeColor(record.type)">
|
||||
<a-tag :color="getTypeColor(record.type)">
|
||||
{{ getTypeLabel(record.type) }}
|
||||
</Tag>
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="openEditModal(record as any)">
|
||||
编辑
|
||||
</a-button>
|
||||
<a-popconfirm
|
||||
title="确定删除该权限吗?"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
@confirm="handleDelete(record.id)"
|
||||
>
|
||||
<a-button type="link" size="small" danger>
|
||||
删除
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
<TableActions @edit="handleEdit(record)" @delete="handleDelete(record.id)" />
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</a-table>
|
||||
|
||||
<Pagination
|
||||
v-model:current="pagination.current"
|
||||
v-model:pageSize="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
@change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 新建/编辑模态框 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
:title="modalTitle"
|
||||
:width="500"
|
||||
@ok="handleSubmit"
|
||||
@cancel="modalVisible = false"
|
||||
<!-- 抽屉 -->
|
||||
<Drawer
|
||||
v-model:open="drawerVisible"
|
||||
:title="drawerTitle"
|
||||
width="480px"
|
||||
:footer-style="{ textAlign: 'right' }"
|
||||
@close="handleClose"
|
||||
>
|
||||
<a-form
|
||||
<Form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
class="permission-form"
|
||||
:rules="{
|
||||
code: [{ required: true, message: '请输入权限代码' }],
|
||||
name: [{ required: true, message: '请输入权限名称' }],
|
||||
type: [{ required: true, message: '请选择类型' }]
|
||||
}"
|
||||
>
|
||||
<a-form-item label="权限代码" name="code">
|
||||
<Form.Item label="权限代码" name="code">
|
||||
<a-input v-model:value="formState.code" placeholder="如: system:user:view" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="权限名称" name="name">
|
||||
</Form.Item>
|
||||
<Form.Item label="权限名称" name="name">
|
||||
<a-input v-model:value="formState.name" placeholder="如: 查看用户" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="类型" name="type">
|
||||
</Form.Item>
|
||||
<Form.Item label="类型" name="type">
|
||||
<a-select v-model:value="formState.type" :options="typeOptions" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="资源路径" name="resource">
|
||||
</Form.Item>
|
||||
<Form.Item label="资源路径" name="resource">
|
||||
<a-input v-model:value="formState.resource" placeholder="如: /api/users" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="请求方法" name="method">
|
||||
</Form.Item>
|
||||
<Form.Item label="请求方法" name="method">
|
||||
<a-select v-model:value="formState.method" :options="methodOptions" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="描述" name="description">
|
||||
</Form.Item>
|
||||
<Form.Item label="描述" name="description">
|
||||
<a-textarea v-model:value="formState.description" :rows="3" placeholder="权限描述" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<template #footer>
|
||||
<Space>
|
||||
<Button @click="handleClose">取消</Button>
|
||||
<Button type="primary" :loading="submitting" @click="handleSubmit">确定</Button>
|
||||
</Space>
|
||||
</template>
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.table-toolbar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.permission-form {
|
||||
padding-top: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,24 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Table, Button, Drawer, Input, Select, Form, Space, Popconfirm, message } from 'ant-design-vue'
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue'
|
||||
import { ref, onMounted, reactive, computed } from 'vue'
|
||||
import { Button, Drawer, Input, Select, Form, Space, message } from 'ant-design-vue'
|
||||
import { PlusOutlined } from '@ant-design/icons-vue'
|
||||
import { getRoles, createRole, updateRole, deleteRole } from '@/api/role'
|
||||
import type { Role } from '@/types'
|
||||
import {
|
||||
TableToolbar,
|
||||
TableActions,
|
||||
Pagination,
|
||||
StatusTag
|
||||
} from '@/components'
|
||||
|
||||
const columns = [
|
||||
{ title: '角色编码', dataIndex: 'code', key: 'code', width: 120 },
|
||||
{ title: '角色名称', dataIndex: 'name', key: 'name', width: 150 },
|
||||
{ title: '类型', dataIndex: 'type', key: 'type', width: 100 },
|
||||
{ title: '角色编码', dataIndex: 'code', key: 'code', width: 100 },
|
||||
{ title: '角色名称', dataIndex: 'name', key: 'name', width: 100 },
|
||||
{ title: '类型', dataIndex: 'type', key: 'type', width: 90 },
|
||||
{ title: '数据权限', dataIndex: 'dataScope', key: 'dataScope', width: 100 },
|
||||
{ title: '描述', dataIndex: 'description', key: 'description', ellipsis: true },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 80 },
|
||||
{ title: '操作', key: 'action', width: 120, fixed: 'right' }
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 70 },
|
||||
{ title: '操作', key: 'action', width: 140, fixed: 'right' as const }
|
||||
]
|
||||
|
||||
const roles = ref<Role[]>([])
|
||||
|
|
@ -22,6 +28,24 @@ const drawerTitle = ref('')
|
|||
const formRef = ref()
|
||||
const submitting = ref(false)
|
||||
|
||||
// 筛选
|
||||
const searchKeyword = ref('')
|
||||
const searchStatus = ref('')
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 分页后的数据
|
||||
const paginatedData = computed(() => {
|
||||
const start = (pagination.current - 1) * pagination.pageSize
|
||||
const end = start + pagination.pageSize
|
||||
return roles.value.slice(start, end)
|
||||
})
|
||||
|
||||
const formState = ref({
|
||||
id: '',
|
||||
code: '',
|
||||
|
|
@ -32,11 +56,6 @@ const formState = ref({
|
|||
status: 'ACTIVE'
|
||||
})
|
||||
|
||||
const statusOptions = [
|
||||
{ value: 'ACTIVE', label: '正常', color: 'success' },
|
||||
{ value: 'DISABLED', label: '禁用', color: 'error' }
|
||||
]
|
||||
|
||||
const typeOptions = [
|
||||
{ value: 'SYSTEM', label: '系统角色' },
|
||||
{ value: 'PROJECT', label: '项目角色' },
|
||||
|
|
@ -44,9 +63,9 @@ const typeOptions = [
|
|||
]
|
||||
|
||||
const dataScopeOptions = [
|
||||
{ value: 'ALL', label: '全部数据', color: 'red' },
|
||||
{ value: 'PROJECT', label: '本项目数据', color: 'blue' },
|
||||
{ value: 'SELF', label: '本人数据', color: 'green' }
|
||||
{ value: 'ALL', label: '全部数据' },
|
||||
{ value: 'PROJECT', label: '本项目数据' },
|
||||
{ value: 'SELF', label: '本人数据' }
|
||||
]
|
||||
|
||||
const fetchRoles = async () => {
|
||||
|
|
@ -54,6 +73,7 @@ const fetchRoles = async () => {
|
|||
try {
|
||||
const res = await getRoles()
|
||||
roles.value = res.data.data || []
|
||||
pagination.total = roles.value.length
|
||||
} catch {
|
||||
message.error('获取角色列表失败')
|
||||
} finally {
|
||||
|
|
@ -61,6 +81,24 @@ const fetchRoles = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
fetchRoles()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
searchStatus.value = ''
|
||||
pagination.current = 1
|
||||
fetchRoles()
|
||||
}
|
||||
|
||||
const handlePageChange = (page: number, pageSize: number) => {
|
||||
pagination.current = page
|
||||
pagination.pageSize = pageSize
|
||||
fetchRoles()
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
drawerTitle.value = '新增角色'
|
||||
formState.value = {
|
||||
|
|
@ -126,49 +164,24 @@ const handleClose = () => {
|
|||
drawerVisible.value = false
|
||||
}
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
const map: Record<string, string> = {
|
||||
ACTIVE: 'success',
|
||||
DISABLED: 'error'
|
||||
}
|
||||
return map[status] || 'default'
|
||||
}
|
||||
|
||||
const getStatusLabel = (status: string) => {
|
||||
const map: Record<string, string> = {
|
||||
ACTIVE: '正常',
|
||||
DISABLED: '禁用'
|
||||
}
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
const getTypeLabel = (type: string) => {
|
||||
const map: Record<string, string> = {
|
||||
SYSTEM: 'SYSTEM',
|
||||
PROJECT: 'PROJECT',
|
||||
DEPARTMENT: 'DEPARTMENT'
|
||||
SYSTEM: '系统角色',
|
||||
PROJECT: '项目角色',
|
||||
DEPARTMENT: '部门级'
|
||||
}
|
||||
return map[type] || type
|
||||
}
|
||||
|
||||
const getDataScopeLabel = (dataScope: string) => {
|
||||
const map: Record<string, string> = {
|
||||
ALL: 'ALL',
|
||||
PROJECT: 'PROJECT',
|
||||
SELF: 'SELF'
|
||||
ALL: '全部数据',
|
||||
PROJECT: '本项目数据',
|
||||
SELF: '本人数据'
|
||||
}
|
||||
return map[dataScope] || dataScope
|
||||
}
|
||||
|
||||
const getDataScopeColor = (dataScope: string) => {
|
||||
const map: Record<string, string> = {
|
||||
ALL: 'red',
|
||||
PROJECT: 'blue',
|
||||
SELF: 'green'
|
||||
}
|
||||
return map[dataScope] || 'default'
|
||||
}
|
||||
|
||||
onMounted(fetchRoles)
|
||||
</script>
|
||||
|
||||
|
|
@ -184,49 +197,54 @@ onMounted(fetchRoles)
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选区 -->
|
||||
<div class="filter-bar">
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model:value="searchKeyword"
|
||||
placeholder="搜索角色编码或名称"
|
||||
style="width: 240px"
|
||||
allow-clear
|
||||
@press-enter="handleSearch"
|
||||
/>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
<a-button @click="handleReset">重置</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<div class="table-card">
|
||||
<Table
|
||||
<TableToolbar @refresh="fetchRoles" />
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="roles"
|
||||
:data-source="paginatedData"
|
||||
:loading="loading"
|
||||
:row-key="(record: Role) => record.id"
|
||||
:pagination="{ pageSize: 10, showSizeChanger: true, showTotal: (total: number) => `共 ${total} 条` }"
|
||||
:pagination="false"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'type'">
|
||||
<a-tag>{{ getTypeLabel(record.type) }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dataScope'">
|
||||
<a-tag :color="getDataScopeColor(record.dataScope)">
|
||||
{{ getDataScopeLabel(record.dataScope) }}
|
||||
</a-tag>
|
||||
<a-tag>{{ getDataScopeLabel(record.dataScope) }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusColor(record.status)">
|
||||
{{ getStatusLabel(record.status) }}
|
||||
</a-tag>
|
||||
<StatusTag :status="record.status" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<Space>
|
||||
<Button type="link" size="small" @click="handleEdit(record)">
|
||||
<EditOutlined /> 编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确认删除"
|
||||
description="删除后不可恢复,是否继续?"
|
||||
ok-text="确认"
|
||||
cancel-text="取消"
|
||||
@confirm="handleDelete(record.id)"
|
||||
>
|
||||
<Button type="link" danger size="small">
|
||||
<DeleteOutlined /> 删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
<TableActions @edit="handleEdit(record)" @delete="handleDelete(record.id)" />
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</a-table>
|
||||
|
||||
<Pagination
|
||||
v-model:current="pagination.current"
|
||||
v-model:pageSize="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
@change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 抽屉 -->
|
||||
|
|
@ -262,7 +280,7 @@ onMounted(fetchRoles)
|
|||
<Select v-model:value="formState.dataScope" :options="dataScopeOptions" placeholder="请选择数据权限" />
|
||||
</Form.Item>
|
||||
<Form.Item label="状态" name="status">
|
||||
<Select v-model:value="formState.status" :options="statusOptions" />
|
||||
<Select v-model:value="formState.status" :options="[{ value: 'ACTIVE', label: '正常' }, { value: 'DISABLED', label: '禁用' }]" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<template #footer>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import { ref, onMounted, reactive, computed } from 'vue'
|
||||
import { Button, Drawer, Form, Space, message } from 'ant-design-vue'
|
||||
import { PlusOutlined } from '@ant-design/icons-vue'
|
||||
import { getUsers, createUser, updateUser, deleteUser } from '@/api/user'
|
||||
|
|
@ -19,12 +19,12 @@ import {
|
|||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{ title: '用户名', dataIndex: 'username', key: 'username', width: 120 },
|
||||
{ title: '姓名', dataIndex: 'realName', key: 'realName', width: 100 },
|
||||
{ title: '手机', dataIndex: 'phone', key: 'phone', width: 120 },
|
||||
{ title: '邮箱', dataIndex: 'email', key: 'email', ellipsis: true },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 80 },
|
||||
{ title: '操作', key: 'action', width: 120, fixed: 'right' as const }
|
||||
{ title: '用户名', dataIndex: 'username', key: 'username', width: 100 },
|
||||
{ title: '姓名', dataIndex: 'realName', key: 'realName', width: 80 },
|
||||
{ title: '手机', dataIndex: 'phone', key: 'phone', width: 110 },
|
||||
{ title: '邮箱', dataIndex: 'email', key: 'email', width: 180, ellipsis: true },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 70 },
|
||||
{ title: '操作', key: 'action', width: 140, fixed: 'right' as const }
|
||||
]
|
||||
|
||||
// 数据
|
||||
|
|
@ -46,6 +46,13 @@ const pagination = reactive({
|
|||
total: 0
|
||||
})
|
||||
|
||||
// 分页后的数据
|
||||
const paginatedData = computed(() => {
|
||||
const start = (pagination.current - 1) * pagination.pageSize
|
||||
const end = start + pagination.pageSize
|
||||
return users.value.slice(start, end)
|
||||
})
|
||||
|
||||
// 表单
|
||||
const formState = ref({
|
||||
id: '',
|
||||
|
|
@ -172,16 +179,18 @@ onMounted(fetchUsers)
|
|||
<template>
|
||||
<div class="page-container">
|
||||
<!-- 页面标题 -->
|
||||
<PageHeader title="用户管理">
|
||||
<template #extra>
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">用户管理</h2>
|
||||
<div class="page-header-actions">
|
||||
<Button type="primary" @click="handleAdd">
|
||||
<PlusOutlined /> 新增用户
|
||||
</Button>
|
||||
</template>
|
||||
</PageHeader>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选区 -->
|
||||
<FilterBar @search="handleSearch" @reset="handleReset">
|
||||
<div class="filter-bar">
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model:value="searchKeyword"
|
||||
placeholder="搜索用户名/姓名/手机"
|
||||
|
|
@ -190,15 +199,18 @@ onMounted(fetchUsers)
|
|||
@pressEnter="handleSearch"
|
||||
/>
|
||||
<StatusSelect v-model="searchStatus" placeholder="全部状态" />
|
||||
</FilterBar>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
<a-button @click="handleReset">重置</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<TableCard>
|
||||
<div class="table-card">
|
||||
<TableToolbar @refresh="fetchUsers" />
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="users"
|
||||
:data-source="paginatedData"
|
||||
:loading="loading"
|
||||
:row-key="(record: User) => record.id"
|
||||
:pagination="false"
|
||||
|
|
@ -219,7 +231,7 @@ onMounted(fetchUsers)
|
|||
:total="pagination.total"
|
||||
@change="handlePageChange"
|
||||
/>
|
||||
</TableCard>
|
||||
</div>
|
||||
|
||||
<!-- 抽屉 -->
|
||||
<Drawer
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
const puppeteer = require('puppeteer');
|
||||
|
||||
(async () => {
|
||||
console.log('启动浏览器...');
|
||||
const browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
|
||||
page.on('console', msg => console.log('Browser console:', msg.text()));
|
||||
page.on('pageerror', error => console.log('Browser error:', error.message));
|
||||
|
||||
console.log('导航到登录页...');
|
||||
await page.goto('http://127.0.0.1:5175/login', { waitUntil: 'networkidle0' });
|
||||
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
|
||||
const title = await page.title();
|
||||
console.log('页面标题:', title);
|
||||
|
||||
await page.screenshot({ path: '/tmp/login-page.png' });
|
||||
console.log('已截图: /tmp/login-page.png');
|
||||
|
||||
const usernameInput = await page.$('input');
|
||||
console.log('找到输入框:', usernameInput ? '是' : '否');
|
||||
|
||||
if (usernameInput) {
|
||||
const inputs = await page.$$('input');
|
||||
console.log('输入框数量:', inputs.length);
|
||||
|
||||
if (inputs.length >= 2) {
|
||||
await inputs[0].type('admin');
|
||||
await inputs[1].type('Admin@123');
|
||||
|
||||
await page.screenshot({ path: '/tmp/before-login.png' });
|
||||
|
||||
const buttons = await page.$$('button');
|
||||
console.log('按钮数量:', buttons.length);
|
||||
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const text = await buttons[i].textContent();
|
||||
console.log(`按钮 ${i}:`, text.trim());
|
||||
}
|
||||
|
||||
if (buttons.length > 0) {
|
||||
await buttons[0].click();
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
|
||||
const url = page.url();
|
||||
console.log('登录后URL:', url);
|
||||
|
||||
await page.screenshot({ path: '/tmp/after-login.png' });
|
||||
console.log('已截图: /tmp/after-login.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
console.log('测试完成');
|
||||
})();
|
||||
Loading…
Reference in New Issue