fix: 系统对接接口
This commit is contained in:
parent
82f485385b
commit
8c4549892b
|
|
@ -14,13 +14,23 @@ export interface MenuItem {
|
|||
code: string
|
||||
icon: string
|
||||
path: string
|
||||
parentId: string
|
||||
parentId: string | null
|
||||
sort: number
|
||||
type: number
|
||||
children?: MenuItem[]
|
||||
children: MenuItem[]
|
||||
}
|
||||
|
||||
// 用户信息接口
|
||||
// 登录响应中的用户信息
|
||||
export interface LoginUserInfo {
|
||||
userId: string
|
||||
username: string
|
||||
realName: string
|
||||
token: string
|
||||
roles: string[]
|
||||
permissions: string[]
|
||||
}
|
||||
|
||||
// 完整的用户信息接口
|
||||
export interface UserInfo {
|
||||
userId: string
|
||||
username: string
|
||||
|
|
@ -28,22 +38,23 @@ export interface UserInfo {
|
|||
email: string
|
||||
phone: string
|
||||
status: number
|
||||
lastLoginIp: string
|
||||
lastLoginTime: string
|
||||
lastLoginIp: string | null
|
||||
lastLoginTime: string | null
|
||||
roles: string[]
|
||||
permissions: string[]
|
||||
menus: MenuItem[]
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
token: string
|
||||
userInfo?: UserInfo
|
||||
code: number
|
||||
message: string
|
||||
data: LoginUserInfo
|
||||
}
|
||||
|
||||
export interface UserInfoResponse {
|
||||
code: number
|
||||
data: UserInfo
|
||||
message: string
|
||||
data: UserInfo
|
||||
}
|
||||
|
||||
// 用户登录
|
||||
|
|
@ -53,7 +64,7 @@ export function login(data: LoginParams) {
|
|||
|
||||
// 获取当前用户信息
|
||||
export function getUserInfo() {
|
||||
return get<UserInfo>('/api/system/auth/info')
|
||||
return get<UserInfoResponse>('/api/system/auth/info')
|
||||
}
|
||||
|
||||
// 登出接口
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
import { get, post, put, del } from './request'
|
||||
|
||||
// 字典参数接口
|
||||
export interface DictParam {
|
||||
id: string
|
||||
createBy: string
|
||||
createTime: string
|
||||
updateBy: string
|
||||
updateTime: string
|
||||
dataType: string
|
||||
flag: number
|
||||
paramName: string
|
||||
paramType: string
|
||||
paramValue: string
|
||||
remark: string
|
||||
sortOrder: number
|
||||
status: number
|
||||
}
|
||||
|
||||
// 字典参数表单数据
|
||||
export interface DictParamFormData {
|
||||
id?: string
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
dataType: string
|
||||
flag?: number
|
||||
paramName: string
|
||||
paramType: string
|
||||
paramValue: string
|
||||
remark?: string
|
||||
sortOrder: number
|
||||
status: number
|
||||
}
|
||||
|
||||
// 分页查询参数
|
||||
export interface DictPageParams {
|
||||
typeCode?: string
|
||||
pageSize: number
|
||||
pageNum: number
|
||||
}
|
||||
|
||||
// 批量获取参数
|
||||
export interface BatchParamsQuery {
|
||||
typeCodes: string // 参数类型编码列表,多个用逗号分隔
|
||||
}
|
||||
|
||||
// 获取参数值查询参数
|
||||
export interface ParamValueQuery {
|
||||
paramName: string
|
||||
typeCode: string
|
||||
}
|
||||
|
||||
// API响应接口
|
||||
export interface ApiResponse<T = unknown> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
export interface PageResponse<T> {
|
||||
records: T[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
// 新增参数
|
||||
export function addDictParam(data: DictParamFormData) {
|
||||
return post<ApiResponse>('/system/param', data)
|
||||
}
|
||||
|
||||
// 修改参数
|
||||
export function updateDictParam(data: DictParamFormData) {
|
||||
return put<ApiResponse>('/system/param', data)
|
||||
}
|
||||
|
||||
// 批量获取多个参数类型的参数列表
|
||||
export function getBatchDictParams(params: BatchParamsQuery) {
|
||||
return get<ApiResponse<DictParam[]>>('/system/param/batch', params)
|
||||
}
|
||||
|
||||
// 分页查询参数列表
|
||||
export function getDictParamPage(params: DictPageParams) {
|
||||
return get<ApiResponse<PageResponse<DictParam>>>('/system/param/list', params)
|
||||
}
|
||||
|
||||
// 刷新参数缓存
|
||||
export function refreshDictCache(typeCode?: string) {
|
||||
const data = typeCode ? { typeCode } : {}
|
||||
return post<ApiResponse>('/system/param/refresh', data)
|
||||
}
|
||||
|
||||
// 根据参数类型获取参数列表
|
||||
export function getDictParamsByType(typeCode: string) {
|
||||
return get<ApiResponse<DictParam[]>>(`/system/param/type/${typeCode}`)
|
||||
}
|
||||
|
||||
// 获取参数值
|
||||
export function getDictParamValue(params: ParamValueQuery) {
|
||||
return get<ApiResponse<string>>('/system/param/value', params)
|
||||
}
|
||||
|
||||
// 删除参数
|
||||
export function deleteDictParam(id: string) {
|
||||
return del<ApiResponse>(`/system/param/${id}`)
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import { get, post } from './request'
|
||||
|
||||
// 权限树节点接口
|
||||
export interface PermissionTreeNode {
|
||||
id: string
|
||||
createTime?: string
|
||||
createBy?: string | null
|
||||
updateTime?: string
|
||||
updateBy?: string | null
|
||||
flag?: number
|
||||
permissionName: string
|
||||
permissionCode: string
|
||||
permissionType: number
|
||||
parentId: string | null
|
||||
path: string
|
||||
component: string
|
||||
icon: string
|
||||
sort: number
|
||||
status: number | null
|
||||
children?: PermissionTreeNode[]
|
||||
}
|
||||
|
||||
// 更新角色权限参数
|
||||
export interface UpdateRolePermissionsData {
|
||||
roleId: string
|
||||
permissionIds: string[]
|
||||
}
|
||||
|
||||
// API响应接口
|
||||
export interface ApiResponse<T = unknown> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
// 根据角色获取权限树
|
||||
export function getPermissionTreeForRole(roleId?: string) {
|
||||
const params = roleId ? { roleId } : {}
|
||||
return get<ApiResponse<PermissionTreeNode[]>>('/api/permission/listForRoleTree', params)
|
||||
}
|
||||
|
||||
// 获取角色已分配的权限ID列表
|
||||
export function getRolePermissionIds(roleId: string) {
|
||||
return get<ApiResponse<PermissionTreeNode[]>>(`/api/permission/role/${roleId}/permissionIds`)
|
||||
}
|
||||
|
||||
// 更新角色权限
|
||||
export function updateRolePermissions(data: UpdateRolePermissionsData) {
|
||||
return post<ApiResponse>('/api/permission/updateRolePermissions', data)
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import { get, post, put, del } from './request'
|
||||
|
||||
// 角色信息接口
|
||||
export interface Role {
|
||||
id: string
|
||||
roleCode: string
|
||||
roleName: string
|
||||
roleDesc: string
|
||||
status: number
|
||||
sort: number
|
||||
loginType: number
|
||||
flag: number
|
||||
createBy: string
|
||||
createTime: string
|
||||
updateBy: string
|
||||
updateTime: string
|
||||
}
|
||||
|
||||
// 角色列表查询参数
|
||||
export interface RoleListParams {
|
||||
roleName?: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
// 角色分页查询参数
|
||||
export interface RolePageParams {
|
||||
pageNum: number
|
||||
pageSize: number
|
||||
roleCode?: string
|
||||
roleName?: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
// 角色新增/更新参数
|
||||
export interface RoleFormData {
|
||||
id?: string
|
||||
roleCode: string
|
||||
roleName: string
|
||||
roleDesc: string
|
||||
status: number
|
||||
sort: number
|
||||
loginType?: number
|
||||
flag?: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
// API响应接口
|
||||
export interface ApiResponse<T = unknown> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
export interface PageResponse<T> {
|
||||
records: T[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
// 获取角色列表
|
||||
export function getRoleList(params?: RoleListParams) {
|
||||
return get<ApiResponse<Role[]>>('/api/role/list', params)
|
||||
}
|
||||
|
||||
// 分页查询角色列表
|
||||
export function getRolePage(params: RolePageParams) {
|
||||
return get<ApiResponse<PageResponse<Role>>>('/api/role/page', params)
|
||||
}
|
||||
|
||||
// 获取角色详情
|
||||
export function getRoleDetail(roleId: string) {
|
||||
return get<ApiResponse<Role>>(`/api/role/detail/${roleId}`)
|
||||
}
|
||||
|
||||
// 新增角色
|
||||
export function addRole(data: RoleFormData) {
|
||||
return post<ApiResponse>('/api/role/add', data)
|
||||
}
|
||||
|
||||
// 更新角色
|
||||
export function updateRole(data: RoleFormData) {
|
||||
return put<ApiResponse>('/api/role/update', data)
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
export function deleteRole(roleId: string) {
|
||||
return del<ApiResponse>(`/api/role/delete/${roleId}`)
|
||||
}
|
||||
|
||||
// 启用角色
|
||||
export function enableRole(roleId: string) {
|
||||
return put<ApiResponse>(`/api/role/restore/${roleId}`)
|
||||
}
|
||||
|
||||
// 禁用角色
|
||||
export function disableRole(roleId: string) {
|
||||
return put<ApiResponse>(`/api/role/disable/${roleId}`)
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
import { get, post, put, del } from './request'
|
||||
|
||||
// 用户信息接口
|
||||
export interface User {
|
||||
id: string
|
||||
createBy: string
|
||||
createTime: string
|
||||
updateBy: string
|
||||
updateTime: string
|
||||
email: string
|
||||
flag: number
|
||||
lastLoginIp: string | null
|
||||
lastLoginTime: string | null
|
||||
password?: string
|
||||
phone: string
|
||||
realName: string
|
||||
status: number
|
||||
username: string
|
||||
roles?: string[] // 用户角色ID数组
|
||||
}
|
||||
|
||||
// 用户分页查询参数
|
||||
export interface UserPageParams {
|
||||
pageNum: number
|
||||
pageSize: number
|
||||
realName?: string
|
||||
status?: number
|
||||
username?: string
|
||||
}
|
||||
|
||||
// 用户新增/更新参数
|
||||
export interface UserFormData {
|
||||
id?: string
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
email: string
|
||||
flag?: number
|
||||
lastLoginIp?: string
|
||||
lastLoginTime?: string
|
||||
password?: string
|
||||
phone: string
|
||||
realName: string
|
||||
status: number
|
||||
username: string
|
||||
}
|
||||
|
||||
// 分配角色参数
|
||||
export interface AssignRolesData {
|
||||
userId: string
|
||||
roleIds: string[]
|
||||
}
|
||||
|
||||
// 修改密码参数
|
||||
export interface ChangePasswordData {
|
||||
id: string
|
||||
newPassword: string
|
||||
oldPassword: string
|
||||
}
|
||||
|
||||
// 修改用户状态参数
|
||||
export interface UpdateUserStatusData {
|
||||
id: string
|
||||
status: number
|
||||
}
|
||||
|
||||
// API响应接口
|
||||
export interface ApiResponse<T = unknown> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
export interface PageResponse<T> {
|
||||
records: T[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
export function addUser(data: UserFormData) {
|
||||
return post<ApiResponse>('/system/user/add', data)
|
||||
}
|
||||
|
||||
// 分配用户角色
|
||||
export function assignUserRoles(data: AssignRolesData) {
|
||||
return post<ApiResponse>('/system/user/assignRoles', data)
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
export function deleteUser(id: string) {
|
||||
return del<ApiResponse>(`/system/user/delete/${id}`)
|
||||
}
|
||||
|
||||
// 分页查询用户列表
|
||||
export function getUserPage(params: UserPageParams) {
|
||||
return get<ApiResponse<PageResponse<User>>>('/system/user/page', params)
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
export function changePassword(data: ChangePasswordData) {
|
||||
return put<ApiResponse>('/system/user/password', data)
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
export function resetUserPassword(id: string) {
|
||||
return put<ApiResponse>(`/system/user/reset-password/${id}`)
|
||||
}
|
||||
|
||||
// 修改用户状态
|
||||
export function updateUserStatus(data: UpdateUserStatusData) {
|
||||
return put<ApiResponse>('/system/user/status', data)
|
||||
}
|
||||
|
||||
// 修改用户
|
||||
export function updateUser(data: UserFormData) {
|
||||
return put<ApiResponse>('/system/user/update', data)
|
||||
}
|
||||
|
||||
// 获取用户详情
|
||||
export function getUserDetail(id: string) {
|
||||
return get<ApiResponse<User>>(`/system/user/${id}`)
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// 菜单路径映射配置
|
||||
export const MENU_PATH_MAP: Record<string, string> = {
|
||||
// 系统管理
|
||||
'/system': '/admin/system',
|
||||
'/system/user': '/admin/users',
|
||||
'/system/role': '/admin/roles', // 角色管理
|
||||
'/system/permission': '/admin/permissions',
|
||||
'/system/param': '/admin/dict',
|
||||
|
||||
// 人才管理
|
||||
'/talent': '/admin/talent',
|
||||
'/talent/basic': '/admin/talent-profile',
|
||||
'/talent/unit': '/admin/talent-unit',
|
||||
'/talent/resume': '/admin/talent-resume',
|
||||
'/talent/domain': '/admin/talent-domain',
|
||||
|
||||
// 科研管理
|
||||
'/research': '/admin/research',
|
||||
'/research/project': '/admin/tech-projects',
|
||||
'/research/initiation': '/admin/research-initiation',
|
||||
'/research/report': '/admin/tech-reports',
|
||||
|
||||
// 成果管理
|
||||
'/achievement': '/admin/achievement',
|
||||
'/achievement/paper': '/admin/tech-achievements',
|
||||
'/achievement/patent': '/admin/tech-patents',
|
||||
'/achievement/award': '/admin/tech-awards',
|
||||
'/achievement/honor': '/admin/tech-honors'
|
||||
}
|
||||
|
||||
// 图标映射配置
|
||||
export const MENU_ICON_MAP: Record<string, string> = {
|
||||
'setting': 'Setting',
|
||||
'user': 'User',
|
||||
'peoples': 'UserFilled',
|
||||
'tree-table': 'Lock',
|
||||
'dict': 'Setting',
|
||||
'profile': 'User',
|
||||
'company': 'OfficeBuilding',
|
||||
'education': 'Document',
|
||||
'skill': 'Star',
|
||||
'research': 'DataAnalysis',
|
||||
'project': 'Folder',
|
||||
'form': 'Document',
|
||||
'documentation': 'DataAnalysis',
|
||||
'trophy': 'Trophy',
|
||||
'document': 'Document',
|
||||
'patent': 'Files',
|
||||
'award': 'Trophy',
|
||||
'medal': 'Medal'
|
||||
}
|
||||
|
||||
// 将后端菜单路径转换为前端路由路径
|
||||
export function convertMenuPath(backendPath: string): string {
|
||||
return MENU_PATH_MAP[backendPath] || backendPath
|
||||
}
|
||||
|
||||
// 将后端图标转换为 Element Plus 图标
|
||||
export function convertMenuIcon(backendIcon: string): string {
|
||||
return MENU_ICON_MAP[backendIcon] || 'Menu'
|
||||
}
|
||||
|
|
@ -33,71 +33,37 @@
|
|||
active-text-color="#409eff"
|
||||
router
|
||||
>
|
||||
<!-- 数据概览 -->
|
||||
<el-menu-item index="/admin/dashboard">
|
||||
<el-icon><Odometer /></el-icon>
|
||||
<span>数据概览</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item-group title="内容管理">
|
||||
<el-menu-item index="/admin/news-policy">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>新闻政策</span>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
|
||||
<el-menu-item-group title="科技资源">
|
||||
<el-menu-item index="/admin/smart-qa">
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
<span>科技问答</span>
|
||||
</el-menu-item>
|
||||
<!-- 动态菜单 -->
|
||||
<template v-for="menu in userMenus" :key="menu.id">
|
||||
<el-menu-item-group :title="menu.name" v-if="menu.children && menu.children.length > 0">
|
||||
<el-menu-item
|
||||
v-for="child in menu.children"
|
||||
:key="child.id"
|
||||
:index="convertMenuPath(child.path)"
|
||||
>
|
||||
<el-icon>
|
||||
<component :is="convertMenuIcon(child.icon)" />
|
||||
</el-icon>
|
||||
<span>{{ child.name }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
|
||||
<el-menu-item index="/admin/tech-resources">
|
||||
<el-icon><Files /></el-icon>
|
||||
<span>科技资源</span>
|
||||
<el-menu-item
|
||||
v-else
|
||||
:index="convertMenuPath(menu.path)"
|
||||
>
|
||||
<el-icon>
|
||||
<component :is="convertMenuIcon(menu.icon)" />
|
||||
</el-icon>
|
||||
<span>{{ menu.name }}</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/talent-profile">
|
||||
<el-icon><User /></el-icon>
|
||||
<span>科技人才</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/tech-projects">
|
||||
<el-icon><Folder /></el-icon>
|
||||
<span>科技项目</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/tech-achievements">
|
||||
<el-icon><Trophy /></el-icon>
|
||||
<span>科技成果</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/tech-reports">
|
||||
<el-icon><DataAnalysis /></el-icon>
|
||||
<span>科技报告</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/tech-awards">
|
||||
<el-icon><Trophy /></el-icon>
|
||||
<span>科技奖励</span>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
|
||||
<el-menu-item-group title="系统管理">
|
||||
<el-menu-item index="/admin/users">
|
||||
<el-icon><UserFilled /></el-icon>
|
||||
<span>用户管理</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/permissions">
|
||||
<el-icon><Lock /></el-icon>
|
||||
<span>权限管理</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/dict">
|
||||
<el-icon><Setting /></el-icon>
|
||||
<span>字典管理</span>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
</template>
|
||||
</el-menu>
|
||||
</aside>
|
||||
|
||||
|
|
@ -128,11 +94,15 @@ import { computed, onMounted } from 'vue'
|
|||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { convertMenuPath, convertMenuIcon } from '@/config/menu'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 获取用户菜单
|
||||
const userMenus = computed(() => userStore.menus)
|
||||
|
||||
// 初始化用户信息
|
||||
onMounted(() => {
|
||||
userStore.initUserInfo()
|
||||
|
|
@ -158,7 +128,9 @@ const breadcrumbConfig: Record<string, { title: string; parent?: string }> = {
|
|||
'/admin/tech-reports': { title: '科技报告', parent: '科技资源' },
|
||||
'/admin/tech-awards': { title: '科技奖励', parent: '科技资源' },
|
||||
'/admin/users': { title: '用户管理', parent: '系统管理' },
|
||||
'/admin/roles': { title: '角色管理', parent: '系统管理' },
|
||||
'/admin/permissions': { title: '权限管理', parent: '系统管理' },
|
||||
'/admin/permission-config': { title: '权限配置', parent: '系统管理' },
|
||||
'/admin/dict': { title: '字典管理', parent: '系统管理' }
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +190,8 @@ const handleCommand = (command: string) => {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
11
src/main.ts
11
src/main.ts
|
|
@ -10,6 +10,17 @@ import ElementPlus from 'element-plus'
|
|||
import 'element-plus/dist/index.css'
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
|
||||
// 忽略 ResizeObserver 错误
|
||||
const resizeObserverErrorHandler = (e: ErrorEvent) => {
|
||||
if (e.message === 'ResizeObserver loop completed with undelivered notifications.') {
|
||||
e.stopImmediatePropagation()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
window.addEventListener('error', resizeObserverErrorHandler)
|
||||
|
||||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
|
|
|
|||
|
|
@ -159,22 +159,26 @@ const routes: Array<RouteRecordRaw> = [
|
|||
name: 'admin-users',
|
||||
component: () => import('@/views/admin/system/users.vue')
|
||||
},
|
||||
{
|
||||
path: 'roles',
|
||||
name: 'admin-roles',
|
||||
component: () => import('@/views/admin/system/roles.vue')
|
||||
},
|
||||
{
|
||||
path: 'permissions',
|
||||
name: 'admin-permissions',
|
||||
component: () => import('@/views/admin/system/permissions.vue')
|
||||
},
|
||||
{
|
||||
path: 'permission-config',
|
||||
name: 'admin-permission-config',
|
||||
component: () => import('@/views/admin/system/permission-config.vue')
|
||||
},
|
||||
{
|
||||
path: 'dict',
|
||||
name: 'admin-dict',
|
||||
component: () => import('@/views/admin/system/dict.vue')
|
||||
},
|
||||
{
|
||||
path: 'test-proxy',
|
||||
name: 'admin-test-proxy',
|
||||
component: () => import('@/views/admin/test-proxy.vue')
|
||||
},
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -110,10 +110,13 @@ export const useUserStore = defineStore('user', {
|
|||
|
||||
// 构建树结构
|
||||
menuList.forEach(menu => {
|
||||
const menuItem = menuMap.get(menu.id)!
|
||||
const menuItem = menuMap.get(menu.id)
|
||||
if (!menuItem) return
|
||||
|
||||
if (menu.parentId && menuMap.has(menu.parentId)) {
|
||||
const parent = menuMap.get(menu.parentId)!
|
||||
const parent = menuMap.get(menu.parentId)
|
||||
if (!parent) return
|
||||
|
||||
if (!parent.children) {
|
||||
parent.children = []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,28 +50,22 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||||
import { login, getUserInfo, type LoginParams } from '@/api/auth'
|
||||
|
||||
defineOptions({
|
||||
name: 'LoginPage'
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const loginFormRef = ref<FormInstance>()
|
||||
const loading = ref(false)
|
||||
const loginFormRef = ref<FormInstance>()
|
||||
|
||||
// 登录表单数据
|
||||
const loginForm = reactive<LoginParams>({
|
||||
username: '',
|
||||
password: '',
|
||||
loginType: 2 // 固定为2
|
||||
loginType: 2
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const loginRules: FormRules = {
|
||||
const loginRules = computed((): FormRules => ({
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '用户名长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
|
|
@ -80,13 +74,12 @@ const loginRules: FormRules = {
|
|||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '密码长度在 6 到 20 个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}))
|
||||
|
||||
// 处理登录
|
||||
const handleLogin = async () => {
|
||||
if (!loginFormRef.value) return
|
||||
|
||||
await loginFormRef.value.validate(async (valid) => {
|
||||
await loginFormRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
try {
|
||||
|
|
@ -96,6 +89,12 @@ const handleLogin = async () => {
|
|||
const loginRes = await login(loginForm)
|
||||
console.log('登录响应:', loginRes)
|
||||
|
||||
// 检查登录是否成功
|
||||
if (loginRes.code !== 200) {
|
||||
ElMessage.error(loginRes.message || '登录失败')
|
||||
return
|
||||
}
|
||||
|
||||
// 保存 token
|
||||
if (loginRes.data.token) {
|
||||
localStorage.setItem('token', loginRes.data.token)
|
||||
|
|
@ -121,26 +120,16 @@ const handleLogin = async () => {
|
|||
router.push('/admin/dashboard')
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error('登录失败:', error)
|
||||
ElMessage.error(error.message || '登录失败,请检查用户名和密码')
|
||||
const errorMessage = error instanceof Error ? error.message : '登录失败,请检查用户名和密码'
|
||||
ElMessage.error(errorMessage)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 测试用的默认账号(可以删除)
|
||||
const setTestAccount = () => {
|
||||
loginForm.username = 'admin'
|
||||
loginForm.password = '123456'
|
||||
}
|
||||
|
||||
// 开发环境下自动填充测试账号
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// setTestAccount() // 取消注释可自动填充测试账号
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
<div class="pagination-section">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.currentPage"
|
||||
v-model:current-page="pagination.pageNum"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="pagination.total"
|
||||
|
|
@ -122,16 +122,17 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
||||
import {
|
||||
getDictList,
|
||||
createDict,
|
||||
updateDict,
|
||||
deleteDict,
|
||||
refreshDictCache
|
||||
} from '@/api/system'
|
||||
import type { DictItem } from '@/api/system'
|
||||
import {
|
||||
addDictParam,
|
||||
updateDictParam,
|
||||
deleteDictParam,
|
||||
getDictParamPage,
|
||||
refreshDictCache,
|
||||
type DictParam,
|
||||
type DictParamFormData
|
||||
} from '@/api/dict'
|
||||
|
||||
defineOptions({
|
||||
name: 'DictAdmin'
|
||||
|
|
@ -141,11 +142,11 @@ const searchForm = reactive({
|
|||
typeCode: ''
|
||||
})
|
||||
|
||||
const tableData = ref<DictItem[]>([])
|
||||
const tableData = ref<DictParam[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
const pagination = reactive({
|
||||
currentPage: 1,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
|
|
@ -156,7 +157,7 @@ const isEdit = ref(false)
|
|||
const formRef = ref<FormInstance>()
|
||||
const submitLoading = ref(false)
|
||||
|
||||
const formData = reactive<DictItem>({
|
||||
const formData = reactive<DictParamFormData>({
|
||||
paramName: '',
|
||||
paramType: '',
|
||||
paramValue: '',
|
||||
|
|
@ -178,15 +179,36 @@ const formRules: FormRules = {
|
|||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getDictList({
|
||||
page: pagination.currentPage,
|
||||
const params = {
|
||||
pageNum: pagination.pageNum,
|
||||
pageSize: pagination.pageSize,
|
||||
typeCode: searchForm.typeCode
|
||||
})
|
||||
tableData.value = res.data.list
|
||||
pagination.total = res.data.total
|
||||
}
|
||||
|
||||
const response = await getDictParamPage(params)
|
||||
if (response.code === 200) {
|
||||
// 检查返回的数据结构
|
||||
if (Array.isArray(response.data)) {
|
||||
// 如果data直接是数组
|
||||
tableData.value = response.data
|
||||
pagination.total = response.data.length
|
||||
} else if (response.data && response.data.records) {
|
||||
// 如果data是分页对象
|
||||
tableData.value = response.data.records
|
||||
pagination.total = response.data.total || response.data.records.length
|
||||
} else {
|
||||
// 其他情况,设为空数组
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
}
|
||||
console.log('字典数据:', response.data)
|
||||
console.log('表格数据:', tableData.value)
|
||||
} else {
|
||||
ElMessage.error(response.message || '获取数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败', error)
|
||||
ElMessage.error('获取数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
|
@ -194,14 +216,14 @@ const fetchData = async () => {
|
|||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.currentPage = 1
|
||||
pagination.pageNum = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
searchForm.typeCode = ''
|
||||
pagination.currentPage = 1
|
||||
pagination.pageNum = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
|
|
@ -212,14 +234,14 @@ const handleAdd = () => {
|
|||
}
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row: DictItem) => {
|
||||
const handleEdit = (row: DictParam) => {
|
||||
isEdit.value = true
|
||||
Object.assign(formData, row)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDelete = (row: DictItem) => {
|
||||
const handleDelete = (row: DictParam) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要删除字典"${row.paramName}"吗?删除后无法恢复!`,
|
||||
'删除确认',
|
||||
|
|
@ -230,11 +252,18 @@ const handleDelete = (row: DictItem) => {
|
|||
}
|
||||
).then(async () => {
|
||||
try {
|
||||
await deleteDict(row.id!)
|
||||
ElMessage.success('删除成功')
|
||||
fetchData()
|
||||
if (row.id) {
|
||||
const response = await deleteDictParam(row.id)
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error(response.message || '删除失败')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除失败', error)
|
||||
ElMessage.error('删除失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消删除')
|
||||
|
|
@ -253,10 +282,15 @@ const handleRefreshCache = () => {
|
|||
}
|
||||
).then(async () => {
|
||||
try {
|
||||
await refreshDictCache()
|
||||
ElMessage.success('缓存刷新成功')
|
||||
const response = await refreshDictCache(searchForm.typeCode)
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('缓存刷新成功')
|
||||
} else {
|
||||
ElMessage.error(response.message || '刷新缓存失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('刷新缓存失败', error)
|
||||
ElMessage.error('刷新缓存失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -265,21 +299,24 @@ const handleRefreshCache = () => {
|
|||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate(async (valid) => {
|
||||
await formRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true
|
||||
try {
|
||||
if (isEdit.value) {
|
||||
await updateDict(formData)
|
||||
ElMessage.success('更新成功')
|
||||
const response = isEdit.value
|
||||
? await updateDictParam(formData)
|
||||
: await addDictParam(formData)
|
||||
|
||||
if (response.code === 200) {
|
||||
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
|
||||
dialogVisible.value = false
|
||||
fetchData()
|
||||
} else {
|
||||
await createDict(formData)
|
||||
ElMessage.success('创建成功')
|
||||
ElMessage.error(response.message || '操作失败')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
fetchData()
|
||||
} catch (error) {
|
||||
console.error('提交失败', error)
|
||||
ElMessage.error('操作失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
|
|
@ -305,12 +342,12 @@ const handleDialogClose = () => {
|
|||
// 分页
|
||||
const handleSizeChange = (size: number) => {
|
||||
pagination.pageSize = size
|
||||
pagination.currentPage = 1
|
||||
pagination.pageNum = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (page: number) => {
|
||||
pagination.currentPage = page
|
||||
pagination.pageNum = page
|
||||
fetchData()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,398 @@
|
|||
<template>
|
||||
<div class="permission-config">
|
||||
<div class="page-header">
|
||||
<h2>权限配置</h2>
|
||||
<p class="page-description" v-if="currentRoleName">
|
||||
正在为角色 <el-tag type="primary">{{ currentRoleName }}</el-tag> 配置权限
|
||||
</p>
|
||||
<p class="page-description" v-else>
|
||||
配置系统权限,控制功能访问范围
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 权限树区域 -->
|
||||
<div class="permission-tree-section">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>权限配置</span>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="handleExpandAll">全部展开</el-button>
|
||||
<el-button size="small" @click="handleCollapseAll">全部收起</el-button>
|
||||
<el-button size="small" @click="handleCheckAll">全选</el-button>
|
||||
<el-button size="small" @click="handleUncheckAll">全不选</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="tree-container" v-loading="treeLoading">
|
||||
<el-tree
|
||||
v-if="permissionTree.length > 0"
|
||||
ref="permissionTreeRef"
|
||||
:data="permissionTree"
|
||||
:props="treeProps"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:default-expanded-keys="defaultExpandedKeys"
|
||||
:default-checked-keys="checkedPermissionIds"
|
||||
check-strictly
|
||||
@check="handleTreeCheck"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="tree-node">
|
||||
<el-icon v-if="data.permissionType === 1" class="node-icon"><Folder /></el-icon>
|
||||
<el-icon v-else class="node-icon"><Document /></el-icon>
|
||||
<span class="node-label">{{ data.permissionName }}</span>
|
||||
<span class="node-code" v-if="data.permissionCode">({{ data.permissionCode }})</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
|
||||
<div class="tree-actions">
|
||||
<el-button type="primary" @click="handleSavePermissions" :loading="saveLoading">
|
||||
保存权限配置
|
||||
</el-button>
|
||||
<el-button @click="handleResetPermissions">重置</el-button>
|
||||
<el-button @click="handleGoBack">返回</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElTree } from 'element-plus'
|
||||
import {
|
||||
getPermissionTreeForRole,
|
||||
getRolePermissionIds,
|
||||
updateRolePermissions,
|
||||
type PermissionTreeNode
|
||||
} from '@/api/permission'
|
||||
|
||||
defineOptions({
|
||||
name: 'PermissionConfig'
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// 响应式数据
|
||||
const currentRoleId = ref<string>('')
|
||||
const currentRoleName = ref<string>('')
|
||||
const permissionTree = ref<PermissionTreeNode[]>([])
|
||||
const checkedPermissionIds = ref<string[]>([])
|
||||
const defaultExpandedKeys = ref<string[]>([])
|
||||
const treeLoading = ref(false)
|
||||
const saveLoading = ref(false)
|
||||
|
||||
// 树组件引用
|
||||
const permissionTreeRef = ref<InstanceType<typeof ElTree>>()
|
||||
|
||||
// 树配置
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'permissionName'
|
||||
}
|
||||
|
||||
// 获取权限树
|
||||
const fetchPermissionTree = async (roleId?: string) => {
|
||||
treeLoading.value = true
|
||||
try {
|
||||
const response = await getPermissionTreeForRole(roleId)
|
||||
if (response.code === 200) {
|
||||
permissionTree.value = response.data
|
||||
// 设置默认展开的节点(第一层)
|
||||
defaultExpandedKeys.value = response.data.map((item: PermissionTreeNode) => item.id)
|
||||
} else {
|
||||
ElMessage.error(response.message || '获取权限树失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取权限树失败:', error)
|
||||
ElMessage.error('获取权限树失败')
|
||||
} finally {
|
||||
treeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取角色已分配的权限
|
||||
const fetchRolePermissions = async (roleId: string) => {
|
||||
try {
|
||||
const response = await getRolePermissionIds(roleId)
|
||||
if (response.code === 200) {
|
||||
// 从返回的权限列表中筛选出status为1的权限ID
|
||||
const checkedIds = response.data
|
||||
.filter((permission: PermissionTreeNode) => permission.status === 1)
|
||||
.map((permission: PermissionTreeNode) => permission.id)
|
||||
|
||||
checkedPermissionIds.value = checkedIds
|
||||
console.log('已选中的权限ID:', checkedIds)
|
||||
|
||||
// 等待树渲染完成后设置选中状态
|
||||
await nextTick()
|
||||
setTimeout(() => {
|
||||
if (permissionTreeRef.value) {
|
||||
permissionTreeRef.value.setCheckedKeys(checkedIds)
|
||||
}
|
||||
}, 100)
|
||||
} else {
|
||||
ElMessage.error(response.message || '获取角色权限失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取角色权限失败:', error)
|
||||
ElMessage.error('获取角色权限失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化权限配置
|
||||
const initPermissionConfig = async (roleId: string) => {
|
||||
currentRoleId.value = roleId
|
||||
|
||||
// 先获取权限树(传递roleId参数)
|
||||
await fetchPermissionTree(roleId)
|
||||
// 然后获取该角色的权限用于回显
|
||||
await fetchRolePermissions(roleId)
|
||||
}
|
||||
|
||||
// 树节点选中变化
|
||||
const handleTreeCheck = (data: PermissionTreeNode, checked: unknown): void => {
|
||||
// 这里可以添加自定义的选中逻辑
|
||||
console.log('节点选中变化:', data, checked)
|
||||
}
|
||||
|
||||
// 全部展开
|
||||
const handleExpandAll = (): void => {
|
||||
if (permissionTreeRef.value) {
|
||||
// 获取所有节点的key
|
||||
const allKeys: string[] = []
|
||||
const getAllKeys = (nodes: PermissionTreeNode[]) => {
|
||||
nodes.forEach(node => {
|
||||
allKeys.push(node.id)
|
||||
if (node.children && node.children.length > 0) {
|
||||
getAllKeys(node.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
getAllKeys(permissionTree.value)
|
||||
|
||||
// 展开所有节点
|
||||
allKeys.forEach(key => {
|
||||
permissionTreeRef.value?.store.nodesMap[key]?.expand()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 全部收起
|
||||
const handleCollapseAll = (): void => {
|
||||
if (permissionTreeRef.value) {
|
||||
// 获取所有节点的key
|
||||
const allKeys: string[] = []
|
||||
const getAllKeys = (nodes: PermissionTreeNode[]) => {
|
||||
nodes.forEach(node => {
|
||||
allKeys.push(node.id)
|
||||
if (node.children && node.children.length > 0) {
|
||||
getAllKeys(node.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
getAllKeys(permissionTree.value)
|
||||
|
||||
// 收起所有节点
|
||||
allKeys.forEach(key => {
|
||||
permissionTreeRef.value?.store.nodesMap[key]?.collapse()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 全选
|
||||
const handleCheckAll = (): void => {
|
||||
if (permissionTreeRef.value) {
|
||||
// 获取所有节点的key
|
||||
const allKeys: string[] = []
|
||||
const getAllKeys = (nodes: PermissionTreeNode[]) => {
|
||||
nodes.forEach(node => {
|
||||
allKeys.push(node.id)
|
||||
if (node.children && node.children.length > 0) {
|
||||
getAllKeys(node.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
getAllKeys(permissionTree.value)
|
||||
|
||||
permissionTreeRef.value.setCheckedKeys(allKeys)
|
||||
}
|
||||
}
|
||||
|
||||
// 全不选
|
||||
const handleUncheckAll = (): void => {
|
||||
if (permissionTreeRef.value) {
|
||||
permissionTreeRef.value.setCheckedKeys([])
|
||||
}
|
||||
}
|
||||
|
||||
// 保存权限配置
|
||||
const handleSavePermissions = async () => {
|
||||
if (!currentRoleId.value) {
|
||||
ElMessage.warning('角色ID不存在')
|
||||
return
|
||||
}
|
||||
|
||||
if (!permissionTreeRef.value) {
|
||||
ElMessage.error('权限树未加载')
|
||||
return
|
||||
}
|
||||
|
||||
saveLoading.value = true
|
||||
try {
|
||||
// 获取选中的权限ID
|
||||
const checkedKeys = permissionTreeRef.value.getCheckedKeys() as string[]
|
||||
const halfCheckedKeys = permissionTreeRef.value.getHalfCheckedKeys() as string[]
|
||||
const allCheckedKeys = [...checkedKeys, ...halfCheckedKeys]
|
||||
|
||||
const response = await updateRolePermissions({
|
||||
roleId: currentRoleId.value,
|
||||
permissionIds: allCheckedKeys
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('权限配置保存成功')
|
||||
// 重新获取角色权限以确保数据同步
|
||||
await fetchRolePermissions(currentRoleId.value)
|
||||
} else {
|
||||
ElMessage.error(response.message || '保存权限配置失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存权限配置失败:', error)
|
||||
ElMessage.error('保存权限配置失败')
|
||||
} finally {
|
||||
saveLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置权限配置
|
||||
const handleResetPermissions = async () => {
|
||||
if (!currentRoleId.value) return
|
||||
|
||||
// 重新获取角色权限
|
||||
await fetchRolePermissions(currentRoleId.value)
|
||||
ElMessage.info('已重置为原始权限配置')
|
||||
}
|
||||
|
||||
// 返回列表页
|
||||
const handleGoBack = () => {
|
||||
router.push('/admin/permissions')
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// 从URL参数获取角色信息
|
||||
const roleIdFromQuery = route.query.roleId as string
|
||||
const roleNameFromQuery = route.query.roleName as string
|
||||
|
||||
if (roleIdFromQuery) {
|
||||
currentRoleName.value = roleNameFromQuery || ''
|
||||
await initPermissionConfig(roleIdFromQuery)
|
||||
} else {
|
||||
ElMessage.error('缺少角色参数')
|
||||
router.push('/admin/permissions')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.permission-config {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header h2 {
|
||||
color: #303133;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.permission-tree-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tree-container {
|
||||
min-height: 400px;
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.node-icon {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.node-code {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.tree-actions {
|
||||
margin-top: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tree-actions .el-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
/* 自定义树节点样式 */
|
||||
:deep(.el-tree-node__content) {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__label) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 选中状态样式 */
|
||||
:deep(.el-tree-node.is-checked > .el-tree-node__content) {
|
||||
background-color: #f0f9ff;
|
||||
}
|
||||
|
||||
/* 半选中状态样式 */
|
||||
:deep(.el-tree-node.is-indeterminate > .el-tree-node__content) {
|
||||
background-color: #fef7e0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,223 +2,195 @@
|
|||
<div class="permissions-admin">
|
||||
<div class="page-header">
|
||||
<h2>权限管理</h2>
|
||||
<p class="page-description">管理系统角色权限,为不同角色分配相应的功能权限</p>
|
||||
</div>
|
||||
|
||||
<div class="search-section">
|
||||
<div class="search-form">
|
||||
<el-form :inline="true" :model="searchForm" class="search-form-inline">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input
|
||||
v-model="searchForm.roleName"
|
||||
placeholder="请输入角色名称"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="全部" clearable style="width: 120px">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="启用" value="启用" />
|
||||
<el-option label="禁用" value="禁用" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" @click="handleAdd">新增角色</el-button>
|
||||
</div>
|
||||
<!-- 搜索表单 -->
|
||||
<div class="search-form">
|
||||
<el-form :model="searchForm" inline>
|
||||
<el-form-item label="角色名称">
|
||||
<el-input
|
||||
v-model="searchForm.roleName"
|
||||
placeholder="请输入角色名称"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色编码">
|
||||
<el-input
|
||||
v-model="searchForm.roleCode"
|
||||
placeholder="请输入角色编码"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select
|
||||
v-model="searchForm.status"
|
||||
placeholder="请选择状态"
|
||||
clearable
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleReset">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 角色列表 -->
|
||||
<div class="table-section">
|
||||
<el-table :data="tableData" style="width: 100%" stripe border v-loading="loading">
|
||||
<el-table-column prop="roleName" label="角色名称" width="150" align="center" />
|
||||
<el-table-column prop="roleCode" label="角色编码" width="150" align="center" />
|
||||
<el-table-column prop="description" label="角色描述" min-width="200" />
|
||||
<el-table-column prop="permissions" label="权限" min-width="300">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
v-for="permission in scope.row.permissions"
|
||||
:key="permission"
|
||||
size="small"
|
||||
style="margin-right: 8px; margin-bottom: 4px;"
|
||||
>
|
||||
{{ permission }}
|
||||
<el-table :data="roleList" v-loading="loading" stripe>
|
||||
<el-table-column prop="id" label="角色ID" width="200" />
|
||||
<el-table-column prop="roleName" label="角色名称" />
|
||||
<el-table-column prop="roleCode" label="角色编码" />
|
||||
<el-table-column prop="roleDesc" label="角色描述" />
|
||||
<el-table-column prop="sort" label="排序" width="80" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === '启用' ? 'success' : 'danger'">
|
||||
{{ scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="warning" size="small" @click="handlePermissions(scope.row)">权限</el-button>
|
||||
<el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
|
||||
<el-table-column prop="createTime" label="创建时间" width="180" />
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" size="small" @click="handleConfigPermissions(row)">
|
||||
配置权限
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="pagination-section">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.currentPage"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="pagination.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
background
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
<div class="pagination-wrapper">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.pageNum"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getRolePage, type Role } from '@/api/role'
|
||||
|
||||
defineOptions({
|
||||
name: 'PermissionsAdmin'
|
||||
})
|
||||
|
||||
interface RoleItem {
|
||||
id: string
|
||||
roleName: string
|
||||
roleCode: string
|
||||
description: string
|
||||
permissions: string[]
|
||||
status: string
|
||||
updateTime: string
|
||||
}
|
||||
const router = useRouter()
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const roleList = ref<Role[]>([])
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
roleName: '',
|
||||
status: ''
|
||||
roleCode: '',
|
||||
status: undefined as number | undefined
|
||||
})
|
||||
|
||||
const tableData = ref<RoleItem[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
// 分页数据
|
||||
const pagination = reactive({
|
||||
currentPage: 1,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
|
||||
const mockData: RoleItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
roleName: '超级管理员',
|
||||
roleCode: 'super_admin',
|
||||
description: '拥有系统所有权限',
|
||||
permissions: ['用户管理', '内容管理', '系统设置', '数据统计'],
|
||||
status: '启用',
|
||||
updateTime: '2024-10-13 10:00:00'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
roleName: '内容编辑',
|
||||
roleCode: 'editor',
|
||||
description: '负责内容的编辑和发布',
|
||||
permissions: ['新闻政策', '科技问答', '科技资源'],
|
||||
status: '启用',
|
||||
updateTime: '2024-10-12 15:30:00'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
roleName: '普通用户',
|
||||
roleCode: 'user',
|
||||
description: '只能查看内容',
|
||||
permissions: ['内容查看'],
|
||||
status: '启用',
|
||||
updateTime: '2024-10-11 09:15:00'
|
||||
}
|
||||
]
|
||||
|
||||
const fetchData = () => {
|
||||
// 获取角色列表
|
||||
const getRoleList = async () => {
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
tableData.value = mockData
|
||||
pagination.total = mockData.length
|
||||
try {
|
||||
const params = {
|
||||
pageNum: pagination.pageNum,
|
||||
pageSize: pagination.pageSize,
|
||||
...searchForm
|
||||
}
|
||||
|
||||
const response = await getRolePage(params)
|
||||
if (response.code === 200) {
|
||||
roleList.value = response.data.records
|
||||
// 如果后端返回的total为0,使用records的长度作为total
|
||||
pagination.total = response.data.total || response.data.records.length
|
||||
console.log('角色列表数据:', response.data)
|
||||
console.log('roleList:', roleList.value)
|
||||
console.log('total:', pagination.total)
|
||||
} else {
|
||||
ElMessage.error(response.message || '获取角色列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取角色列表失败:', error)
|
||||
ElMessage.error('获取角色列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
fetchData()
|
||||
pagination.pageNum = 1
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
const handleReset = () => {
|
||||
searchForm.roleName = ''
|
||||
searchForm.status = ''
|
||||
fetchData()
|
||||
Object.assign(searchForm, {
|
||||
roleName: '',
|
||||
roleCode: '',
|
||||
status: undefined
|
||||
})
|
||||
pagination.pageNum = 1
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
ElMessage.info('新增角色功能开发中')
|
||||
}
|
||||
|
||||
const handleEdit = (row: RoleItem) => {
|
||||
ElMessage.info('编辑功能开发中')
|
||||
console.log(row)
|
||||
}
|
||||
|
||||
const handlePermissions = (row: RoleItem) => {
|
||||
ElMessage.info('权限配置功能开发中')
|
||||
console.log(row)
|
||||
}
|
||||
|
||||
const handleDelete = (row: RoleItem) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要删除角色"${row.roleName}"吗?删除后无法恢复!`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
dangerouslyUseHTMLString: false
|
||||
// 配置权限
|
||||
const handleConfigPermissions = (row: Role) => {
|
||||
// 跳转到权限配置页面
|
||||
router.push({
|
||||
path: '/admin/permission-config',
|
||||
query: {
|
||||
roleId: row.id,
|
||||
roleName: row.roleName
|
||||
}
|
||||
).then(() => {
|
||||
// 模拟删除请求
|
||||
const index = tableData.value.findIndex(item => item.id === row.id)
|
||||
if (index > -1) {
|
||||
tableData.value.splice(index, 1)
|
||||
pagination.total = tableData.value.length
|
||||
|
||||
// 如果当前页没有数据了,回到上一页
|
||||
if (tableData.value.length === 0 && pagination.currentPage > 1) {
|
||||
pagination.currentPage--
|
||||
}
|
||||
|
||||
ElMessage.success('删除成功')
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消删除')
|
||||
})
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
const handleSizeChange = (size: number) => {
|
||||
pagination.pageSize = size
|
||||
fetchData()
|
||||
pagination.pageNum = 1
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (page: number) => {
|
||||
pagination.currentPage = page
|
||||
fetchData()
|
||||
pagination.pageNum = page
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
getRoleList()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -237,31 +209,30 @@ onMounted(() => {
|
|||
color: #303133;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
.search-form {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.search-form-inline {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pagination-section {
|
||||
.pagination-wrapper {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
<template>
|
||||
<div class="roles-page">
|
||||
<div class="page-header">
|
||||
<h2>角色管理</h2>
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
<el-icon><Plus /></el-icon>
|
||||
新增角色
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<div class="search-form">
|
||||
<el-form :model="searchForm" inline>
|
||||
<el-form-item label="角色名称">
|
||||
<el-input
|
||||
v-model="searchForm.roleName"
|
||||
placeholder="请输入角色名称"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色编码">
|
||||
<el-input
|
||||
v-model="searchForm.roleCode"
|
||||
placeholder="请输入角色编码"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select
|
||||
v-model="searchForm.status"
|
||||
placeholder="请选择状态"
|
||||
clearable
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleReset">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="page-content">
|
||||
<el-table :data="roleList" v-loading="loading">
|
||||
<el-table-column prop="id" label="角色ID" width="200" />
|
||||
<el-table-column prop="roleName" label="角色名称" />
|
||||
<el-table-column prop="roleCode" label="角色编码" />
|
||||
<el-table-column prop="roleDesc" label="角色描述" />
|
||||
<el-table-column prop="sort" label="排序" width="80" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="180" />
|
||||
<el-table-column label="操作" width="280" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" size="small" @click="handleEdit(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="info" size="small" @click="handlePermissions(row)">
|
||||
权限
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 0"
|
||||
type="success"
|
||||
size="small"
|
||||
@click="handleEnable(row)"
|
||||
>
|
||||
启用
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 1"
|
||||
type="warning"
|
||||
size="small"
|
||||
@click="handleDisable(row)"
|
||||
>
|
||||
禁用
|
||||
</el-button>
|
||||
<el-button type="danger" size="small" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-wrapper">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.pageNum"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 角色表单对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="isEdit ? '编辑角色' : '新增角色'"
|
||||
width="600px"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="formData.roleName" placeholder="请输入角色名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色编码" prop="roleCode">
|
||||
<el-input v-model="formData.roleCode" placeholder="请输入角色编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色描述" prop="roleDesc">
|
||||
<el-input
|
||||
v-model="formData.roleDesc"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入角色描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number
|
||||
v-model="formData.sort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
placeholder="请输入排序值"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="1">启用</el-radio>
|
||||
<el-radio :label="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import {
|
||||
getRolePage,
|
||||
addRole,
|
||||
updateRole,
|
||||
deleteRole,
|
||||
enableRole,
|
||||
disableRole,
|
||||
type Role,
|
||||
type RoleFormData
|
||||
} from '@/api/role'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const dialogVisible = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const roleList = ref<Role[]>([])
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
roleName: '',
|
||||
roleCode: '',
|
||||
status: undefined as number | undefined
|
||||
})
|
||||
|
||||
// 分页数据
|
||||
const pagination = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive<RoleFormData>({
|
||||
id: '',
|
||||
roleName: '',
|
||||
roleCode: '',
|
||||
roleDesc: '',
|
||||
sort: 0,
|
||||
status: 1
|
||||
})
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单验证规则
|
||||
const formRules: FormRules = {
|
||||
roleName: [
|
||||
{ required: true, message: '请输入角色名称', trigger: 'blur' }
|
||||
],
|
||||
roleCode: [
|
||||
{ required: true, message: '请输入角色编码', trigger: 'blur' }
|
||||
],
|
||||
sort: [
|
||||
{ required: true, message: '请输入排序值', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
// 获取角色列表
|
||||
const getRoleList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {
|
||||
pageNum: pagination.pageNum,
|
||||
pageSize: pagination.pageSize,
|
||||
...searchForm
|
||||
}
|
||||
|
||||
const response = await getRolePage(params)
|
||||
if (response.code === 200) {
|
||||
roleList.value = response.data.records
|
||||
pagination.total = response.data.total || response.data.records.length
|
||||
} else {
|
||||
ElMessage.error(response.message || '获取角色列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取角色列表失败:', error)
|
||||
ElMessage.error('获取角色列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.pageNum = 1
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
const handleReset = () => {
|
||||
Object.assign(searchForm, {
|
||||
roleName: '',
|
||||
roleCode: '',
|
||||
status: undefined
|
||||
})
|
||||
pagination.pageNum = 1
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
// 新增角色
|
||||
const handleAdd = () => {
|
||||
isEdit.value = false
|
||||
// 确保对话框关闭状态下重置表单
|
||||
dialogVisible.value = false
|
||||
resetForm()
|
||||
// 使用 nextTick 确保表单重置完成后再打开对话框
|
||||
nextTick(() => {
|
||||
dialogVisible.value = true
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑角色
|
||||
const handleEdit = (row: Role) => {
|
||||
isEdit.value = true
|
||||
Object.assign(formData, row)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
const handleDelete = async (row: Role) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除角色"${row.roleName}"吗?`,
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
|
||||
const response = await deleteRole(row.id)
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
getRoleList()
|
||||
} else {
|
||||
ElMessage.error(response.message || '删除失败')
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除角色失败:', error)
|
||||
ElMessage.error('删除失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 启用角色
|
||||
const handleEnable = async (row: Role) => {
|
||||
try {
|
||||
const response = await enableRole(row.id)
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('启用成功')
|
||||
getRoleList()
|
||||
} else {
|
||||
ElMessage.error(response.message || '启用失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('启用角色失败:', error)
|
||||
ElMessage.error('启用失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 禁用角色
|
||||
const handleDisable = async (row: Role) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要禁用角色"${row.roleName}"吗?`,
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
|
||||
const response = await disableRole(row.id)
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('禁用成功')
|
||||
getRoleList()
|
||||
} else {
|
||||
ElMessage.error(response.message || '禁用失败')
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('禁用角色失败:', error)
|
||||
ElMessage.error('禁用失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 配置权限
|
||||
const handlePermissions = (row: Role) => {
|
||||
// 跳转到权限管理页面,并传递角色ID
|
||||
router.push({
|
||||
path: '/admin/permissions',
|
||||
query: { roleId: row.id, roleName: row.roleName }
|
||||
})
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
dialogVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
submitLoading.value = true
|
||||
|
||||
const response = isEdit.value
|
||||
? await updateRole(formData)
|
||||
: await addRole(formData)
|
||||
|
||||
if (response.code === 200) {
|
||||
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
|
||||
dialogVisible.value = false
|
||||
getRoleList()
|
||||
} else {
|
||||
ElMessage.error(response.message || '操作失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交表单失败:', error)
|
||||
ElMessage.error('操作失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
// 先重置表单验证状态
|
||||
formRef.value?.resetFields()
|
||||
|
||||
// 然后清空表单数据
|
||||
Object.assign(formData, {
|
||||
id: '',
|
||||
roleName: '',
|
||||
roleCode: '',
|
||||
roleDesc: '',
|
||||
sort: 0,
|
||||
status: 1
|
||||
})
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
const handleSizeChange = (size: number) => {
|
||||
pagination.pageSize = size
|
||||
pagination.pageNum = 1
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (page: number) => {
|
||||
pagination.pageNum = page
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getRoleList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.roles-page {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header h2 {
|
||||
margin: 0;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -15,56 +15,92 @@
|
|||
style="width: 150px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="searchForm.role" placeholder="全部" clearable style="width: 120px">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="管理员" value="管理员" />
|
||||
<el-option label="编辑" value="编辑" />
|
||||
<el-option label="普通用户" value="普通用户" />
|
||||
<el-form-item label="真实姓名">
|
||||
<el-input
|
||||
v-model="searchForm.realName"
|
||||
placeholder="请输入真实姓名"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱">
|
||||
<el-input
|
||||
v-model="searchForm.email"
|
||||
placeholder="请输入邮箱"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="全部" clearable style="width: 120px">
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置搜索</el-button>
|
||||
<el-button type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleReset">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" @click="handleAdd">新增用户</el-button>
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
<el-icon><Plus /></el-icon>
|
||||
新增用户
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section">
|
||||
<el-table :data="tableData" style="width: 100%" stripe border v-loading="loading">
|
||||
<el-table :data="tableData" style="width: 100%; height: 100%;" stripe border v-loading="loading">
|
||||
<el-table-column prop="username" label="用户名" width="120" align="center" />
|
||||
<el-table-column prop="realName" label="真实姓名" width="120" align="center" />
|
||||
<el-table-column prop="email" label="邮箱" min-width="180" />
|
||||
<el-table-column prop="role" label="角色" width="100" align="center">
|
||||
<el-table-column prop="email" label="邮箱" width="200" />
|
||||
<el-table-column prop="phone" label="手机号" width="130" />
|
||||
<el-table-column prop="roles" label="角色" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getRoleType(scope.row.role)">
|
||||
{{ scope.row.role }}
|
||||
<el-tag
|
||||
v-for="role in scope.row.roles"
|
||||
:key="role"
|
||||
size="small"
|
||||
style="margin-right: 4px"
|
||||
>
|
||||
{{ role }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === '正常' ? 'success' : 'danger'">
|
||||
{{ scope.row.status }}
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
|
||||
{{ scope.row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="lastLogin" label="最后登录" width="180" align="center" />
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<el-table-column prop="lastLoginTime" label="最后登录" width="180" align="center" />
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button
|
||||
:type="scope.row.status === '正常' ? 'warning' : 'success'"
|
||||
size="small"
|
||||
@click="handleToggleStatus(scope.row)"
|
||||
>
|
||||
{{ scope.row.status === '正常' ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
|
||||
<div class="action-links">
|
||||
<el-link type="primary" @click="handleEdit(scope.row)">编辑</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="primary" @click="handleAssignRoles(scope.row)">分配角色</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link
|
||||
:type="scope.row.status === 1 ? 'warning' : 'success'"
|
||||
@click="handleToggleStatus(scope.row)"
|
||||
>
|
||||
{{ scope.row.status === 1 ? '禁用' : '启用' }}
|
||||
</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="primary" @click="handleResetPassword(scope.row)">重置密码</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="danger" @click="handleDelete(scope.row)">删除</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -72,7 +108,7 @@
|
|||
|
||||
<div class="pagination-section">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.currentPage"
|
||||
v-model:current-page="pagination.pageNum"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="pagination.total"
|
||||
|
|
@ -82,147 +118,405 @@
|
|||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 用户表单对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="isEdit ? '编辑用户' : '新增用户'"
|
||||
width="600px"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="formData.username" placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="真实姓名" prop="realName">
|
||||
<el-input v-model="formData.realName" placeholder="请输入真实姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="formData.phone" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!isEdit" label="密码" prop="password">
|
||||
<el-input v-model="formData.password" type="password" placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="1">启用</el-radio>
|
||||
<el-radio :label="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 角色分配对话框 -->
|
||||
<el-dialog
|
||||
v-model="roleDialogVisible"
|
||||
title="分配角色"
|
||||
width="500px"
|
||||
>
|
||||
<el-checkbox-group v-model="selectedRoleIds">
|
||||
<el-checkbox
|
||||
v-for="role in roleList"
|
||||
:key="role.id"
|
||||
:label="role.id"
|
||||
style="display: block; margin-bottom: 10px;"
|
||||
>
|
||||
{{ role.roleName }} ({{ role.roleCode }})
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<template #footer>
|
||||
<el-button @click="roleDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmitRoles">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
||||
import {
|
||||
getUserPage,
|
||||
addUser,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
updateUserStatus,
|
||||
resetUserPassword,
|
||||
assignUserRoles,
|
||||
type User,
|
||||
type UserFormData
|
||||
} from '@/api/user'
|
||||
import { getRoleList, type Role } from '@/api/role'
|
||||
import { getUserDetail } from '@/api'
|
||||
|
||||
defineOptions({
|
||||
name: 'UsersAdmin'
|
||||
})
|
||||
|
||||
interface UserItem {
|
||||
id: string
|
||||
username: string
|
||||
realName: string
|
||||
email: string
|
||||
role: string
|
||||
status: string
|
||||
lastLogin: string
|
||||
}
|
||||
|
||||
const searchForm = reactive({
|
||||
username: '',
|
||||
role: ''
|
||||
realName: '',
|
||||
status: undefined as number | undefined
|
||||
})
|
||||
|
||||
const tableData = ref<UserItem[]>([])
|
||||
const tableData = ref<User[]>([])
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const dialogVisible = ref(false)
|
||||
const roleDialogVisible = ref(false)
|
||||
const isEdit = ref(false)
|
||||
|
||||
const pagination = reactive({
|
||||
currentPage: 1,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
|
||||
const mockData: UserItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
username: 'admin',
|
||||
realName: '系统管理员',
|
||||
email: 'admin@example.com',
|
||||
role: '管理员',
|
||||
status: '正常',
|
||||
lastLogin: '2024-10-13 09:30:00'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
username: 'editor',
|
||||
realName: '内容编辑',
|
||||
email: 'editor@example.com',
|
||||
role: '编辑',
|
||||
status: '正常',
|
||||
lastLogin: '2024-10-12 16:45:00'
|
||||
}
|
||||
]
|
||||
// 表单相关
|
||||
const formRef = ref<FormInstance>()
|
||||
const formData = reactive<UserFormData>({
|
||||
username: '',
|
||||
realName: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
password: '',
|
||||
status: 1
|
||||
})
|
||||
|
||||
const getRoleType = (role: string) => {
|
||||
switch (role) {
|
||||
case '管理员': return 'danger'
|
||||
case '编辑': return 'warning'
|
||||
case '普通用户': return 'info'
|
||||
default: return 'info'
|
||||
// 角色相关
|
||||
const roleList = ref<Role[]>([])
|
||||
const selectedUserId = ref<string>('')
|
||||
const selectedRoleIds = ref<string[]>([])
|
||||
|
||||
// 表单验证规则
|
||||
const formRules: FormRules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '用户名长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
realName: [
|
||||
{ required: true, message: '请输入真实姓名', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '密码长度在 6 到 20 个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 获取用户列表
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {
|
||||
pageNum: pagination.pageNum,
|
||||
pageSize: pagination.pageSize,
|
||||
...searchForm
|
||||
}
|
||||
|
||||
const response = await getUserPage(params)
|
||||
if (response.code === 200) {
|
||||
tableData.value = response.data.records
|
||||
pagination.total = response.data.total || response.data.records.length
|
||||
} else {
|
||||
ElMessage.error(response.message || '获取用户列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户列表失败:', error)
|
||||
ElMessage.error('获取用户列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetchData = () => {
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
tableData.value = mockData
|
||||
pagination.total = mockData.length
|
||||
loading.value = false
|
||||
}, 500)
|
||||
// 获取角色列表
|
||||
const fetchRoleList = async () => {
|
||||
try {
|
||||
const response = await getRoleList()
|
||||
if (response.code === 200) {
|
||||
roleList.value = response.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取角色列表失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.pageNum = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
searchForm.username = ''
|
||||
searchForm.role = ''
|
||||
Object.assign(searchForm, {
|
||||
username: '',
|
||||
realName: '',
|
||||
status: undefined
|
||||
})
|
||||
pagination.pageNum = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
const handleAdd = () => {
|
||||
ElMessage.info('新增用户功能开发中')
|
||||
isEdit.value = false
|
||||
resetForm()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = (row: UserItem) => {
|
||||
ElMessage.info('编辑功能开发中')
|
||||
console.log(row)
|
||||
// 编辑用户
|
||||
const handleEdit = (row: User) => {
|
||||
isEdit.value = true
|
||||
Object.assign(formData, row)
|
||||
// 编辑时不需要密码
|
||||
delete formData.password
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleToggleStatus = (row: UserItem) => {
|
||||
const action = row.status === '正常' ? '禁用' : '启用'
|
||||
ElMessageBox.confirm(`确定要${action}该用户吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
ElMessage.success(`${action}成功`)
|
||||
fetchData()
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = (row: UserItem) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要删除用户"${row.username}"吗?删除后无法恢复!`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
dangerouslyUseHTMLString: false
|
||||
}
|
||||
).then(() => {
|
||||
// 模拟删除请求
|
||||
const index = tableData.value.findIndex(item => item.id === row.id)
|
||||
if (index > -1) {
|
||||
tableData.value.splice(index, 1)
|
||||
pagination.total = tableData.value.length
|
||||
// 分配角色
|
||||
const handleAssignRoles = async (row: User) => {
|
||||
selectedUserId.value = row.id
|
||||
selectedRoleIds.value = []
|
||||
|
||||
try {
|
||||
// 获取角色列表
|
||||
await fetchRoleList()
|
||||
|
||||
// 获取用户详情以回显已分配的角色
|
||||
const userDetailResponse = await getUserDetail(row.id)
|
||||
if (userDetailResponse.code === 200) {
|
||||
const userDetail = userDetailResponse.data
|
||||
console.log('用户详情:', userDetail)
|
||||
|
||||
// 如果当前页没有数据了,回到上一页
|
||||
if (tableData.value.length === 0 && pagination.currentPage > 1) {
|
||||
pagination.currentPage--
|
||||
// 如果用户详情中有角色信息,进行回显
|
||||
if (userDetail.roles && Array.isArray(userDetail.roles)) {
|
||||
selectedRoleIds.value = userDetail.roles
|
||||
console.log('已选中的角色ID:', selectedRoleIds.value)
|
||||
}
|
||||
|
||||
ElMessage.success('删除成功')
|
||||
} else {
|
||||
ElMessage.error(userDetailResponse.message || '获取用户详情失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消删除')
|
||||
|
||||
// 打开对话框
|
||||
roleDialogVisible.value = true
|
||||
} catch (error) {
|
||||
console.error('获取用户详情失败:', error)
|
||||
ElMessage.error('获取用户角色信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 启用/禁用用户
|
||||
const handleToggleStatus = async (row: User) => {
|
||||
const newStatus = row.status === 1 ? 0 : 1
|
||||
const action = newStatus === 1 ? '启用' : '禁用'
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要${action}该用户吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
const response = await updateUserStatus({
|
||||
id: row.id,
|
||||
status: newStatus
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
ElMessage.success(`${action}成功`)
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error(response.message || `${action}失败`)
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error(`${action}用户失败:`, error)
|
||||
ElMessage.error(`${action}失败`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
const handleResetPassword = async (row: User) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要重置用户"${row.username}"的密码吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
const response = await resetUserPassword(row.id)
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('密码重置成功')
|
||||
} else {
|
||||
ElMessage.error(response.message || '密码重置失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('重置密码失败:', error)
|
||||
ElMessage.error('重置密码失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 删除用户
|
||||
const handleDelete = async (row: User) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除用户"${row.username}"吗?删除后无法恢复!`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
|
||||
const response = await deleteUser(row.id)
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error(response.message || '删除失败')
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除用户失败:', error)
|
||||
ElMessage.error('删除失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
submitLoading.value = true
|
||||
|
||||
const response = isEdit.value
|
||||
? await updateUser(formData)
|
||||
: await addUser(formData)
|
||||
|
||||
if (response.code === 200) {
|
||||
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
|
||||
dialogVisible.value = false
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error(response.message || '操作失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交表单失败:', error)
|
||||
ElMessage.error('操作失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 提交角色分配
|
||||
const handleSubmitRoles = async () => {
|
||||
try {
|
||||
const response = await assignUserRoles({
|
||||
userId: selectedUserId.value,
|
||||
roleIds: selectedRoleIds.value
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('角色分配成功')
|
||||
roleDialogVisible.value = false
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error(response.message || '角色分配失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('角色分配失败:', error)
|
||||
ElMessage.error('角色分配失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
Object.assign(formData, {
|
||||
username: '',
|
||||
realName: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
password: '',
|
||||
status: 1
|
||||
})
|
||||
}
|
||||
|
||||
const handleSizeChange = (size: number) => {
|
||||
pagination.pageSize = size
|
||||
pagination.pageNum = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (page: number) => {
|
||||
pagination.currentPage = page
|
||||
pagination.pageNum = page
|
||||
fetchData()
|
||||
}
|
||||
|
||||
|
|
@ -236,6 +530,9 @@ onMounted(() => {
|
|||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
height: calc(100vh - 120px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
|
|
@ -266,11 +563,30 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.table-section {
|
||||
flex: 1;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.action-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.action-links .el-link {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.action-links .el-divider--vertical {
|
||||
height: 12px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
<template>
|
||||
<div class="test-proxy">
|
||||
<h2>代理测试页面</h2>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>环境信息</h3>
|
||||
<p>当前环境: {{ currentEnv }}</p>
|
||||
<p>Base URL: {{ baseURL }}</p>
|
||||
<p>完整请求地址: {{ fullURL }}</p>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>测试请求</h3>
|
||||
<el-button type="primary" @click="testRequest" :loading="loading">
|
||||
测试 API 请求
|
||||
</el-button>
|
||||
|
||||
<div v-if="result" class="result">
|
||||
<h4>请求结果:</h4>
|
||||
<pre>{{ result }}</pre>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="error">
|
||||
<h4>错误信息:</h4>
|
||||
<pre>{{ error }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>说明</h3>
|
||||
<ul>
|
||||
<li>如果看到跨域错误,说明代理没有生效</li>
|
||||
<li>请求应该发送到: http://localhost:8080/brain/...</li>
|
||||
<li>而不是: http://47.110.148.47:8090/brain/...</li>
|
||||
<li>如果代理正常,请求会成功或返回后端错误(非跨域错误)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { getEnvConfig } from '@/config/env'
|
||||
import { getDictList } from '@/api/system'
|
||||
|
||||
defineOptions({
|
||||
name: 'TestProxy'
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const result = ref('')
|
||||
const error = ref('')
|
||||
|
||||
const envConfig = getEnvConfig()
|
||||
const currentEnv = process.env.NODE_ENV
|
||||
const baseURL = envConfig.baseURL
|
||||
const fullURL = computed(() => `${baseURL}/system/param/list`)
|
||||
|
||||
const testRequest = async () => {
|
||||
loading.value = true
|
||||
result.value = ''
|
||||
error.value = ''
|
||||
|
||||
try {
|
||||
console.log('发起测试请求...')
|
||||
console.log('Base URL:', baseURL)
|
||||
console.log('完整地址:', fullURL.value)
|
||||
|
||||
const res = await getDictList({
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
result.value = JSON.stringify(res, null, 2)
|
||||
console.log('请求成功:', res)
|
||||
} catch (err: any) {
|
||||
error.value = err.message || '请求失败'
|
||||
console.error('请求失败:', err)
|
||||
|
||||
// 检查是否是跨域错误
|
||||
if (err.message === 'Network Error' || err.code === 'ERR_NETWORK') {
|
||||
error.value += '\n\n这可能是跨域错误,请检查:\n1. 开发服务器是否重启\n2. 代理配置是否正确\n3. baseURL 是否使用相对路径'
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.test-proxy {
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #0ea5e9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #fef2f2;
|
||||
border: 1px solid #ef4444;
|
||||
border-radius: 4px;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 5px 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,374 +0,0 @@
|
|||
{
|
||||
"tool_call_id": "tool_4af0d1db-1bf4-4a48-8566-32bef59a73e8",
|
||||
"status": "succeeded",
|
||||
"content": "获取订舱列表成功,分页数据{'total': 60, 'page': 1, 'page_size': 10, 'total_pages': 6, 'has_next': True, 'has_prev': False}",
|
||||
"raw": {
|
||||
"code": 200,
|
||||
"msg": "获取订舱列表成功,分页数据{'total': 60, 'page': 1, 'page_size': 10, 'total_pages': 6, 'has_next': True, 'has_prev': False}",
|
||||
"data": {
|
||||
"preview": {
|
||||
"title": "",
|
||||
"type": 1,
|
||||
"id": "",
|
||||
"content": {
|
||||
"mcpName": "订舱列表",
|
||||
"type": 1,
|
||||
"id": "",
|
||||
"fullscreen": false,
|
||||
"formData": [
|
||||
{
|
||||
"name": "订舱列表",
|
||||
"tag": "table",
|
||||
"key": "booking_list",
|
||||
"headerColumns": [
|
||||
{
|
||||
"title": "id",
|
||||
"key": "id",
|
||||
"align": "left",
|
||||
"sortable": false,
|
||||
"hidden": true,
|
||||
"width": "",
|
||||
"isExternal": false,
|
||||
"externalType": 1
|
||||
},
|
||||
{
|
||||
"title": "文件名",
|
||||
"key": "file_name",
|
||||
"align": "left",
|
||||
"hidden": false,
|
||||
"width": "",
|
||||
"isExternal": false,
|
||||
"externalType": 1
|
||||
},
|
||||
{
|
||||
"title": "订舱编号",
|
||||
"key": "booking_number",
|
||||
"align": "left",
|
||||
"hidden": false,
|
||||
"width": "",
|
||||
"isExternal": false,
|
||||
"externalType": 1
|
||||
},
|
||||
{
|
||||
"title": "状态",
|
||||
"key": "status",
|
||||
"align": "left",
|
||||
"hidden": false,
|
||||
"width": "",
|
||||
"isExternal": false,
|
||||
"externalType": 1
|
||||
},
|
||||
{
|
||||
"title": "汇率",
|
||||
"key": "usd",
|
||||
"align": "left",
|
||||
"hidden": false,
|
||||
"width": "",
|
||||
"isExternal": false,
|
||||
"externalType": 1
|
||||
},
|
||||
{
|
||||
"title": "实例id",
|
||||
"key": "instance_id",
|
||||
"align": "left",
|
||||
"hidden": true,
|
||||
"width": "",
|
||||
"isExternal": false,
|
||||
"externalType": 1
|
||||
},
|
||||
{
|
||||
"title": "创建人",
|
||||
"key": "create_by",
|
||||
"align": "left",
|
||||
"hidden": false,
|
||||
"width": "",
|
||||
"isExternal": false,
|
||||
"externalType": 1
|
||||
},
|
||||
{
|
||||
"title": "审核文件",
|
||||
"key": "audit_file",
|
||||
"align": "left",
|
||||
"hidden": false,
|
||||
"width": "",
|
||||
"isExternal": true,
|
||||
"externalType": 2
|
||||
},
|
||||
{
|
||||
"title": "合同文件",
|
||||
"key": "final_file",
|
||||
"align": "left",
|
||||
"hidden": false,
|
||||
"width": "",
|
||||
"isExternal": true,
|
||||
"externalType": 2
|
||||
},
|
||||
{
|
||||
"title": "操作",
|
||||
"key": "operations",
|
||||
"type": "operations",
|
||||
"align": "center"
|
||||
}
|
||||
],
|
||||
"contentColumns": [
|
||||
"id",
|
||||
"file_name",
|
||||
"booking_number",
|
||||
"status",
|
||||
"usd",
|
||||
"instance_id",
|
||||
"create_by",
|
||||
"audit_file",
|
||||
"final_file",
|
||||
"operations"
|
||||
],
|
||||
"dataRows": [
|
||||
{
|
||||
"id": "fd096220-da85-4c2b-99f6-b9d0962ee273",
|
||||
"file_name": "高泰出口货订单表.xlsx",
|
||||
"booking_number": "HO547781",
|
||||
"status": "已完成",
|
||||
"usd": "3.0000",
|
||||
"instance_id": 694,
|
||||
"create_by": "test123",
|
||||
"audit_file": [
|
||||
{
|
||||
"fileName": "订舱明细HO54778120250920161525.xlsx",
|
||||
"fileId": "cfb70f5f-f503-4674-a796-81d21365cbd7",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
],
|
||||
"final_file": [
|
||||
{
|
||||
"fileName": "订舱合同_HO547781_20250920161532_98601.xlsx",
|
||||
"fileId": "a6136963-b75d-4753-aed6-be67ae28890f",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_HO547781_20250920161534_94197.xlsx",
|
||||
"fileId": "19e49396-605e-469c-9aed-e494c4514849",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_HO547781_20250920161536_69715.xlsx",
|
||||
"fileId": "47ed34a3-8f51-4c63-96b4-9adf425bd432",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "8de4b287-e5f0-460d-bdb7-8bcaa4e618d8",
|
||||
"file_name": "高泰出口货订单.xlsx",
|
||||
"booking_number": null,
|
||||
"status": "待审核",
|
||||
"usd": null,
|
||||
"instance_id": 692,
|
||||
"create_by": "test123",
|
||||
"audit_file": null,
|
||||
"final_file": null
|
||||
},
|
||||
{
|
||||
"id": "a54db8cc-c92d-4018-b57e-ebc64337baa1",
|
||||
"file_name": "高泰出口货订单表.xlsx",
|
||||
"booking_number": null,
|
||||
"status": "待处理",
|
||||
"usd": null,
|
||||
"instance_id": 691,
|
||||
"create_by": "test123",
|
||||
"audit_file": null,
|
||||
"final_file": null
|
||||
},
|
||||
{
|
||||
"id": "f46ec726-97a6-4e19-b009-27c585444158",
|
||||
"file_name": "高泰出口货订单.xlsx",
|
||||
"booking_number": null,
|
||||
"status": "待审核",
|
||||
"usd": null,
|
||||
"instance_id": 689,
|
||||
"create_by": "test123",
|
||||
"audit_file": null,
|
||||
"final_file": null
|
||||
},
|
||||
{
|
||||
"id": "aa75c1d8-3d63-4090-94a4-bcf340d7879d",
|
||||
"file_name": "高泰出口货订单表.xlsx",
|
||||
"booking_number": "LA158968",
|
||||
"status": "已完成",
|
||||
"usd": "3.0000",
|
||||
"instance_id": 688,
|
||||
"create_by": "test123",
|
||||
"audit_file": [
|
||||
{
|
||||
"fileName": "订舱明细LA15896820250920155818.xlsx",
|
||||
"fileId": "fa01de0a-af07-49c1-b101-30c4e98aa0f0",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
],
|
||||
"final_file": [
|
||||
{
|
||||
"fileName": "订舱合同_LA158968_20250920155824_68312.xlsx",
|
||||
"fileId": "eb235513-f136-495e-9ee4-34e811a1d6a8",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_LA158968_20250920155826_42587.xlsx",
|
||||
"fileId": "5c6b39aa-4f06-4136-937b-7ef0f642b7a4",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_LA158968_20250920155828_15180.xlsx",
|
||||
"fileId": "4c83559c-182f-4e28-8acb-67796bac1703",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d7ab299b-d455-4b6e-8e43-507ea1a6f462",
|
||||
"file_name": "高泰出口货订单.xlsx",
|
||||
"booking_number": null,
|
||||
"status": "待审核",
|
||||
"usd": null,
|
||||
"instance_id": 686,
|
||||
"create_by": "test123",
|
||||
"audit_file": null,
|
||||
"final_file": null
|
||||
},
|
||||
{
|
||||
"id": "ad21ebf2-d87a-43bf-8639-1cdabcd77c97",
|
||||
"file_name": "高泰出口货订单表.xlsx",
|
||||
"booking_number": "UO815266",
|
||||
"status": "已完成",
|
||||
"usd": "3.0000",
|
||||
"instance_id": 685,
|
||||
"create_by": "test123",
|
||||
"audit_file": [
|
||||
{
|
||||
"fileName": "订舱明细UO81526620250920153747.xlsx",
|
||||
"fileId": "2ccff892-ec61-4a8e-9901-f01c3daf72b7",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
],
|
||||
"final_file": [
|
||||
{
|
||||
"fileName": "订舱合同_UO815266_20250920153753_92191.xlsx",
|
||||
"fileId": "d55d7929-5ea8-4129-a8c1-5d6f28ce89d9",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_UO815266_20250920153755_44609.xlsx",
|
||||
"fileId": "19ef6a3b-d9d8-4987-b6bd-0bb48b0532e2",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_UO815266_20250920153757_77201.xlsx",
|
||||
"fileId": "a1ebe5c5-bd9f-40f7-b919-8011e60915c3",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "bae2609b-7608-41e3-93a2-02a81f7e738f",
|
||||
"file_name": "高泰出口货订单.xlsx",
|
||||
"booking_number": null,
|
||||
"status": "待审核",
|
||||
"usd": null,
|
||||
"instance_id": 683,
|
||||
"create_by": "test123",
|
||||
"audit_file": null,
|
||||
"final_file": null
|
||||
},
|
||||
{
|
||||
"id": "e58886df-727b-4803-b13d-df0682d9e226",
|
||||
"file_name": "高泰出口货订单表.xlsx",
|
||||
"booking_number": "DR677738",
|
||||
"status": "已完成",
|
||||
"usd": "3.0000",
|
||||
"instance_id": 682,
|
||||
"create_by": "test123",
|
||||
"audit_file": [
|
||||
{
|
||||
"fileName": "订舱明细DR67773820250920153514.xlsx",
|
||||
"fileId": "85125ce5-708b-4eb0-81cf-94a29d014fec",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
],
|
||||
"final_file": [
|
||||
{
|
||||
"fileName": "订舱合同_DR677738_20250920153522_95548.xlsx",
|
||||
"fileId": "8275efd6-b7ea-4f23-b7eb-116c5d6a89ec",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_DR677738_20250920153524_32804.xlsx",
|
||||
"fileId": "60581a74-11b0-4ef0-b7da-6f8190479880",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_DR677738_20250920153526_19680.xlsx",
|
||||
"fileId": "97cee51d-e172-4edc-8021-76940c43c91e",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "af5f54c2-d207-4c1a-91db-05631c1a6369",
|
||||
"file_name": "高泰出口货订单表.xlsx",
|
||||
"booking_number": "MY512871",
|
||||
"status": "已完成",
|
||||
"usd": "3.0000",
|
||||
"instance_id": 679,
|
||||
"create_by": "test123",
|
||||
"audit_file": [
|
||||
{
|
||||
"fileName": "订舱明细MY51287120250920152648.xlsx",
|
||||
"fileId": "62bbd275-d902-4b51-b557-6b45ac7d49ad",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
],
|
||||
"final_file": [
|
||||
{
|
||||
"fileName": "订舱合同_MY512871_20250920152704_47232.xlsx",
|
||||
"fileId": "ed9a0baa-9962-48d9-bcec-2dfc0d500dd4",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_MY512871_20250920152707_39135.xlsx",
|
||||
"fileId": "ff01b42c-3fd8-4d02-867b-37a7d16446c3",
|
||||
"fileType": "xlsx"
|
||||
},
|
||||
{
|
||||
"fileName": "订舱合同_MY512871_20250920152708_52585.xlsx",
|
||||
"fileId": "681368c2-92c1-4de7-a596-a235214e5e42",
|
||||
"fileType": "xlsx"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"striped": true,
|
||||
"scrollX": false,
|
||||
"fullscreen": false,
|
||||
"operationsButtons": [
|
||||
{
|
||||
"label": "修改订舱",
|
||||
"key": "btn1",
|
||||
"type": "primary",
|
||||
"prompt": "prompt:modify_booking()",
|
||||
"passAllParams": false
|
||||
},
|
||||
{
|
||||
"label": "删除订舱",
|
||||
"key": "btn2",
|
||||
"type": "primary",
|
||||
"prompt": "prompt:delete_booking()",
|
||||
"passAllParams": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"success": true,
|
||||
"time": "2025-10-13 11:35:44"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
const { defineConfig } = require('@vue/cli-service')
|
||||
const { defineConfig } = require("@vue/cli-service");
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true,
|
||||
devServer: {
|
||||
port: 8080,
|
||||
proxy: {
|
||||
'/brain': {
|
||||
target: 'http://47.110.148.47:8090',
|
||||
"/brain": {
|
||||
target: "http://47.110.148.47:8090",
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
pathRewrite: {
|
||||
'^/brain': '/brain'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
"^/brain": "/brain",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue