feat: 统一函数命名
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
"format": "prettier --write src/",
|
"format": "prettier --write src/",
|
||||||
"type-check": "vue-tsc --build",
|
"type-check": "tsc --noEmit -p tsconfig.app.json",
|
||||||
"deploy": "./scripts/deploy.sh"
|
"deploy": "./scripts/deploy.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -24,7 +24,6 @@
|
|||||||
"@radix-ui/react-label": "^2.1.11",
|
"@radix-ui/react-label": "^2.1.11",
|
||||||
"@radix-ui/react-separator": "^1.1.11",
|
"@radix-ui/react-separator": "^1.1.11",
|
||||||
"@radix-ui/react-slot": "^1.3.0",
|
"@radix-ui/react-slot": "^1.3.0",
|
||||||
"@radix-ui/react-tooltip": "^1.2.11",
|
|
||||||
"@tailwindcss/vite": "^4.3.2",
|
"@tailwindcss/vite": "^4.3.2",
|
||||||
"@tanstack/react-query": "^5.101.2",
|
"@tanstack/react-query": "^5.101.2",
|
||||||
"@tanstack/react-router": "^1.170.17",
|
"@tanstack/react-router": "^1.170.17",
|
||||||
@@ -64,7 +63,6 @@
|
|||||||
"eslint": "^9.39.4",
|
"eslint": "^9.39.4",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-prettier": "^5.5.6",
|
"eslint-plugin-prettier": "^5.5.6",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.26",
|
"eslint-plugin-react-refresh": "^0.4.26",
|
||||||
"globals": "^16.5.0",
|
"globals": "^16.5.0",
|
||||||
|
|||||||
1108
pnpm-lock.yaml
generated
1108
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,31 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
PROJECT_ROOT=$(cd -- "$(dirname "${BASH_SOURCE[0]}")/.." &> /dev/null && pwd)
|
|
||||||
REMOTE_SERVER="01"
|
|
||||||
REMOTE_DIR="/srv/docker/nginx/www/admin"
|
|
||||||
|
|
||||||
cd "${PROJECT_ROOT}"
|
|
||||||
|
|
||||||
pnpm run build
|
|
||||||
|
|
||||||
echo $PWD
|
|
||||||
|
|
||||||
tar -czf dist.tar.gz ./dist >/dev/null 2>&1
|
|
||||||
|
|
||||||
scp dist.tar.gz "${REMOTE_SERVER}:${REMOTE_DIR}"
|
|
||||||
|
|
||||||
ssh "$REMOTE_SERVER" bash -s -- "${REMOTE_DIR}" << 'EOF'
|
|
||||||
REMOTE_DIR="$1"
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
cd "${REMOTE_DIR}"
|
|
||||||
rm -rf dist
|
|
||||||
tar -xzf dist.tar.gz
|
|
||||||
rm dist.tar.gz
|
|
||||||
EOF
|
|
||||||
|
|
||||||
rm -rf dist
|
|
||||||
rm -f dist.tar.gz
|
|
||||||
echo "部署完成"
|
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
import { http } from '@/lib'
|
import { http } from '@/lib'
|
||||||
import type { Category, CategoryForm, PageParams } from '@/schemas'
|
import type { Category, CategoryForm, PageParams } from '@/schemas'
|
||||||
|
|
||||||
export const getCategories = (params: PageParams) => {
|
export const listCategories = (params: PageParams) => {
|
||||||
return http.get<Category[], ApiPageResponse<Category>>('/category', { params })
|
return http.get<Category[], ApiPageResponse<Category>>('/categories', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createCategory = (data: CategoryForm) => {
|
export const createCategory = (data: CategoryForm) => {
|
||||||
return http.post('/category', data)
|
return http.post('/categories', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateCategory = (id: number, data: CategoryForm) => {
|
export const updateCategory = (id: number, data: CategoryForm) => {
|
||||||
return http.patch(`/category/${id}`, data)
|
return http.patch(`/categories/${id}`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteCategory = (id: number) => {
|
export const deleteCategory = (id: number) => {
|
||||||
return http.delete(`/category/${id}`)
|
return http.delete(`/categories/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const listAllCategories = () => {
|
export const listAllCategories = () => {
|
||||||
return http.get<Category[]>('/category/all')
|
return http.get<Category[]>('/categories/all')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import { http } from '@/lib'
|
import { http } from '@/lib'
|
||||||
import type { PageParams, Post, PostFormOutput } from '@/schemas'
|
import type { PageParams, Post, PostFormOutput } from '@/schemas'
|
||||||
|
|
||||||
export const getPosts = (params: PageParams) => {
|
export const listPosts = (params: PageParams) => {
|
||||||
return http.get<Post[], ApiPageResponse<Post>>('/post', { params })
|
return http.get<Post[], ApiPageResponse<Post>>('/posts', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPost = (data: PostFormOutput) => {
|
export const createPost = (data: PostFormOutput) => {
|
||||||
return http.post<{ post_id: number }>('/post', data)
|
return http.post<{ post_id: number }>('/posts', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updatePost = (id: number, data: PostFormOutput) => {
|
export const updatePost = (id: number, data: PostFormOutput) => {
|
||||||
return http.patch(`/post/${id}`, data)
|
return http.patch(`/posts/${id}`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPost = (id: number) => {
|
export const getPost = (id: number) => {
|
||||||
return http.get<Post>(`/post/${id}`)
|
return http.get<Post>(`/posts/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deletePost = (id: number) => {
|
export const deletePost = (id: number) => {
|
||||||
return http.delete(`/post/${id}`)
|
return http.delete(`/posts/${id}`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import { http } from '@/lib'
|
import { http } from '@/lib'
|
||||||
import type { PageParams, Tag, TagForm } from '@/schemas'
|
import type { PageParams, Tag, TagForm } from '@/schemas'
|
||||||
|
|
||||||
export const getTags = (params: PageParams) => {
|
export const listTags = (params: PageParams) => {
|
||||||
return http.get<Tag[], ApiPageResponse<Tag>>('/tag', { params })
|
return http.get<Tag[], ApiPageResponse<Tag>>('/tags', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createTag = (data: TagForm) => {
|
export const createTag = (data: TagForm) => {
|
||||||
return http.post('/tag', data)
|
return http.post('/tags', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateTag = (id: number, data: TagForm) => {
|
export const updateTag = (id: number, data: TagForm) => {
|
||||||
return http.patch(`/tag/${id}`, data)
|
return http.patch(`/tags/${id}`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteTag = (id: number) => {
|
export const deleteTag = (id: number) => {
|
||||||
return http.delete(`/tag/${id}`)
|
return http.delete(`/tags/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const listAllTags = () => {
|
export const listAllTags = () => {
|
||||||
return http.get<Tag[]>('/tag/all')
|
return http.get<Tag[]>('/tags/all')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
import { http } from '@/lib'
|
import { http } from '@/lib'
|
||||||
import type { PageParams, SearchSysApiParams, SysApi, SysApiForm } from '@/schemas'
|
import type { Api, ApiForm, PageParams, SearchApiParams } from '@/schemas'
|
||||||
|
|
||||||
export const getSysApis = (params: PageParams & SearchSysApiParams) => {
|
export const listApis = (params: PageParams & SearchApiParams) => {
|
||||||
return http.get<SysApi[], ApiPageResponse<SysApi>>('/api', { params })
|
return http.get<Api[], ApiPageResponse<Api>>('/apis', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSysApiGroups = () => {
|
export const listApiGroups = () => {
|
||||||
return http.get<string[]>('/api/group-names')
|
return http.get<string[]>('/apis/groups')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAllSysApi = () => {
|
export const listAllApi = () => {
|
||||||
return http.get<SysApi[]>('/api/all')
|
return http.get<Api[]>('/apis/all')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getRoleApis = (roleId: number) => {
|
export const getRoleApis = (roleId: number) => {
|
||||||
return http.get<SysApi[]>(`/role/${roleId}/apis`)
|
return http.get<Api[]>(`/roles/${roleId}/apis`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateSysApi = (id: number, data: SysApiForm) => {
|
export const updateApi = (id: number, data: ApiForm) => {
|
||||||
return http.patch('/api/' + id, data)
|
return http.patch('/apis/' + id, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createSysApi = (data: SysApiForm) => {
|
export const createApi = (data: ApiForm) => {
|
||||||
return http.post('/api', data)
|
return http.post('/apis', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteSysApi = (id: number) => {
|
export const deleteApi = (id: number) => {
|
||||||
return http.delete(`/api/${id}`)
|
return http.delete(`/apis/${id}`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { http } from '@/lib'
|
import { http } from '@/lib'
|
||||||
import type { PageParams, SysFile, SysFileForm } from '@/schemas'
|
import type { File, FileForm, PageParams } from '@/schemas'
|
||||||
|
|
||||||
export const getSysFiles = (params: PageParams) => {
|
export const listFiles = (params: PageParams) => {
|
||||||
return http.get<SysFile[], ApiPageResponse<SysFile>>('/file', { params })
|
return http.get<File[], ApiPageResponse<File>>('/files', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const uploadFile = (file: File, onProgress?: (percent: number) => void) => {
|
export const uploadFile = (file: globalThis.File, onProgress?: (percent: number) => void) => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
return http.post<SysFileForm>('/file', formData, {
|
return http.post<FileForm>('/files', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
onUploadProgress: (progressEvent) => {
|
onUploadProgress: (progressEvent) => {
|
||||||
if (onProgress && progressEvent.total) {
|
if (onProgress && progressEvent.total) {
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import { http } from '@/lib'
|
import { http } from '@/lib'
|
||||||
import type { SysMenu, SysMenuForm } from '@/schemas'
|
import type { Menu, MenuForm } from '@/schemas'
|
||||||
|
|
||||||
export const getAllSysMenus = () => {
|
export const getAllMenus = () => {
|
||||||
return http.get<SysMenu[]>('/menu/all')
|
return http.get<Menu[]>('/menus/all')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSysRoleMenus = (roleId: number) => {
|
export const getRoleMenus = (roleId: number) => {
|
||||||
return http.get<SysMenu[]>(`/role/${roleId}/menus`)
|
return http.get<Menu[]>(`/roles/${roleId}/menus`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const assignSysRoleMenus = (id: number, menu_ids: number[]) => {
|
export const assignRoleMenus = (id: number, menu_ids: number[]) => {
|
||||||
return http.put(`role/${id}/menus`, { menu_ids })
|
return http.put(`roles/${id}/menus`, { menu_ids })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createSysMenu = (data: SysMenuForm) => {
|
export const createMenu = (data: MenuForm) => {
|
||||||
return http.post('/menu', data)
|
return http.post('/menus', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateSysMenu = (id: number, data: SysMenuForm) => {
|
export const updateMenu = (id: number, data: MenuForm) => {
|
||||||
return http.patch('/menu/' + id, data)
|
return http.patch('/menus/' + id, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteSysMenu = (id: number) => {
|
export const deleteMenu = (id: number) => {
|
||||||
return http.delete(`/menu/${id}`)
|
return http.delete(`/menus/${id}`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { http } from '@/lib'
|
import { http } from '@/lib'
|
||||||
import type { SysRole, SysRoleForm } from '@/schemas'
|
import type { Role, RoleForm } from '@/schemas'
|
||||||
import { type PageParams } from '@/schemas'
|
import { type PageParams } from '@/schemas'
|
||||||
|
|
||||||
export const getSysRoles = (params: PageParams) => {
|
export const listRoles = (params: PageParams) => {
|
||||||
return http.get<SysRole[], ApiPageResponse<SysRole>>('/role', { params })
|
return http.get<Role[], ApiPageResponse<Role>>('/roles', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAllSysRoles = () => {
|
export const listAllRoles = () => {
|
||||||
return http.get<SysRole[]>('/role/all')
|
return http.get<Role[]>('/roles/all')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateSysRole = (id: number, data: SysRoleForm) => {
|
export const updateRole = (id: number, data: RoleForm) => {
|
||||||
return http.patch('/role/' + id, data)
|
return http.patch('/roles/' + id, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createSysRole = (data: SysRoleForm) => {
|
export const createRole = (data: RoleForm) => {
|
||||||
return http.post('/role', data)
|
return http.post('/roles', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteSysRole = (id: number) => {
|
export const deleteRole = (id: number) => {
|
||||||
return http.delete(`/role/${id}`)
|
return http.delete(`/roles/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const assignSysRoleApis = (id: number, api_ids: number[]) => {
|
export const assignRoleApis = (id: number, api_ids: number[]) => {
|
||||||
return http.put(`role/${id}/apis`, { api_ids })
|
return http.put(`roles/${id}/apis`, { api_ids })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,35 @@
|
|||||||
import { http } from '@/lib'
|
import { http } from '@/lib'
|
||||||
import type {
|
import type { Role, SearchUserParams, User, UserFormOutput, UserInfo } from '@/schemas'
|
||||||
SearchSysUserParams,
|
|
||||||
SysRole,
|
|
||||||
SysUser,
|
|
||||||
SysUserFormOutput,
|
|
||||||
SysUserInfo,
|
|
||||||
} from '@/schemas'
|
|
||||||
import { type PageParams } from '@/schemas'
|
import { type PageParams } from '@/schemas'
|
||||||
|
|
||||||
export const getSysUsers = (params: PageParams & SearchSysUserParams) => {
|
export const listUsers = (params: PageParams & SearchUserParams) => {
|
||||||
return http.get<SysUser[], ApiPageResponse<SysUser>>('/user', { params })
|
return http.get<User[], ApiPageResponse<User>>('/users', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSysUserRoles = (id: number) => {
|
export const listUserRoles = (id: number) => {
|
||||||
return http.get<SysRole[]>(`/user/${id}/roles`)
|
return http.get<Role[]>(`/users/${id}/roles`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSysUserInfo = () => {
|
export const getUserInfo = () => {
|
||||||
return http.get<SysUserInfo>('/user/info')
|
return http.get<UserInfo>('/users/me')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateSysUser = (id: number, data: SysUserFormOutput) => {
|
export const updateUser = (id: number, data: UserFormOutput) => {
|
||||||
return http.patch('/user/' + id, data)
|
return http.patch('/users/' + id, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createSysUser = (data: SysUserFormOutput) => {
|
export const createUser = (data: UserFormOutput) => {
|
||||||
return http.post('/user', data)
|
return http.post('/users', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteSysUser = (id: number) => {
|
export const deleteUser = (id: number) => {
|
||||||
return http.delete(`/user/${id}`)
|
return http.delete(`/users/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const assignSysUserRoles = (id: number, role_ids: number[]) => {
|
export const assignUserRoles = (id: number, role_ids: number[]) => {
|
||||||
return http.put(`user/${id}/roles`, { role_ids })
|
return http.put(`users/${id}/roles`, { role_ids })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const changeSysUserPassword = (id: number, password: string) => {
|
export const updateUserPassword = (id: number, password: string) => {
|
||||||
return http.patch(`/user/${id}/password`, { password })
|
return http.patch(`/users/${id}/password`, { password })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export const WEditor = ({ defaultValues, onChange }: Props) => {
|
|||||||
uploadImage: {
|
uploadImage: {
|
||||||
async customUpload(file: File, insertFn: InsertFnType) {
|
async customUpload(file: File, insertFn: InsertFnType) {
|
||||||
const { data } = await uploadFile(file)
|
const { data } = await uploadFile(file)
|
||||||
insertFn(data.file_path, data.file_path, data.file_path)
|
insertFn(data.file_url, data.file_url, data.file_url)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from '@/components/ui/attachment.tsx'
|
} from '@/components/ui/attachment.tsx'
|
||||||
import { Input } from '@/components/ui/input.tsx'
|
import { Input } from '@/components/ui/input.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import type { SysFileForm } from '@/schemas'
|
import type { FileForm } from '@/schemas'
|
||||||
|
|
||||||
type State = 'idle' | 'uploading' | 'processing' | 'error' | 'done'
|
type State = 'idle' | 'uploading' | 'processing' | 'error' | 'done'
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@ type UploadFile = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FileUploadProps {
|
export interface FileUploadProps {
|
||||||
defaultFiles?: SysFileForm[]
|
defaultFiles?: FileForm[]
|
||||||
onChange?: (files: SysFileForm[]) => void
|
onChange?: (files: FileForm[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FileUpload = ({ defaultFiles, onChange }: FileUploadProps) => {
|
export const FileUpload = ({ defaultFiles, onChange }: FileUploadProps) => {
|
||||||
@@ -37,7 +37,7 @@ export const FileUpload = ({ defaultFiles, onChange }: FileUploadProps) => {
|
|||||||
id: item.id,
|
id: item.id,
|
||||||
uid: nanoid(),
|
uid: nanoid(),
|
||||||
state: 'done',
|
state: 'done',
|
||||||
file_name: item.file_path,
|
file_name: item.file_url,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ export const FileUpload = ({ defaultFiles, onChange }: FileUploadProps) => {
|
|||||||
.filter((item) => item.id)
|
.filter((item) => item.id)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
id: item.id as number,
|
id: item.id as number,
|
||||||
file_path: item.file_name,
|
file_url: item.file_name,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
onChange?.(_files)
|
onChange?.(_files)
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ import {
|
|||||||
} from '@/components/ui/sidebar'
|
} from '@/components/ui/sidebar'
|
||||||
import { MenuType } from '@/enum'
|
import { MenuType } from '@/enum'
|
||||||
import { generateMenus } from '@/lib'
|
import { generateMenus } from '@/lib'
|
||||||
import type { SysMenuTree } from '@/schemas'
|
import type { MenuTree } from '@/schemas'
|
||||||
import { siteConfig } from '@/siteConfig.ts'
|
import { siteConfig } from '@/siteConfig.ts'
|
||||||
import { useStore } from '@/store'
|
import { useStore } from '@/store'
|
||||||
|
|
||||||
const isMenuActive = (menu: SysMenuTree, pathname: string): boolean => {
|
const isMenuActive = (menu: MenuTree, pathname: string): boolean => {
|
||||||
if (menu.path === pathname) {
|
if (menu.path === pathname) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ const isMenuActive = (menu: SysMenuTree, pathname: string): boolean => {
|
|||||||
return menu.children?.some((item) => isMenuActive(item, pathname)) ?? false
|
return menu.children?.some((item) => isMenuActive(item, pathname)) ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
const MenuTreeItem = ({ menu, depth = 0 }: { menu: SysMenuTree; depth?: number }) => {
|
const MenuTreeItem = ({ menu, depth = 0 }: { menu: MenuTree; depth?: number }) => {
|
||||||
const { expandedMenus, toggleMenu } = useStore()
|
const { expandedMenus, toggleMenu } = useStore()
|
||||||
const pathname = useRouterState({ select: (state) => state.location.pathname })
|
const pathname = useRouterState({ select: (state) => state.location.pathname })
|
||||||
const active = isMenuActive(menu, pathname)
|
const active = isMenuActive(menu, pathname)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { MenuType } from '@/enum'
|
import { MenuType } from '@/enum'
|
||||||
import type { SysMenu, SysMenuTree } from '@/schemas'
|
import type { Menu, MenuTree } from '@/schemas'
|
||||||
|
|
||||||
// 生成树状菜单 用于菜单渲染
|
// 生成树状菜单 用于菜单渲染
|
||||||
export const generateMenus = ({
|
export const generateMenus = ({
|
||||||
@@ -8,11 +8,11 @@ export const generateMenus = ({
|
|||||||
showHidden = false,
|
showHidden = false,
|
||||||
showButton = false,
|
showButton = false,
|
||||||
}: {
|
}: {
|
||||||
menus: SysMenu[]
|
menus: Menu[]
|
||||||
parent_id?: number | null
|
parent_id?: number | null
|
||||||
showHidden?: boolean
|
showHidden?: boolean
|
||||||
showButton?: boolean
|
showButton?: boolean
|
||||||
}): SysMenuTree[] => {
|
}): MenuTree[] => {
|
||||||
return menus
|
return menus
|
||||||
.filter(
|
.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
@@ -27,7 +27,7 @@ export const generateMenus = ({
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateBreadcrumbs = (list: SysMenu[], path: string) => {
|
export const generateBreadcrumbs = (list: Menu[], path: string) => {
|
||||||
const breadcrumbs: string[] = []
|
const breadcrumbs: string[] = []
|
||||||
const findNodeId = list.find((item) => item.path === path)
|
const findNodeId = list.find((item) => item.path === path)
|
||||||
if (!findNodeId) return breadcrumbs
|
if (!findNodeId) return breadcrumbs
|
||||||
@@ -45,7 +45,7 @@ export const generateBreadcrumbs = (list: SysMenu[], path: string) => {
|
|||||||
return breadcrumbs
|
return breadcrumbs
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getExpandedMenus = (menus: SysMenu[]) => {
|
export const getExpandedMenus = (menus: Menu[]) => {
|
||||||
const expandedMenus: number[] = []
|
const expandedMenus: number[] = []
|
||||||
const path = location.pathname
|
const path = location.pathname
|
||||||
const findNodeId = menus.find((item) => item.path === path)
|
const findNodeId = menus.find((item) => item.path === path)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useMemo, useState } from 'react'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getCategories } from '@/api'
|
import { listCategories } from '@/api'
|
||||||
import { hasPermission } from '@/lib'
|
import { hasPermission } from '@/lib'
|
||||||
import { type Category } from '@/schemas'
|
import { type Category } from '@/schemas'
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ export default function CrudPage() {
|
|||||||
const { data, isFetching, refetch } = useQuery({
|
const { data, isFetching, refetch } = useQuery({
|
||||||
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getCategories({
|
listCategories({
|
||||||
page: pagination.pageIndex + 1,
|
page: pagination.pageIndex + 1,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { ImagePlus } from 'lucide-react'
|
import { ImagePlus } from 'lucide-react'
|
||||||
|
|
||||||
import { uploadFile } from '@/api'
|
import { uploadFile } from '@/api'
|
||||||
import type { SysFileForm } from '@/schemas'
|
import type { FileForm } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: SysFileForm | undefined
|
value: FileForm | undefined
|
||||||
onChange?: (file: SysFileForm) => void
|
onChange?: (file: FileForm) => void
|
||||||
invalid?: boolean
|
invalid?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CoverUpload = ({ value, onChange, invalid }: Props) => {
|
export const CoverUpload = ({ value, onChange, invalid }: Props) => {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
const [initialFile, setInitialFile] = useState<SysFileForm>()
|
const [initialFile, setInitialFile] = useState<FileForm>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInitialFile(value)
|
setInitialFile(value)
|
||||||
@@ -54,7 +54,7 @@ export const CoverUpload = ({ value, onChange, invalid }: Props) => {
|
|||||||
>
|
>
|
||||||
{initialFile ? (
|
{initialFile ? (
|
||||||
<div className='h-full'>
|
<div className='h-full'>
|
||||||
<img className='object-cover w-full h-full' src={initialFile.file_path} alt='' />
|
<img className='object-cover w-full h-full' src={initialFile.file_url} alt='' />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export default function PostEdit() {
|
|||||||
|
|
||||||
form.reset({
|
form.reset({
|
||||||
...data,
|
...data,
|
||||||
cover: { id: data.cover_id, file_path: data.cover },
|
cover: { id: data.cover_id, file_url: data.cover },
|
||||||
})
|
})
|
||||||
|
|
||||||
setEditorContent(data.content)
|
setEditorContent(data.content)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useMemo, useState } from 'react'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getPosts } from '@/api'
|
import { listPosts } from '@/api'
|
||||||
import { hasPermission } from '@/lib'
|
import { hasPermission } from '@/lib'
|
||||||
import { type Post } from '@/schemas'
|
import { type Post } from '@/schemas'
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ export default function CrudPage() {
|
|||||||
const { data, isFetching, refetch } = useQuery({
|
const { data, isFetching, refetch } = useQuery({
|
||||||
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getPosts({
|
listPosts({
|
||||||
page: pagination.pageIndex + 1,
|
page: pagination.pageIndex + 1,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useMemo, useState } from 'react'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getTags } from '@/api'
|
import { listTags } from '@/api'
|
||||||
import { hasPermission } from '@/lib'
|
import { hasPermission } from '@/lib'
|
||||||
import { type Tag } from '@/schemas'
|
import { type Tag } from '@/schemas'
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ export default function CrudPage() {
|
|||||||
const { data, isFetching, refetch } = useQuery({
|
const { data, isFetching, refetch } = useQuery({
|
||||||
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getTags({
|
listTags({
|
||||||
page: pagination.pageIndex + 1,
|
page: pagination.pageIndex + 1,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Controller, useForm } from 'react-hook-form'
|
|||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import z from 'zod'
|
import z from 'zod'
|
||||||
|
|
||||||
import { createSysApi, updateSysApi } from '@/api'
|
import { createApi, updateApi } from '@/api'
|
||||||
import { Badge } from '@/components/ui/badge.tsx'
|
import { Badge } from '@/components/ui/badge.tsx'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import {
|
import {
|
||||||
@@ -29,10 +29,10 @@ import {
|
|||||||
} from '@/components/ui/select.tsx'
|
} from '@/components/ui/select.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { HttpMethod, HttpMethodClass } from '@/enum'
|
import { HttpMethod, HttpMethodClass } from '@/enum'
|
||||||
import { SysApiFormSchema, type SysApi, type SysApiForm } from '@/schemas'
|
import { apiFormSchema, type Api, type ApiForm } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow?: SysApi | null
|
currentRow?: Api | null
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -45,8 +45,8 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
|
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof SysApiFormSchema>>({
|
const form = useForm<z.infer<typeof apiFormSchema>>({
|
||||||
resolver: zodResolver(SysApiFormSchema),
|
resolver: zodResolver(apiFormSchema),
|
||||||
defaultValues: currentRow
|
defaultValues: currentRow
|
||||||
? { ...currentRow }
|
? { ...currentRow }
|
||||||
: {
|
: {
|
||||||
@@ -58,13 +58,13 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSubmit = async (values: SysApiForm) => {
|
const onSubmit = async (values: ApiForm) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
if (values.id) {
|
if (values.id) {
|
||||||
await updateSysApi(values.id, values)
|
await updateApi(values.id, values)
|
||||||
} else {
|
} else {
|
||||||
await createSysApi(values)
|
await createApi(values)
|
||||||
}
|
}
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { Plus } from 'lucide-react'
|
|||||||
import { Auth } from '@/components/auth'
|
import { Auth } from '@/components/auth'
|
||||||
import { DataTable } from '@/components/data-table'
|
import { DataTable } from '@/components/data-table'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import type { SearchSysApiParams } from '@/schemas'
|
import type { SearchApiParams } from '@/schemas'
|
||||||
|
|
||||||
import { useCrud } from './crud-provider.tsx'
|
import { useCrud } from './crud-provider.tsx'
|
||||||
import { SearchForm } from './search-form.tsx'
|
import { SearchForm } from './search-form.tsx'
|
||||||
|
|
||||||
interface Props<TData> {
|
interface Props<TData> {
|
||||||
table: Table<TData>
|
table: Table<TData>
|
||||||
onSearch?: (params: SearchSysApiParams) => void
|
onSearch?: (params: SearchApiParams) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ApiTable<TData>({ table, onSearch }: Props<TData>) {
|
export function ApiTable<TData>({ table, onSearch }: Props<TData>) {
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import type { ColumnDef } from '@tanstack/react-table'
|
|||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { HttpMethodClass, type HttpMethodValues } from '@/enum'
|
import { HttpMethodClass, type HttpMethodValues } from '@/enum'
|
||||||
import { formatDate, hasPermission } from '@/lib'
|
import { formatDate, hasPermission } from '@/lib'
|
||||||
import type { SysApi } from '@/schemas'
|
import type { Api } from '@/schemas'
|
||||||
|
|
||||||
import { RowActions } from './row-actions'
|
import { RowActions } from './row-actions'
|
||||||
|
|
||||||
export const createColumns = (): ColumnDef<SysApi>[] => {
|
export const createColumns = (): ColumnDef<Api>[] => {
|
||||||
const showAction = hasPermission(['api:update', 'api:delete'], 'any')
|
const showAction = hasPermission(['api:update', 'api:delete'], 'any')
|
||||||
|
|
||||||
const columns: ColumnDef<SysApi>[] = [
|
const columns: ColumnDef<Api>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: 'name',
|
accessorKey: 'name',
|
||||||
header: '接口名称',
|
header: '接口名称',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createContext, useContext, useState, type ReactNode } from 'react'
|
import { createContext, useContext, useState, type ReactNode } from 'react'
|
||||||
|
|
||||||
import { type SysApi } from '@/schemas'
|
import { type Api } from '@/schemas'
|
||||||
|
|
||||||
import type { Action } from '../constants'
|
import type { Action } from '../constants'
|
||||||
|
|
||||||
@@ -11,11 +11,11 @@ type CrudContextType<T> = {
|
|||||||
setCurrentRow: (row: T | null) => void
|
setCurrentRow: (row: T | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CrudContext = createContext<CrudContextType<SysApi> | null>(null)
|
export const CrudContext = createContext<CrudContextType<Api> | null>(null)
|
||||||
|
|
||||||
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [action, setAction] = useState<Action>(null)
|
const [action, setAction] = useState<Action>(null)
|
||||||
const [currentRow, setCurrentRow] = useState<SysApi | null>(null)
|
const [currentRow, setCurrentRow] = useState<Api | null>(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState } from 'react'
|
|||||||
|
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { deleteSysApi } from '@/api'
|
import { deleteApi } from '@/api'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
@@ -14,10 +14,10 @@ import {
|
|||||||
} from '@/components/ui/alert-dialog'
|
} from '@/components/ui/alert-dialog'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { type SysApi } from '@/schemas'
|
import { type Api } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow: SysApi
|
currentRow: Api
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -29,7 +29,7 @@ export function DeleteDialog({ open, onClose, currentRow, onConfirm }: Props) {
|
|||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await deleteSysApi(currentRow.id)
|
await deleteApi(currentRow.id)
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu.tsx'
|
} from '@/components/ui/dropdown-menu.tsx'
|
||||||
import { type SysApi } from '@/schemas'
|
import { type Api } from '@/schemas'
|
||||||
|
|
||||||
import { useCrud } from './crud-provider'
|
import { useCrud } from './crud-provider'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
row: Row<SysApi>
|
row: Row<Api>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RowActions({ row }: Props) {
|
export function RowActions({ row }: Props) {
|
||||||
|
|||||||
@@ -17,24 +17,25 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select.tsx'
|
} from '@/components/ui/select.tsx'
|
||||||
import { HttpMethod, HttpMethodClass } from '@/enum'
|
import { HttpMethod, HttpMethodClass } from '@/enum'
|
||||||
import { searchSysApiSchema, type SearchSysApiParams } from '@/schemas'
|
import { searchApiSchema, type SearchApiParams } from '@/schemas'
|
||||||
|
|
||||||
interface SearchFormProps {
|
interface SearchFormProps {
|
||||||
onSearch?: (params: SearchSysApiParams) => void
|
onSearch?: (params: SearchApiParams) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SearchForm({ onSearch }: SearchFormProps) {
|
export function SearchForm({ onSearch }: SearchFormProps) {
|
||||||
const formId = useId()
|
const formId = useId()
|
||||||
|
|
||||||
const form = useForm<SearchSysApiParams>({
|
const form = useForm<SearchApiParams>({
|
||||||
resolver: zodResolver(searchSysApiSchema),
|
resolver: zodResolver(searchApiSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
|
name: '',
|
||||||
method: undefined,
|
method: undefined,
|
||||||
group_name: '',
|
group_name: '',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSubmit = (values: SearchSysApiParams) => {
|
const handleSubmit = (values: SearchApiParams) => {
|
||||||
onSearch?.(values)
|
onSearch?.(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +46,25 @@ export function SearchForm({ onSearch }: SearchFormProps) {
|
|||||||
return (
|
return (
|
||||||
<form id={formId} onSubmit={form.handleSubmit(handleSubmit)}>
|
<form id={formId} onSubmit={form.handleSubmit(handleSubmit)}>
|
||||||
<FieldGroup className='flex-row flex-wrap'>
|
<FieldGroup className='flex-row flex-wrap'>
|
||||||
|
<Controller
|
||||||
|
name='name'
|
||||||
|
control={form.control}
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<Field className='max-w-85' orientation='horizontal' data-invalid={fieldState.invalid}>
|
||||||
|
<FieldLabel htmlFor={`${formId}-${field.name}`} className='w-24'>
|
||||||
|
接口名称
|
||||||
|
</FieldLabel>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
id={`${formId}-${field.name}`}
|
||||||
|
aria-invalid={fieldState.invalid}
|
||||||
|
placeholder='请输入接口名称'
|
||||||
|
autoComplete='off'
|
||||||
|
/>
|
||||||
|
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
name='group_name'
|
name='group_name'
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { useMemo, useState } from 'react'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getSysApis } from '@/api'
|
import { listApis } from '@/api'
|
||||||
import { hasPermission } from '@/lib'
|
import { hasPermission } from '@/lib'
|
||||||
import { type SearchSysApiParams, type SysApi } from '@/schemas'
|
import { type Api, type SearchApiParams } from '@/schemas'
|
||||||
|
|
||||||
import { ActionDialogs } from './components/action-dialogs.tsx'
|
import { ActionDialogs } from './components/action-dialogs.tsx'
|
||||||
import { ApiTable } from './components/api-table.tsx'
|
import { ApiTable } from './components/api-table.tsx'
|
||||||
@@ -19,12 +19,12 @@ export default function CrudPage() {
|
|||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [searchParams, setSearchParams] = useState<SearchSysApiParams>({})
|
const [searchParams, setSearchParams] = useState<SearchApiParams>({})
|
||||||
|
|
||||||
const { data, isFetching, refetch } = useQuery({
|
const { data, isFetching, refetch } = useQuery({
|
||||||
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize, searchParams],
|
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize, searchParams],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getSysApis({
|
listApis({
|
||||||
page: pagination.pageIndex + 1,
|
page: pagination.pageIndex + 1,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
...searchParams,
|
...searchParams,
|
||||||
@@ -33,11 +33,11 @@ export default function CrudPage() {
|
|||||||
enabled: hasPermission('api:list'),
|
enabled: hasPermission('api:list'),
|
||||||
})
|
})
|
||||||
|
|
||||||
const tableData = useMemo<SysApi[]>(() => data?.list ?? [], [data])
|
const tableData = useMemo<Api[]>(() => data?.list ?? [], [data])
|
||||||
|
|
||||||
const columns = useMemo(() => createColumns(), [])
|
const columns = useMemo(() => createColumns(), [])
|
||||||
|
|
||||||
const table = useReactTable<SysApi>({
|
const table = useReactTable<Api>({
|
||||||
data: tableData,
|
data: tableData,
|
||||||
columns,
|
columns,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
@@ -51,7 +51,7 @@ export default function CrudPage() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSearch = (params: SearchSysApiParams) => {
|
const handleSearch = (params: SearchApiParams) => {
|
||||||
const isSameParams = JSON.stringify(params) === JSON.stringify(searchParams)
|
const isSameParams = JSON.stringify(params) === JSON.stringify(searchParams)
|
||||||
|
|
||||||
setSearchParams(params)
|
setSearchParams(params)
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ import type { ColumnDef } from '@tanstack/react-table'
|
|||||||
|
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { formatDate, formatFileSize } from '@/lib'
|
import { formatDate, formatFileSize } from '@/lib'
|
||||||
import type { SysFile } from '@/schemas'
|
import type { File } from '@/schemas'
|
||||||
|
|
||||||
export const createColumns = (): ColumnDef<SysFile>[] => {
|
export const createColumns = (): ColumnDef<File>[] => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
accessorKey: 'file_path',
|
accessorKey: 'file_url',
|
||||||
header: '文件路径',
|
header: '文件路径',
|
||||||
meta: { className: ' max-w-24' },
|
meta: { className: ' max-w-24' },
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Button variant='link' onClick={() => window.open(row.getValue('file_path'))}>
|
<Button variant='link' onClick={() => window.open(row.getValue('file_url'))}>
|
||||||
点击查看
|
点击查看
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createContext, useContext, useState, type ReactNode } from 'react'
|
import { createContext, useContext, useState, type ReactNode } from 'react'
|
||||||
|
|
||||||
import { type SysRole } from '@/schemas'
|
import { type Role } from '@/schemas'
|
||||||
|
|
||||||
import type { Action } from '../constants'
|
import type { Action } from '../constants'
|
||||||
|
|
||||||
@@ -11,11 +11,11 @@ type CrudContextType<T> = {
|
|||||||
setCurrentRow: (row: T | null) => void
|
setCurrentRow: (row: T | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CrudContext = createContext<CrudContextType<SysRole> | null>(null)
|
export const CrudContext = createContext<CrudContextType<Role> | null>(null)
|
||||||
|
|
||||||
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [action, setAction] = useState<Action>(null)
|
const [action, setAction] = useState<Action>(null)
|
||||||
const [currentRow, setCurrentRow] = useState<SysRole | null>(null)
|
const [currentRow, setCurrentRow] = useState<Role | null>(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { useMemo, useState } from 'react'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getSysFiles } from '@/api'
|
import { listFiles } from '@/api'
|
||||||
import { hasPermission } from '@/lib'
|
import { hasPermission } from '@/lib'
|
||||||
import type { SysFile } from '@/schemas'
|
import type { File } from '@/schemas'
|
||||||
|
|
||||||
import { createColumns } from './components/columns.tsx'
|
import { createColumns } from './components/columns.tsx'
|
||||||
import { CrudProvider } from './components/crud-provider.tsx'
|
import { CrudProvider } from './components/crud-provider.tsx'
|
||||||
@@ -21,7 +21,7 @@ export default function CrudPage() {
|
|||||||
const { data, isFetching, refetch } = useQuery({
|
const { data, isFetching, refetch } = useQuery({
|
||||||
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getSysFiles({
|
listFiles({
|
||||||
page: pagination.pageIndex + 1,
|
page: pagination.pageIndex + 1,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
}),
|
}),
|
||||||
@@ -29,11 +29,11 @@ export default function CrudPage() {
|
|||||||
enabled: hasPermission('file:list'),
|
enabled: hasPermission('file:list'),
|
||||||
})
|
})
|
||||||
|
|
||||||
const tableData = useMemo<SysFile[]>(() => data?.list ?? [], [data])
|
const tableData = useMemo<File[]>(() => data?.list ?? [], [data])
|
||||||
|
|
||||||
const columns = useMemo(() => createColumns(), [])
|
const columns = useMemo(() => createColumns(), [])
|
||||||
|
|
||||||
const table = useReactTable<SysFile>({
|
const table = useReactTable<File>({
|
||||||
data: tableData,
|
data: tableData,
|
||||||
columns,
|
columns,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
|
|||||||
import { Controller, useForm } from 'react-hook-form'
|
import { Controller, useForm } from 'react-hook-form'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { createSysMenu, getAllSysMenus, updateSysMenu } from '@/api'
|
import { createMenu, getAllMenus, updateMenu } from '@/api'
|
||||||
import { MenuIcon } from '@/components/menu-icon'
|
import { MenuIcon } from '@/components/menu-icon'
|
||||||
import { TreeSelect, type TreeSelectNode } from '@/components/tree-select'
|
import { TreeSelect, type TreeSelectNode } from '@/components/tree-select'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
@@ -30,10 +30,10 @@ import {
|
|||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { MenuIconOptions, MenuType, menuTypeOptions, Status, statusOptions } from '@/enum'
|
import { MenuIconOptions, MenuType, menuTypeOptions, Status, statusOptions } from '@/enum'
|
||||||
import { sysMenuFormSchema, type SysMenu, type SysMenuForm, type SysMenuTree } from '@/schemas'
|
import { menuFormSchema, type Menu, type MenuForm, type MenuTree } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow?: SysMenuTree | null
|
currentRow?: MenuTree | null
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -43,7 +43,7 @@ const ROOT_PARENT_VALUE = '__no_parent__'
|
|||||||
const NO_ICON_VALUE = '__no_icon__'
|
const NO_ICON_VALUE = '__no_icon__'
|
||||||
|
|
||||||
const generateMenuOptions = (
|
const generateMenuOptions = (
|
||||||
data: SysMenu[],
|
data: Menu[],
|
||||||
parent_id: number | null = null,
|
parent_id: number | null = null,
|
||||||
currentId: number | null = null,
|
currentId: number | null = null,
|
||||||
): TreeSelectNode[] => {
|
): TreeSelectNode[] => {
|
||||||
@@ -63,8 +63,8 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
const formId = useId()
|
const formId = useId()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const form = useForm<SysMenuForm>({
|
const form = useForm<MenuForm>({
|
||||||
resolver: zodResolver(sysMenuFormSchema),
|
resolver: zodResolver(menuFormSchema),
|
||||||
defaultValues: currentRow
|
defaultValues: currentRow
|
||||||
? { ...currentRow }
|
? { ...currentRow }
|
||||||
: {
|
: {
|
||||||
@@ -87,7 +87,7 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
if (!open) return
|
if (!open) return
|
||||||
|
|
||||||
const fetchOptions = async () => {
|
const fetchOptions = async () => {
|
||||||
const { data } = await getAllSysMenus()
|
const { data } = await getAllMenus()
|
||||||
const options = generateMenuOptions(data, null, currentRow?.id)
|
const options = generateMenuOptions(data, null, currentRow?.id)
|
||||||
setMenuOptions([{ id: ROOT_PARENT_VALUE, label: '无父级' }, ...options])
|
setMenuOptions([{ id: ROOT_PARENT_VALUE, label: '无父级' }, ...options])
|
||||||
}
|
}
|
||||||
@@ -95,13 +95,13 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
fetchOptions()
|
fetchOptions()
|
||||||
}, [open, currentRow?.id])
|
}, [open, currentRow?.id])
|
||||||
|
|
||||||
const onSubmit = async (values: SysMenuForm) => {
|
const onSubmit = async (values: MenuForm) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
if (values.id) {
|
if (values.id) {
|
||||||
await updateSysMenu(values.id, values)
|
await updateMenu(values.id, values)
|
||||||
} else {
|
} else {
|
||||||
await createSysMenu(values)
|
await createMenu(values)
|
||||||
}
|
}
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { StatusLabel } from '@/components/label'
|
|||||||
import { MenuIcon } from '@/components/menu-icon'
|
import { MenuIcon } from '@/components/menu-icon'
|
||||||
import { menuTypeDict, type MenuTypeValues, type StatusValues } from '@/enum'
|
import { menuTypeDict, type MenuTypeValues, type StatusValues } from '@/enum'
|
||||||
import { formatDate, hasPermission } from '@/lib'
|
import { formatDate, hasPermission } from '@/lib'
|
||||||
import type { SysMenuTree } from '@/schemas'
|
import type { MenuTree } from '@/schemas'
|
||||||
|
|
||||||
import { RowActions } from './row-actions.tsx'
|
import { RowActions } from './row-actions.tsx'
|
||||||
|
|
||||||
export const createColumns = (): ColumnDef<SysMenuTree>[] => {
|
export const createColumns = (): ColumnDef<MenuTree>[] => {
|
||||||
const showAction = hasPermission(['menu:update', 'menu:delete'], 'any')
|
const showAction = hasPermission(['menu:update', 'menu:delete'], 'any')
|
||||||
|
|
||||||
const columns: ColumnDef<SysMenuTree>[] = [
|
const columns: ColumnDef<MenuTree>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: 'name',
|
accessorKey: 'name',
|
||||||
header: '菜单名称',
|
header: '菜单名称',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createContext, useContext, useState, type ReactNode } from 'react'
|
import { createContext, useContext, useState, type ReactNode } from 'react'
|
||||||
|
|
||||||
import { type SysMenuTree } from '@/schemas'
|
import { type MenuTree } from '@/schemas'
|
||||||
|
|
||||||
import type { Action } from '../constants'
|
import type { Action } from '../constants'
|
||||||
|
|
||||||
@@ -11,11 +11,11 @@ type CrudContextType<T> = {
|
|||||||
setCurrentRow: (row: T | null) => void
|
setCurrentRow: (row: T | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CrudContext = createContext<CrudContextType<SysMenuTree> | null>(null)
|
export const CrudContext = createContext<CrudContextType<MenuTree> | null>(null)
|
||||||
|
|
||||||
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [action, setAction] = useState<Action>(null)
|
const [action, setAction] = useState<Action>(null)
|
||||||
const [currentRow, setCurrentRow] = useState<SysMenuTree | null>(null)
|
const [currentRow, setCurrentRow] = useState<MenuTree | null>(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState } from 'react'
|
|||||||
|
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { deleteSysMenu } from '@/api'
|
import { deleteMenu } from '@/api'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
@@ -14,10 +14,10 @@ import {
|
|||||||
} from '@/components/ui/alert-dialog'
|
} from '@/components/ui/alert-dialog'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { type SysMenuTree } from '@/schemas'
|
import { type MenuTree } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow: SysMenuTree
|
currentRow: MenuTree
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -29,7 +29,7 @@ export function DeleteDialog({ open, onClose, currentRow, onConfirm }: Props) {
|
|||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await deleteSysMenu(currentRow.id)
|
await deleteMenu(currentRow.id)
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu.tsx'
|
} from '@/components/ui/dropdown-menu.tsx'
|
||||||
import { type SysMenuTree } from '@/schemas'
|
import { type MenuTree } from '@/schemas'
|
||||||
|
|
||||||
import { useCrud } from './crud-provider'
|
import { useCrud } from './crud-provider'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
row: Row<SysMenuTree>
|
row: Row<MenuTree>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RowActions({ row }: Props) {
|
export function RowActions({ row }: Props) {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { useMemo } from 'react'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getCoreRowModel, getExpandedRowModel, useReactTable } from '@tanstack/react-table'
|
import { getCoreRowModel, getExpandedRowModel, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getAllSysMenus } from '@/api'
|
import { getAllMenus } from '@/api'
|
||||||
import { generateMenus, hasPermission } from '@/lib'
|
import { generateMenus, hasPermission } from '@/lib'
|
||||||
import { type SysMenuTree } from '@/schemas'
|
import { type MenuTree } from '@/schemas'
|
||||||
|
|
||||||
import { ActionDialogs } from './components/action-dialogs.tsx'
|
import { ActionDialogs } from './components/action-dialogs.tsx'
|
||||||
import { createColumns } from './components/columns.tsx'
|
import { createColumns } from './components/columns.tsx'
|
||||||
@@ -16,19 +16,19 @@ import { QUERY_KEY } from './constants.ts'
|
|||||||
export default function CrudPage() {
|
export default function CrudPage() {
|
||||||
const { data, isFetching, refetch } = useQuery({
|
const { data, isFetching, refetch } = useQuery({
|
||||||
queryKey: [QUERY_KEY],
|
queryKey: [QUERY_KEY],
|
||||||
queryFn: () => getAllSysMenus(),
|
queryFn: () => getAllMenus(),
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
enabled: hasPermission('menu:list'),
|
enabled: hasPermission('menu:list'),
|
||||||
})
|
})
|
||||||
|
|
||||||
const tableData = useMemo<SysMenuTree[]>(
|
const tableData = useMemo<MenuTree[]>(
|
||||||
() => generateMenus({ menus: data?.data ?? [], showHidden: true, showButton: true }),
|
() => generateMenus({ menus: data?.data ?? [], showHidden: true, showButton: true }),
|
||||||
[data],
|
[data],
|
||||||
)
|
)
|
||||||
|
|
||||||
const columns = useMemo(() => createColumns(), [])
|
const columns = useMemo(() => createColumns(), [])
|
||||||
|
|
||||||
const table = useReactTable<SysMenuTree>({
|
const table = useReactTable<MenuTree>({
|
||||||
data: tableData,
|
data: tableData,
|
||||||
columns,
|
columns,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Controller, useForm } from 'react-hook-form'
|
|||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import z from 'zod'
|
import z from 'zod'
|
||||||
|
|
||||||
import { createSysRole, updateSysRole } from '@/api'
|
import { createRole, updateRole } from '@/api'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -28,10 +28,10 @@ import {
|
|||||||
} from '@/components/ui/select.tsx'
|
} from '@/components/ui/select.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { Status, statusOptions } from '@/enum'
|
import { Status, statusOptions } from '@/enum'
|
||||||
import { sysRoleFormSchema, type SysRole, type SysRoleForm } from '@/schemas'
|
import { roleFormSchema, type Role, type RoleForm } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow?: SysRole | null
|
currentRow?: Role | null
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -42,8 +42,8 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
const formId = useId()
|
const formId = useId()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof sysRoleFormSchema>>({
|
const form = useForm<z.infer<typeof roleFormSchema>>({
|
||||||
resolver: zodResolver(sysRoleFormSchema),
|
resolver: zodResolver(roleFormSchema),
|
||||||
defaultValues: currentRow
|
defaultValues: currentRow
|
||||||
? { ...currentRow }
|
? { ...currentRow }
|
||||||
: {
|
: {
|
||||||
@@ -53,13 +53,13 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSubmit = async (values: SysRoleForm) => {
|
const onSubmit = async (values: RoleForm) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
if (values.id) {
|
if (values.id) {
|
||||||
await updateSysRole(values.id, values)
|
await updateRole(values.id, values)
|
||||||
} else {
|
} else {
|
||||||
await createSysRole(values)
|
await createRole(values)
|
||||||
}
|
}
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
|
|||||||
|
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { assignSysRoleApis, getAllSysApi, getRoleApis, getSysApiGroups } from '@/api'
|
import { assignRoleApis, getRoleApis, listAllApi, listApiGroups } from '@/api'
|
||||||
import { CheckboxTree, useCheckboxTree, type CheckboxTreeNode } from '@/components/checkbox-tree'
|
import { CheckboxTree, useCheckboxTree, type CheckboxTreeNode } from '@/components/checkbox-tree'
|
||||||
import { DialogSkeleton } from '@/components/dialog-skeleton'
|
import { DialogSkeleton } from '@/components/dialog-skeleton'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
@@ -18,10 +18,10 @@ import {
|
|||||||
} from '@/components/ui/dialog.tsx'
|
} from '@/components/ui/dialog.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { HttpMethodClass, type HttpMethodValues } from '@/enum'
|
import { HttpMethodClass, type HttpMethodValues } from '@/enum'
|
||||||
import type { SysRole } from '@/schemas'
|
import type { Role } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow?: SysRole | null
|
currentRow?: Role | null
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -38,8 +38,8 @@ export function AssignApisDialog({ open, onClose, onConfirm, currentRow }: Props
|
|||||||
const fetchApis = async () => {
|
const fetchApis = async () => {
|
||||||
setSkeletonLoading(true)
|
setSkeletonLoading(true)
|
||||||
|
|
||||||
const { data: groups } = await getSysApiGroups()
|
const { data: groups } = await listApiGroups()
|
||||||
const { data: apis } = await getAllSysApi()
|
const { data: apis } = await listAllApi()
|
||||||
const { data: checkedApi } = await getRoleApis(currentRow?.id as number)
|
const { data: checkedApi } = await getRoleApis(currentRow?.id as number)
|
||||||
|
|
||||||
const ids: string[] = checkedApi.map((api) => api.id.toString())
|
const ids: string[] = checkedApi.map((api) => api.id.toString())
|
||||||
@@ -80,7 +80,7 @@ export function AssignApisDialog({ open, onClose, onConfirm, currentRow }: Props
|
|||||||
.filter((item) => !item.startsWith('group_names_'))
|
.filter((item) => !item.startsWith('group_names_'))
|
||||||
.map((item) => parseInt(item))
|
.map((item) => parseInt(item))
|
||||||
|
|
||||||
await assignSysRoleApis(currentRow?.id as number, ids)
|
await assignRoleApis(currentRow?.id as number, ids)
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
|
|||||||
|
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { assignSysRoleMenus, getAllSysMenus, getSysRoleMenus } from '@/api'
|
import { assignRoleMenus, getAllMenus, getRoleMenus } from '@/api'
|
||||||
import { CheckboxTree, useCheckboxTree, type CheckboxTreeNode } from '@/components/checkbox-tree'
|
import { CheckboxTree, useCheckboxTree, type CheckboxTreeNode } from '@/components/checkbox-tree'
|
||||||
import { DialogSkeleton } from '@/components/dialog-skeleton'
|
import { DialogSkeleton } from '@/components/dialog-skeleton'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
@@ -16,16 +16,16 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog.tsx'
|
} from '@/components/ui/dialog.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import type { SysMenu, SysRole } from '@/schemas'
|
import type { Menu, Role } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow?: SysRole | null
|
currentRow?: Role | null
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildTreeData = (data: SysMenu[], parentId: string | null = null): CheckboxTreeNode[] => {
|
const buildTreeData = (data: Menu[], parentId: string | null = null): CheckboxTreeNode[] => {
|
||||||
return data
|
return data
|
||||||
.filter((item) => (item.parent_id?.toString() ?? null) === parentId)
|
.filter((item) => (item.parent_id?.toString() ?? null) === parentId)
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
@@ -52,8 +52,8 @@ export function AssignMenusDialog({ open, onClose, onConfirm, currentRow }: Prop
|
|||||||
const fetchMenus = async () => {
|
const fetchMenus = async () => {
|
||||||
setSkeletonLoading(true)
|
setSkeletonLoading(true)
|
||||||
|
|
||||||
const { data: menus } = await getAllSysMenus()
|
const { data: menus } = await getAllMenus()
|
||||||
const { data: checkedMenus } = await getSysRoleMenus(currentRow?.id as number)
|
const { data: checkedMenus } = await getRoleMenus(currentRow?.id as number)
|
||||||
setTreeData(buildTreeData(menus))
|
setTreeData(buildTreeData(menus))
|
||||||
setCheckedIds(checkedMenus.map((item) => item.id.toString()))
|
setCheckedIds(checkedMenus.map((item) => item.id.toString()))
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ export function AssignMenusDialog({ open, onClose, onConfirm, currentRow }: Prop
|
|||||||
const handleConfirm = async () => {
|
const handleConfirm = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await assignSysRoleMenus(
|
await assignRoleMenus(
|
||||||
currentRow?.id as number,
|
currentRow?.id as number,
|
||||||
getCheckedIdsWithParents().map((item) => parseInt(item)),
|
getCheckedIdsWithParents().map((item) => parseInt(item)),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ import type { ColumnDef } from '@tanstack/react-table'
|
|||||||
import { StatusLabel } from '@/components/label'
|
import { StatusLabel } from '@/components/label'
|
||||||
import { type StatusValues } from '@/enum'
|
import { type StatusValues } from '@/enum'
|
||||||
import { formatDate, hasPermission } from '@/lib'
|
import { formatDate, hasPermission } from '@/lib'
|
||||||
import type { SysRole } from '@/schemas'
|
import type { Role } from '@/schemas'
|
||||||
|
|
||||||
import { RowActions } from './row-actions'
|
import { RowActions } from './row-actions'
|
||||||
|
|
||||||
export const createColumns = (): ColumnDef<SysRole>[] => {
|
export const createColumns = (): ColumnDef<Role>[] => {
|
||||||
const showAction = hasPermission(
|
const showAction = hasPermission(
|
||||||
['role:update', 'role:delete', 'role:assign-menus', 'role:assign-apis'],
|
['role:update', 'role:delete', 'role:assign-menus', 'role:assign-apis'],
|
||||||
'any',
|
'any',
|
||||||
)
|
)
|
||||||
|
|
||||||
const columns: ColumnDef<SysRole>[] = [
|
const columns: ColumnDef<Role>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: 'name',
|
accessorKey: 'name',
|
||||||
header: '角色名称',
|
header: '角色名称',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createContext, useContext, useState, type ReactNode } from 'react'
|
import { createContext, useContext, useState, type ReactNode } from 'react'
|
||||||
|
|
||||||
import { type SysRole } from '@/schemas'
|
import { type Role } from '@/schemas'
|
||||||
|
|
||||||
import type { Action } from '../constants'
|
import type { Action } from '../constants'
|
||||||
|
|
||||||
@@ -11,11 +11,11 @@ type CrudContextType<T> = {
|
|||||||
setCurrentRow: (row: T | null) => void
|
setCurrentRow: (row: T | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CrudContext = createContext<CrudContextType<SysRole> | null>(null)
|
export const CrudContext = createContext<CrudContextType<Role> | null>(null)
|
||||||
|
|
||||||
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [action, setAction] = useState<Action>(null)
|
const [action, setAction] = useState<Action>(null)
|
||||||
const [currentRow, setCurrentRow] = useState<SysRole | null>(null)
|
const [currentRow, setCurrentRow] = useState<Role | null>(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState } from 'react'
|
|||||||
|
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { deleteSysRole } from '@/api'
|
import { deleteRole } from '@/api'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
@@ -14,10 +14,10 @@ import {
|
|||||||
} from '@/components/ui/alert-dialog'
|
} from '@/components/ui/alert-dialog'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { type SysRole } from '@/schemas'
|
import { type Role } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow: SysRole
|
currentRow: Role
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -29,7 +29,7 @@ export function DeleteDialog({ open, onClose, currentRow, onConfirm }: Props) {
|
|||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await deleteSysRole(currentRow.id)
|
await deleteRole(currentRow.id)
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu.tsx'
|
} from '@/components/ui/dropdown-menu.tsx'
|
||||||
import { type SysRole } from '@/schemas'
|
import { type Role } from '@/schemas'
|
||||||
|
|
||||||
import { useCrud } from './crud-provider'
|
import { useCrud } from './crud-provider'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
row: Row<SysRole>
|
row: Row<Role>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RowActions({ row }: Props) {
|
export function RowActions({ row }: Props) {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { useMemo, useState } from 'react'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getSysRoles } from '@/api'
|
import { listRoles } from '@/api'
|
||||||
import { hasPermission } from '@/lib'
|
import { hasPermission } from '@/lib'
|
||||||
import { type SysRole } from '@/schemas'
|
import { type Role } from '@/schemas'
|
||||||
|
|
||||||
import { ActionDialogs } from './components/action-dialogs.tsx'
|
import { ActionDialogs } from './components/action-dialogs.tsx'
|
||||||
import { createColumns } from './components/columns.tsx'
|
import { createColumns } from './components/columns.tsx'
|
||||||
@@ -22,7 +22,7 @@ export default function CrudPage() {
|
|||||||
const { data, isFetching, refetch } = useQuery({
|
const { data, isFetching, refetch } = useQuery({
|
||||||
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getSysRoles({
|
listRoles({
|
||||||
page: pagination.pageIndex + 1,
|
page: pagination.pageIndex + 1,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
}),
|
}),
|
||||||
@@ -30,11 +30,11 @@ export default function CrudPage() {
|
|||||||
enabled: hasPermission('role:list'),
|
enabled: hasPermission('role:list'),
|
||||||
})
|
})
|
||||||
|
|
||||||
const tableData = useMemo<SysRole[]>(() => data?.list ?? [], [data])
|
const tableData = useMemo<Role[]>(() => data?.list ?? [], [data])
|
||||||
|
|
||||||
const columns = useMemo(() => createColumns(), [])
|
const columns = useMemo(() => createColumns(), [])
|
||||||
|
|
||||||
const table = useReactTable<SysRole>({
|
const table = useReactTable<Role>({
|
||||||
data: tableData,
|
data: tableData,
|
||||||
columns,
|
columns,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
|
|||||||
import { Controller, useForm } from 'react-hook-form'
|
import { Controller, useForm } from 'react-hook-form'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { createSysUser, updateSysUser } from '@/api'
|
import { createUser, updateUser } from '@/api'
|
||||||
import { FileUpload } from '@/components/file-upload'
|
import { FileUpload } from '@/components/file-upload'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import {
|
import {
|
||||||
@@ -28,15 +28,10 @@ import {
|
|||||||
} from '@/components/ui/select.tsx'
|
} from '@/components/ui/select.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { Status, statusOptions } from '@/enum'
|
import { Status, statusOptions } from '@/enum'
|
||||||
import {
|
import { userFormSchema, type User, type UserFormInput, type UserFormOutput } from '@/schemas'
|
||||||
sysUserFormSchema,
|
|
||||||
type SysUser,
|
|
||||||
type SysUserFormInput,
|
|
||||||
type SysUserFormOutput,
|
|
||||||
} from '@/schemas'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow?: SysUser | null
|
currentRow?: User | null
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -47,13 +42,13 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
const formId = useId()
|
const formId = useId()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const form = useForm<SysUserFormInput, undefined, SysUserFormOutput>({
|
const form = useForm<UserFormInput, undefined, UserFormOutput>({
|
||||||
resolver: zodResolver(sysUserFormSchema),
|
resolver: zodResolver(userFormSchema),
|
||||||
defaultValues: currentRow
|
defaultValues: currentRow
|
||||||
? {
|
? {
|
||||||
...currentRow,
|
...currentRow,
|
||||||
avatar: currentRow.avatar_id
|
avatar: currentRow.avatar_id
|
||||||
? [{ id: currentRow.avatar_id, file_path: currentRow.avatar_url }]
|
? [{ id: currentRow.avatar_id, file_url: currentRow.avatar_url }]
|
||||||
: [],
|
: [],
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
@@ -66,14 +61,14 @@ export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSubmit = async (values: SysUserFormOutput) => {
|
const onSubmit = async (values: UserFormOutput) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
if (values.id) {
|
if (values.id) {
|
||||||
await updateSysUser(values.id, values)
|
await updateUser(values.id, values)
|
||||||
} else {
|
} else {
|
||||||
await createSysUser(values)
|
await createUser(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
|
|||||||
|
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { assignSysUserRoles, getAllSysRoles, getSysUserRoles } from '@/api'
|
import { assignUserRoles, listAllRoles, listUserRoles } from '@/api'
|
||||||
import { DialogSkeleton } from '@/components/dialog-skeleton'
|
import { DialogSkeleton } from '@/components/dialog-skeleton'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import { Checkbox } from '@/components/ui/checkbox'
|
import { Checkbox } from '@/components/ui/checkbox'
|
||||||
@@ -24,17 +24,17 @@ import {
|
|||||||
FieldSet,
|
FieldSet,
|
||||||
} from '@/components/ui/field.tsx'
|
} from '@/components/ui/field.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import type { SysRole, SysUser } from '@/schemas'
|
import type { Role, User } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow?: SysUser | null
|
currentRow?: User | null
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AssignRolesDialog({ open, onClose, onConfirm, currentRow }: Props) {
|
export function AssignRolesDialog({ open, onClose, onConfirm, currentRow }: Props) {
|
||||||
const [roles, setRoles] = useState<SysRole[]>([])
|
const [roles, setRoles] = useState<Role[]>([])
|
||||||
const [checkedIds, setCheckedIds] = useState<number[]>([])
|
const [checkedIds, setCheckedIds] = useState<number[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [skeletonLoading, setSkeletonLoading] = useState(true)
|
const [skeletonLoading, setSkeletonLoading] = useState(true)
|
||||||
@@ -42,7 +42,7 @@ export function AssignRolesDialog({ open, onClose, onConfirm, currentRow }: Prop
|
|||||||
const handleConfirm = async () => {
|
const handleConfirm = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await assignSysUserRoles(currentRow!.id, checkedIds)
|
await assignUserRoles(currentRow!.id, checkedIds)
|
||||||
onConfirm()
|
onConfirm()
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -58,8 +58,8 @@ export function AssignRolesDialog({ open, onClose, onConfirm, currentRow }: Prop
|
|||||||
const fetchUserRoles = async () => {
|
const fetchUserRoles = async () => {
|
||||||
setSkeletonLoading(true)
|
setSkeletonLoading(true)
|
||||||
|
|
||||||
const { data } = await getAllSysRoles()
|
const { data } = await listAllRoles()
|
||||||
const { data: userRoles } = await getSysUserRoles(currentRow!.id)
|
const { data: userRoles } = await listUserRoles(currentRow!.id)
|
||||||
setRoles(data)
|
setRoles(data)
|
||||||
setCheckedIds(userRoles.map((role) => role.id))
|
setCheckedIds(userRoles.map((role) => role.id))
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Controller, useForm } from 'react-hook-form'
|
|||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
import { changeSysUserPassword } from '@/api'
|
import { updateUserPassword } from '@/api'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -19,10 +19,10 @@ import {
|
|||||||
import { Field, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field'
|
import { Field, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { changePasswordFormSchema, type ChangePasswordForm, type SysUser } from '@/schemas'
|
import { changePasswordFormSchema, type ChangePasswordForm, type User } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow?: SysUser | null
|
currentRow?: User | null
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -42,7 +42,7 @@ export function ChangePasswordDialog({ open, currentRow, onClose, onConfirm }: P
|
|||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
await changeSysUserPassword(currentRow.id, values.password)
|
await updateUserPassword(currentRow.id, values.password)
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ import { StatusLabel } from '@/components/label'
|
|||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||||
import { type StatusValues } from '@/enum'
|
import { type StatusValues } from '@/enum'
|
||||||
import { formatDate, hasPermission } from '@/lib'
|
import { formatDate, hasPermission } from '@/lib'
|
||||||
import type { SysUser } from '@/schemas'
|
import type { User } from '@/schemas'
|
||||||
|
|
||||||
import { RowActions } from './row-actions'
|
import { RowActions } from './row-actions'
|
||||||
|
|
||||||
export const createColumns = (): ColumnDef<SysUser>[] => {
|
export const createColumns = (): ColumnDef<User>[] => {
|
||||||
const showAction = hasPermission(
|
const showAction = hasPermission(
|
||||||
['user:update', 'user:assign-roles', 'user:update-password', 'user:delete'],
|
['user:update', 'user:assign-roles', 'user:update-password', 'user:delete'],
|
||||||
'any',
|
'any',
|
||||||
)
|
)
|
||||||
|
|
||||||
const columns: ColumnDef<SysUser>[] = [
|
const columns: ColumnDef<User>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: 'avatar_url',
|
accessorKey: 'avatar_url',
|
||||||
header: '用户头像',
|
header: '用户头像',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createContext, useContext, useState, type ReactNode } from 'react'
|
import { createContext, useContext, useState, type ReactNode } from 'react'
|
||||||
|
|
||||||
import { type SysUser } from '@/schemas'
|
import { type User } from '@/schemas'
|
||||||
|
|
||||||
import type { Action } from '../constants'
|
import type { Action } from '../constants'
|
||||||
|
|
||||||
@@ -11,11 +11,11 @@ type CrudContextType<T> = {
|
|||||||
setCurrentRow: (row: T | null) => void
|
setCurrentRow: (row: T | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CrudContext = createContext<CrudContextType<SysUser> | null>(null)
|
export const CrudContext = createContext<CrudContextType<User> | null>(null)
|
||||||
|
|
||||||
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
export const CrudProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [action, setAction] = useState<Action>(null)
|
const [action, setAction] = useState<Action>(null)
|
||||||
const [currentRow, setCurrentRow] = useState<SysUser | null>(null)
|
const [currentRow, setCurrentRow] = useState<User | null>(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
<CrudContext value={{ action, setAction, currentRow, setCurrentRow }}>{children}</CrudContext>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState } from 'react'
|
|||||||
|
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { deleteSysUser } from '@/api'
|
import { deleteUser } from '@/api'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
@@ -14,10 +14,10 @@ import {
|
|||||||
} from '@/components/ui/alert-dialog'
|
} from '@/components/ui/alert-dialog'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import { Spinner } from '@/components/ui/spinner.tsx'
|
import { Spinner } from '@/components/ui/spinner.tsx'
|
||||||
import { type SysUser } from '@/schemas'
|
import { type User } from '@/schemas'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentRow: SysUser
|
currentRow: User
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onConfirm: () => void
|
onConfirm: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -29,7 +29,7 @@ export function DeleteDialog({ open, onClose, currentRow, onConfirm }: Props) {
|
|||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await deleteSysUser(currentRow.id)
|
await deleteUser(currentRow.id)
|
||||||
toast('操作成功!')
|
toast('操作成功!')
|
||||||
onConfirm()
|
onConfirm()
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu.tsx'
|
} from '@/components/ui/dropdown-menu.tsx'
|
||||||
import { type SysUser } from '@/schemas'
|
import { type User } from '@/schemas'
|
||||||
|
|
||||||
import { useCrud } from './crud-provider'
|
import { useCrud } from './crud-provider'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
row: Row<SysUser>
|
row: Row<User>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RowActions({ row }: Props) {
|
export function RowActions({ row }: Props) {
|
||||||
|
|||||||
@@ -7,23 +7,23 @@ import { Controller, useForm } from 'react-hook-form'
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Field, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field.tsx'
|
import { Field, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field.tsx'
|
||||||
import { Input } from '@/components/ui/input.tsx'
|
import { Input } from '@/components/ui/input.tsx'
|
||||||
import { searchSysUserSchema, type SearchSysUserParams } from '@/schemas'
|
import { searchUserSchema, type SearchUserParams } from '@/schemas'
|
||||||
|
|
||||||
interface SearchFormProps {
|
interface SearchFormProps {
|
||||||
onSearch?: (params: SearchSysUserParams) => void
|
onSearch?: (params: SearchUserParams) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SearchForm({ onSearch }: SearchFormProps) {
|
export function SearchForm({ onSearch }: SearchFormProps) {
|
||||||
const formId = useId()
|
const formId = useId()
|
||||||
|
|
||||||
const form = useForm<SearchSysUserParams>({
|
const form = useForm<SearchUserParams>({
|
||||||
resolver: zodResolver(searchSysUserSchema),
|
resolver: zodResolver(searchUserSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
username: '',
|
username: '',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSubmit = (values: SearchSysUserParams) => {
|
const handleSubmit = (values: SearchUserParams) => {
|
||||||
onSearch?.(values)
|
onSearch?.(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { Plus } from 'lucide-react'
|
|||||||
import { Auth } from '@/components/auth'
|
import { Auth } from '@/components/auth'
|
||||||
import { DataTable } from '@/components/data-table'
|
import { DataTable } from '@/components/data-table'
|
||||||
import { Button } from '@/components/ui/button.tsx'
|
import { Button } from '@/components/ui/button.tsx'
|
||||||
import type { SearchSysUserParams } from '@/schemas'
|
import type { SearchUserParams } from '@/schemas'
|
||||||
|
|
||||||
import { useCrud } from './crud-provider.tsx'
|
import { useCrud } from './crud-provider.tsx'
|
||||||
import { SearchForm } from './search-form.tsx'
|
import { SearchForm } from './search-form.tsx'
|
||||||
|
|
||||||
interface Props<TData> {
|
interface Props<TData> {
|
||||||
table: Table<TData>
|
table: Table<TData>
|
||||||
onSearch?: (params: SearchSysUserParams) => void
|
onSearch?: (params: SearchUserParams) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UserTable<TData>({ table, onSearch }: Props<TData>) {
|
export function UserTable<TData>({ table, onSearch }: Props<TData>) {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { useMemo, useState } from 'react'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getSysUsers } from '@/api'
|
import { listUsers } from '@/api'
|
||||||
import { hasPermission } from '@/lib'
|
import { hasPermission } from '@/lib'
|
||||||
import { type SearchSysUserParams, type SysUser } from '@/schemas'
|
import { type SearchUserParams, type User } from '@/schemas'
|
||||||
|
|
||||||
import { ActionDialogs } from './components/action-dialogs.tsx'
|
import { ActionDialogs } from './components/action-dialogs.tsx'
|
||||||
import { createColumns } from './components/columns.tsx'
|
import { createColumns } from './components/columns.tsx'
|
||||||
@@ -19,12 +19,12 @@ export default function CrudPage() {
|
|||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [searchParams, setSearchParams] = useState<SearchSysUserParams>({})
|
const [searchParams, setSearchParams] = useState<SearchUserParams>({})
|
||||||
|
|
||||||
const { data, isFetching, refetch } = useQuery({
|
const { data, isFetching, refetch } = useQuery({
|
||||||
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize, searchParams],
|
queryKey: [QUERY_KEY, pagination.pageIndex, pagination.pageSize, searchParams],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getSysUsers({
|
listUsers({
|
||||||
page: pagination.pageIndex + 1,
|
page: pagination.pageIndex + 1,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
...searchParams,
|
...searchParams,
|
||||||
@@ -33,11 +33,11 @@ export default function CrudPage() {
|
|||||||
enabled: hasPermission('user:list'),
|
enabled: hasPermission('user:list'),
|
||||||
})
|
})
|
||||||
|
|
||||||
const tableData = useMemo<SysUser[]>(() => data?.list ?? [], [data])
|
const tableData = useMemo<User[]>(() => data?.list ?? [], [data])
|
||||||
|
|
||||||
const columns = useMemo(() => createColumns(), [])
|
const columns = useMemo(() => createColumns(), [])
|
||||||
|
|
||||||
const table = useReactTable<SysUser>({
|
const table = useReactTable<User>({
|
||||||
data: tableData,
|
data: tableData,
|
||||||
columns,
|
columns,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
@@ -51,7 +51,7 @@ export default function CrudPage() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSearch = (params: SearchSysUserParams) => {
|
const handleSearch = (params: SearchUserParams) => {
|
||||||
setSearchParams(params)
|
setSearchParams(params)
|
||||||
setPagination((prev) => ({ ...prev, pageIndex: 0 }))
|
setPagination((prev) => ({ ...prev, pageIndex: 0 }))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
|
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
|
||||||
|
|
||||||
import NotFound from '@/pages/404/index'
|
import NotFound from '@/pages/404/index'
|
||||||
import { type SysMenu } from '@/schemas'
|
import { type Menu } from '@/schemas'
|
||||||
import { siteConfig } from '@/siteConfig.ts'
|
import { siteConfig } from '@/siteConfig.ts'
|
||||||
import { useStore } from '@/store'
|
import { useStore } from '@/store'
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ const getComponent = (fileName: string) => {
|
|||||||
return lazyRouteComponent(importer)
|
return lazyRouteComponent(importer)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setupRouter = (menus: SysMenu[]) => {
|
export const setupRouter = (menus: Menu[]) => {
|
||||||
const rootChildren: AnyRoute[] = []
|
const rootChildren: AnyRoute[] = []
|
||||||
const layoutChildren: AnyRoute[] = []
|
const layoutChildren: AnyRoute[] = []
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
import { sysFileFormSchema } from '@/schemas'
|
import { fileFormSchema } from '@/schemas'
|
||||||
|
|
||||||
export const postSchema = z.object({
|
export const postSchema = z.object({
|
||||||
id: z.number().int(),
|
id: z.number().int(),
|
||||||
@@ -34,7 +34,7 @@ export const postFormSchema = z
|
|||||||
summary: z.string(),
|
summary: z.string(),
|
||||||
status: z.number().int(),
|
status: z.number().int(),
|
||||||
published_at: z.string().min(1, '请选择发布日期!'),
|
published_at: z.string().min(1, '请选择发布日期!'),
|
||||||
cover: sysFileFormSchema,
|
cover: fileFormSchema,
|
||||||
sort: z.coerce.number<number>().int().min(0, '排序值不能小于0'),
|
sort: z.coerce.number<number>().int().min(0, '排序值不能小于0'),
|
||||||
content: z.string().optional(),
|
content: z.string().optional(),
|
||||||
category_id: z
|
category_id: z
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { z } from 'zod'
|
|||||||
|
|
||||||
import { HttpMethodSchema } from '@/enum'
|
import { HttpMethodSchema } from '@/enum'
|
||||||
|
|
||||||
export const SysApiSchema = z.object({
|
export const ApiSchema = z.object({
|
||||||
id: z.number().int(),
|
id: z.number().int(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
group_name: z.string(),
|
group_name: z.string(),
|
||||||
@@ -11,17 +11,18 @@ export const SysApiSchema = z.object({
|
|||||||
sort: z.number(),
|
sort: z.number(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysApi = z.infer<typeof SysApiSchema>
|
export type Api = z.infer<typeof ApiSchema>
|
||||||
|
|
||||||
// 搜索参数
|
// 搜索参数
|
||||||
export const searchSysApiSchema = z.object({
|
export const searchApiSchema = z.object({
|
||||||
|
name: z.string().optional(),
|
||||||
group_name: z.string().optional(),
|
group_name: z.string().optional(),
|
||||||
method: HttpMethodSchema.optional(),
|
method: HttpMethodSchema.optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SearchSysApiParams = z.infer<typeof searchSysApiSchema>
|
export type SearchApiParams = z.infer<typeof searchApiSchema>
|
||||||
|
|
||||||
export const SysApiFormSchema = z.object({
|
export const apiFormSchema = z.object({
|
||||||
id: z.number().optional(),
|
id: z.number().optional(),
|
||||||
name: z.string().min(2, '接口名称至少2个字符').max(16, '接口名称不能超过16个字符'),
|
name: z.string().min(2, '接口名称至少2个字符').max(16, '接口名称不能超过16个字符'),
|
||||||
group_name: z.string().min(2, '分组名称至少2个字符').max(16, '分组名称不能超过16个字符'),
|
group_name: z.string().min(2, '分组名称至少2个字符').max(16, '分组名称不能超过16个字符'),
|
||||||
@@ -30,4 +31,4 @@ export const SysApiFormSchema = z.object({
|
|||||||
sort: z.coerce.number<number>().int().min(0, '排序值不能小于0'),
|
sort: z.coerce.number<number>().int().min(0, '排序值不能小于0'),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysApiForm = z.infer<typeof SysApiFormSchema>
|
export type ApiForm = z.infer<typeof apiFormSchema>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
export const sysFileSchema = z.object({
|
export const fileSchema = z.object({
|
||||||
id: z.number().int(),
|
id: z.number().int(),
|
||||||
file_name: z.string(),
|
file_name: z.string(),
|
||||||
file_path: z.string(),
|
file_path: z.string(),
|
||||||
|
file_url: z.string(),
|
||||||
original_name: z.string(),
|
original_name: z.string(),
|
||||||
folder_name: z.string(),
|
folder_name: z.string(),
|
||||||
mime_type: z.string(),
|
mime_type: z.string(),
|
||||||
@@ -12,11 +13,11 @@ export const sysFileSchema = z.object({
|
|||||||
updated_at: z.string(),
|
updated_at: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysFile = z.infer<typeof sysFileSchema>
|
export type File = z.infer<typeof fileSchema>
|
||||||
|
|
||||||
export const sysFileFormSchema = z.object({
|
export const fileFormSchema = z.object({
|
||||||
id: z.number().int(),
|
id: z.number().int(),
|
||||||
file_path: z.string(),
|
file_url: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysFileForm = z.infer<typeof sysFileFormSchema>
|
export type FileForm = z.infer<typeof fileFormSchema>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const MenuIconKeySchema = z.custom<MenuIconKeyValues>(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export const sysMenuSchema = z.object({
|
export const menuSchema = z.object({
|
||||||
id: z.number().int(),
|
id: z.number().int(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
@@ -25,13 +25,13 @@ export const sysMenuSchema = z.object({
|
|||||||
updated_at: z.string(),
|
updated_at: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysMenu = z.infer<typeof sysMenuSchema>
|
export type Menu = z.infer<typeof menuSchema>
|
||||||
|
|
||||||
export interface SysMenuTree extends SysMenu {
|
export interface MenuTree extends Menu {
|
||||||
children?: SysMenuTree[]
|
children?: MenuTree[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sysMenuFormSchema = z
|
export const menuFormSchema = z
|
||||||
.object({
|
.object({
|
||||||
id: z.number().optional(),
|
id: z.number().optional(),
|
||||||
name: z.string().min(1, '菜单名称不能为空').max(16, '菜单名称不能超过16个字符'),
|
name: z.string().min(1, '菜单名称不能为空').max(16, '菜单名称不能超过16个字符'),
|
||||||
@@ -63,4 +63,4 @@ export const sysMenuFormSchema = z
|
|||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysMenuForm = z.infer<typeof sysMenuFormSchema>
|
export type MenuForm = z.infer<typeof menuFormSchema>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { z } from 'zod'
|
|||||||
|
|
||||||
import { StatusSchema } from '@/enum'
|
import { StatusSchema } from '@/enum'
|
||||||
|
|
||||||
export const sysRoleSchema = z.object({
|
export const roleSchema = z.object({
|
||||||
id: z.number().int(),
|
id: z.number().int(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
code: z.string(),
|
code: z.string(),
|
||||||
@@ -11,9 +11,9 @@ export const sysRoleSchema = z.object({
|
|||||||
updated_at: z.string(),
|
updated_at: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysRole = z.infer<typeof sysRoleSchema>
|
export type Role = z.infer<typeof roleSchema>
|
||||||
|
|
||||||
export const sysRoleFormSchema = z
|
export const roleFormSchema = z
|
||||||
.object({
|
.object({
|
||||||
id: z.number().optional(),
|
id: z.number().optional(),
|
||||||
name: z.string().min(2, '角色名称至少2个字符').max(16, '角色名称不能超过16个字符'),
|
name: z.string().min(2, '角色名称至少2个字符').max(16, '角色名称不能超过16个字符'),
|
||||||
@@ -33,4 +33,4 @@ export const sysRoleFormSchema = z
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysRoleForm = z.infer<typeof sysRoleFormSchema>
|
export type RoleForm = z.infer<typeof roleFormSchema>
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { z } from 'zod'
|
|||||||
|
|
||||||
import { StatusSchema } from '@/enum'
|
import { StatusSchema } from '@/enum'
|
||||||
|
|
||||||
import { sysFileFormSchema } from './file'
|
import { fileFormSchema } from './file'
|
||||||
import { sysMenuSchema } from './menu'
|
import { menuSchema } from './menu'
|
||||||
|
|
||||||
export const sysUserSchema = z.object({
|
export const userSchema = z.object({
|
||||||
id: z.number().int(),
|
id: z.number().int(),
|
||||||
account: z.string(),
|
account: z.string(),
|
||||||
username: z.string(),
|
username: z.string(),
|
||||||
@@ -16,15 +16,15 @@ export const sysUserSchema = z.object({
|
|||||||
avatar_id: z.number().int().optional(),
|
avatar_id: z.number().int().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysUser = z.infer<typeof sysUserSchema>
|
export type User = z.infer<typeof userSchema>
|
||||||
|
|
||||||
// 搜索参数
|
// 搜索参数
|
||||||
export const searchSysUserSchema = z.object({
|
export const searchUserSchema = z.object({
|
||||||
username: z.string().optional(),
|
username: z.string().optional(),
|
||||||
})
|
})
|
||||||
export type SearchSysUserParams = z.infer<typeof searchSysUserSchema>
|
export type SearchUserParams = z.infer<typeof searchUserSchema>
|
||||||
|
|
||||||
export const sysUserInfoSchema = sysUserSchema
|
export const userInfoSchema = userSchema
|
||||||
.pick({
|
.pick({
|
||||||
id: true,
|
id: true,
|
||||||
account: true,
|
account: true,
|
||||||
@@ -34,11 +34,11 @@ export const sysUserInfoSchema = sysUserSchema
|
|||||||
})
|
})
|
||||||
.extend({
|
.extend({
|
||||||
roles: z.array(z.string()),
|
roles: z.array(z.string()),
|
||||||
menus: z.array(sysMenuSchema),
|
menus: z.array(menuSchema),
|
||||||
permissions: z.array(z.string()),
|
permissions: z.array(z.string()),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysUserInfo = z.infer<typeof sysUserInfoSchema>
|
export type UserInfo = z.infer<typeof userInfoSchema>
|
||||||
|
|
||||||
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{5,16}$/
|
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{5,16}$/
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ export const changePasswordFormSchema = z
|
|||||||
|
|
||||||
export type ChangePasswordForm = z.infer<typeof changePasswordFormSchema>
|
export type ChangePasswordForm = z.infer<typeof changePasswordFormSchema>
|
||||||
|
|
||||||
export const sysUserFormSchema = z
|
export const userFormSchema = z
|
||||||
.object({
|
.object({
|
||||||
id: z.number().optional(),
|
id: z.number().optional(),
|
||||||
account: z
|
account: z
|
||||||
@@ -75,7 +75,7 @@ export const sysUserFormSchema = z
|
|||||||
message: '请选择状态',
|
message: '请选择状态',
|
||||||
}),
|
}),
|
||||||
confirmPassword: z.string().optional(),
|
confirmPassword: z.string().optional(),
|
||||||
avatar: z.array(sysFileFormSchema).optional(),
|
avatar: z.array(fileFormSchema).optional(),
|
||||||
})
|
})
|
||||||
.superRefine((data, ctx) => {
|
.superRefine((data, ctx) => {
|
||||||
if (!data.id) {
|
if (!data.id) {
|
||||||
@@ -100,6 +100,6 @@ export const sysUserFormSchema = z
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export type SysUserFormInput = z.input<typeof sysUserFormSchema>
|
export type UserFormInput = z.input<typeof userFormSchema>
|
||||||
|
|
||||||
export type SysUserFormOutput = z.output<typeof sysUserFormSchema>
|
export type UserFormOutput = z.output<typeof userFormSchema>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
|
|
||||||
import { getSysUserInfo, logout } from '@/api'
|
import { getUserInfo, logout } from '@/api'
|
||||||
import { getExpandedMenus, sleep } from '@/lib'
|
import { getExpandedMenus, sleep } from '@/lib'
|
||||||
import type { SysUserInfo } from '@/schemas'
|
import type { UserInfo } from '@/schemas'
|
||||||
|
|
||||||
const IS_LOGIN_KEY = 'is_login'
|
const IS_LOGIN_KEY = 'is_login'
|
||||||
|
|
||||||
interface Store {
|
interface Store {
|
||||||
token: string | null
|
token: string | null
|
||||||
tokenExp: string | null
|
tokenExp: string | null
|
||||||
userInfo: SysUserInfo | null
|
userInfo: UserInfo | null
|
||||||
expandedMenus: number[]
|
expandedMenus: number[]
|
||||||
isLogin: boolean
|
isLogin: boolean
|
||||||
setToken: (token: string | null, tokenExp?: string | null) => void
|
setToken: (token: string | null, tokenExp?: string | null) => void
|
||||||
@@ -66,7 +66,7 @@ export const useStore = create<Store>((set) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [{ data }] = await Promise.all([getSysUserInfo(), sleep(500)])
|
const [{ data }] = await Promise.all([getUserInfo(), sleep(500)])
|
||||||
set({
|
set({
|
||||||
userInfo: data,
|
userInfo: data,
|
||||||
expandedMenus: getExpandedMenus(data.menus),
|
expandedMenus: getExpandedMenus(data.menus),
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ export default defineConfig(({ mode }) => {
|
|||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
tsconfigPaths(),
|
tsconfigPaths(),
|
||||||
checker({
|
checker({
|
||||||
typescript: true,
|
typescript: {
|
||||||
|
tsconfigPath: './tsconfig.app.json',
|
||||||
|
},
|
||||||
eslint: {
|
eslint: {
|
||||||
lintCommand: 'eslint "./src/**/*.{ts,tsx}"',
|
lintCommand: 'eslint "./src/**/*.{ts,tsx}"',
|
||||||
useFlatConfig: true,
|
useFlatConfig: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user