diff --git a/kinit-admin/src/api/login/index.ts b/kinit-admin/src/api/login/index.ts index 8fc3240..3bdb23f 100644 --- a/kinit-admin/src/api/login/index.ts +++ b/kinit-admin/src/api/login/index.ts @@ -1,12 +1,8 @@ import request from '@/config/axios' import type { UserLoginType, UserType } from './types' -interface RoleParams { - roleName: string -} - export const loginApi = (data: UserLoginType): Promise => { - return request.post({ url: '/api/auth/login', data }) + return request.post({ url: '/auth/login/', data }) } export const loginOutApi = (): Promise => { @@ -20,12 +16,6 @@ export const getUserListApi = ({ params }: AxiosConfig) => { }>({ url: '/user/list', params }) } -export const getAdminRoleApi = ( - params: RoleParams -): Promise> => { - return request.get({ url: '/role/list', params }) -} - -export const getTestRoleApi = (params: RoleParams): Promise> => { - return request.get({ url: '/role/list', params }) +export const getRoleMenusApi = (): Promise> => { + return request.get({ url: '/auth/getMenuList/' }) } diff --git a/kinit-admin/src/api/login/types.ts b/kinit-admin/src/api/login/types.ts index 49aee22..ddacc9c 100644 --- a/kinit-admin/src/api/login/types.ts +++ b/kinit-admin/src/api/login/types.ts @@ -5,8 +5,8 @@ export type UserLoginType = { export type UserType = { telephone: string - password: string - role: string - roleId: string - permissions: string | string[] + nickname: string + id: number + avatar: string + name: string } diff --git a/kinit-admin/src/config/app.ts b/kinit-admin/src/config/app.ts index 4660d0c..ca7a7f4 100644 --- a/kinit-admin/src/config/app.ts +++ b/kinit-admin/src/config/app.ts @@ -48,7 +48,7 @@ export interface AppState { } export const appModules: AppState = { - userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突 + userInfo: 'UserInfo', // 登录信息保存的存储字段名称-建议每个项目换一个字段,避免与其他项目冲突,方便后面直接使用这个名称来获取对应的登录用户信息 sizeMap: ['default', 'large', 'small'], mobile: false, // 是否是移动端 title: import.meta.env.VITE_APP_TITLE, // 标题 diff --git a/kinit-admin/src/config/axios/config.ts b/kinit-admin/src/config/axios/config.ts index 1830335..749c5f2 100644 --- a/kinit-admin/src/config/axios/config.ts +++ b/kinit-admin/src/config/axios/config.ts @@ -14,22 +14,22 @@ const config: { */ base_url: { // 开发环境接口前缀 - base: '', + base: '/api', // 打包开发环境接口前缀 - dev: '', + dev: '/api', // 打包生产环境接口前缀 - pro: '', + pro: '/api', // 打包测试环境接口前缀 - test: '' + test: '/api' }, /** * 接口成功返回状态码 */ - result_code: '200', + result_code: 200, /** * 接口请求超时时间 diff --git a/kinit-admin/src/config/axios/service.ts b/kinit-admin/src/config/axios/service.ts index 7c28020..2e5777a 100644 --- a/kinit-admin/src/config/axios/service.ts +++ b/kinit-admin/src/config/axios/service.ts @@ -1,4 +1,5 @@ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios' +import { useAuthStoreWithOut } from '@/store/modules/auth' import qs from 'qs' @@ -10,18 +11,24 @@ const { result_code, base_url } = config export const PATH_URL = base_url[import.meta.env.VITE_API_BASEPATH] +const authStore = useAuthStoreWithOut() + // 创建axios实例 const service: AxiosInstance = axios.create({ baseURL: PATH_URL, // api 的 base_url - timeout: config.request_timeout // 请求超时时间 + timeout: config.request_timeout, // 请求超时时间 + headers: {} // 请求头信息 }) // request拦截器 service.interceptors.request.use( (config: AxiosRequestConfig) => { + if (authStore.token !== '') { + config.headers['Authorization'] = authStore.token // 让每个请求携带自定义token 请根据实际情况自行修改 + } if ( config.method === 'post' && - (config.headers as any)['Content-Type'] === 'application/x-www-form-urlencoded' + config.headers['Content-Type'] === 'application/x-www-form-urlencoded' ) { config.data = qs.stringify(config.data) } diff --git a/kinit-admin/src/store/modules/auth.ts b/kinit-admin/src/store/modules/auth.ts index 6151ffa..2e2d1a9 100644 --- a/kinit-admin/src/store/modules/auth.ts +++ b/kinit-admin/src/store/modules/auth.ts @@ -6,15 +6,13 @@ import { loginApi } from '@/api/login' export interface AuthState { token: string is_reset_password: boolean - user_id: number } export const useAuthStore = defineStore({ id: 'auth', state: (): AuthState => ({ token: '', - is_reset_password: false, - user_id: 0 + is_reset_password: false }), persist: { // 开启持久化存储 @@ -24,9 +22,6 @@ export const useAuthStore = defineStore({ getToken(): string { return this.token }, - getUserId(): number { - return this.user_id - }, getIsResetPassword(): boolean { return this.is_reset_password } @@ -35,13 +30,9 @@ export const useAuthStore = defineStore({ async login(formData: UserLoginType) { const res = await loginApi(formData) if (res) { - console.log('登录成功', res) - } else { - console.log('登录失败', res) + this.token = `${res.data.token_type} ${res.data.access_token}` + this.is_reset_password = res.data.is_reset_password } - // this.token = token - // this.is_reset_password = is_reset_password - // this.user_id = user_id return res } } diff --git a/kinit-admin/src/store/modules/permission.ts b/kinit-admin/src/store/modules/permission.ts index 822a2d4..5b5501c 100644 --- a/kinit-admin/src/store/modules/permission.ts +++ b/kinit-admin/src/store/modules/permission.ts @@ -38,22 +38,10 @@ export const usePermissionStore = defineStore({ } }, actions: { - generateRoutes( - type: 'admin' | 'test' | 'none', - routers?: AppCustomRouteRecordRaw[] | string[] - ): Promise { + generateRoutes(routers?: AppCustomRouteRecordRaw[]): Promise { return new Promise((resolve) => { let routerMap: AppRouteRecordRaw[] = [] - if (type === 'admin') { - // 模拟后端过滤菜单 - routerMap = generateRoutesFn2(routers as AppCustomRouteRecordRaw[]) - } else if (type === 'test') { - // 模拟前端过滤菜单 - routerMap = generateRoutesFn1(cloneDeep(asyncRouterMap), routers as string[]) - } else { - // 直接读取静态路由表 - routerMap = cloneDeep(asyncRouterMap) - } + routerMap = generateRoutesFn2(routers as AppCustomRouteRecordRaw[]) // 动态路由,404一定要放到最后面 this.addRouters = routerMap.concat([ { diff --git a/kinit-admin/src/views/Dashboard/Analysis.vue b/kinit-admin/src/views/Dashboard/Analysis.vue deleted file mode 100644 index 4ce6f27..0000000 --- a/kinit-admin/src/views/Dashboard/Analysis.vue +++ /dev/null @@ -1,127 +0,0 @@ - - - diff --git a/kinit-admin/src/views/Login/components/LoginForm.vue b/kinit-admin/src/views/Login/components/LoginForm.vue index b11e87d..842de19 100644 --- a/kinit-admin/src/views/Login/components/LoginForm.vue +++ b/kinit-admin/src/views/Login/components/LoginForm.vue @@ -4,14 +4,14 @@ import { Form } from '@/components/Form' import { useI18n } from '@/hooks/web/useI18n' import { ElButton, ElCheckbox, ElLink } from 'element-plus' import { useForm } from '@/hooks/web/useForm' -import { getTestRoleApi, getAdminRoleApi } from '@/api/login' +import { getRoleMenusApi } from '@/api/login' import { useCache } from '@/hooks/web/useCache' import { useAppStore } from '@/store/modules/app' import { useAuthStoreWithOut } from '@/store/modules/auth' import { usePermissionStore } from '@/store/modules/permission' import { useRouter } from 'vue-router' import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router' -import { UserType, UserLoginType } from '@/api/login/types' +import { UserLoginType } from '@/api/login/types' import { useValidator } from '@/hooks/web/useValidator' const { required } = useValidator() @@ -57,7 +57,7 @@ const schema = reactive([ { field: 'password', label: t('login.password'), - value: 'admin', + value: '430559', component: 'InputPassword', colProps: { span: 24 @@ -132,18 +132,10 @@ const signIn = async () => { const res = await authStore.login(formData) if (res) { - wsCache.set(appStore.getUserInfo, res.data) + // 存储用户信息 + wsCache.set(appStore.getUserInfo, res.data.user) // 是否使用动态路由 - if (appStore.getDynamicRouter) { - getRole() - } else { - await permissionStore.generateRoutes('none').catch(() => {}) - permissionStore.getAddRouters.forEach((route) => { - addRoute(route as RouteRecordRaw) // 动态添加可访问路由表 - }) - permissionStore.setIsAddRouters(true) - push({ path: redirect.value || permissionStore.addRouters[0].path }) - } + getMenu() } } finally { loading.value = false @@ -152,26 +144,15 @@ const signIn = async () => { }) } -// 获取角色信息 -const getRole = async () => { - const { getFormData } = methods - const formData = await getFormData() - const params = { - roleName: formData.telephone - } - // admin - 模拟后端过滤菜单 - // test - 模拟前端过滤菜单 - const res = - formData.telephone === 'admin' ? await getAdminRoleApi(params) : await getTestRoleApi(params) +// 获取用户菜单信息 +const getMenu = async () => { + const res = await getRoleMenusApi() + console.log('菜单信息', res) if (res) { const { wsCache } = useCache() const routers = res.data || [] wsCache.set('roleRouters', routers) - - formData.telephone === 'admin' - ? await permissionStore.generateRoutes('admin', routers).catch(() => {}) - : await permissionStore.generateRoutes('test', routers).catch(() => {}) - + await permissionStore.generateRoutes(routers).catch(() => {}) permissionStore.getAddRouters.forEach((route) => { addRoute(route as RouteRecordRaw) // 动态添加可访问路由表 }) diff --git a/kinit-api/alembic/versions/2d8939b3a228_update.py b/kinit-api/alembic/versions/2d8939b3a228_update.py new file mode 100644 index 0000000..5a11172 --- /dev/null +++ b/kinit-api/alembic/versions/2d8939b3a228_update.py @@ -0,0 +1,46 @@ +"""update + +Revision ID: 2d8939b3a228 +Revises: ecb50546debd +Create Date: 2022-09-22 16:34:00.663906 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '2d8939b3a228' +down_revision = 'ecb50546debd' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('vadmin_auth_menu', sa.Column('name', sa.String(length=50), nullable=False, comment='名称')) + op.add_column('vadmin_auth_menu', sa.Column('noCache', sa.Boolean(), nullable=True, comment='如果设置为true,则不会被 缓存(默认 false)')) + op.add_column('vadmin_auth_menu', sa.Column('breadcrumb', sa.Boolean(), nullable=True, comment='如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)')) + op.add_column('vadmin_auth_menu', sa.Column('affix', sa.Boolean(), nullable=True, comment='如果设置为true,则会一直固定在tag项中(默认 false)')) + op.add_column('vadmin_auth_menu', sa.Column('noTagsView', sa.Boolean(), nullable=True, comment='如果设置为true,则不会出现在tag中(默认 false)')) + op.add_column('vadmin_auth_menu', sa.Column('canTo', sa.Boolean(), nullable=True, comment='设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)')) + op.drop_index('ix_vadmin_auth_menu_title', table_name='vadmin_auth_menu') + op.create_index(op.f('ix_vadmin_auth_menu_name'), 'vadmin_auth_menu', ['name'], unique=False) + op.drop_column('vadmin_auth_menu', 'title_zh') + op.drop_column('vadmin_auth_menu', 'title') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('vadmin_auth_menu', sa.Column('title', mysql.VARCHAR(length=50), nullable=False, comment='名称')) + op.add_column('vadmin_auth_menu', sa.Column('title_zh', mysql.VARCHAR(length=50), nullable=True, comment='中文名称')) + op.drop_index(op.f('ix_vadmin_auth_menu_name'), table_name='vadmin_auth_menu') + op.create_index('ix_vadmin_auth_menu_title', 'vadmin_auth_menu', ['title'], unique=False) + op.drop_column('vadmin_auth_menu', 'canTo') + op.drop_column('vadmin_auth_menu', 'noTagsView') + op.drop_column('vadmin_auth_menu', 'affix') + op.drop_column('vadmin_auth_menu', 'breadcrumb') + op.drop_column('vadmin_auth_menu', 'noCache') + op.drop_column('vadmin_auth_menu', 'name') + # ### end Alembic commands ### diff --git a/kinit-api/alembic/versions/d37b76a689c1_update.py b/kinit-api/alembic/versions/d37b76a689c1_update.py new file mode 100644 index 0000000..7a0cc76 --- /dev/null +++ b/kinit-api/alembic/versions/d37b76a689c1_update.py @@ -0,0 +1,34 @@ +"""update + +Revision ID: d37b76a689c1 +Revises: 2d8939b3a228 +Create Date: 2022-09-22 16:35:48.607099 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'd37b76a689c1' +down_revision = '2d8939b3a228' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('vadmin_auth_menu', sa.Column('title', sa.String(length=50), nullable=False, comment='名称')) + op.drop_index('ix_vadmin_auth_menu_name', table_name='vadmin_auth_menu') + op.create_index(op.f('ix_vadmin_auth_menu_title'), 'vadmin_auth_menu', ['title'], unique=False) + op.drop_column('vadmin_auth_menu', 'name') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('vadmin_auth_menu', sa.Column('name', mysql.VARCHAR(length=50), nullable=False, comment='名称')) + op.drop_index(op.f('ix_vadmin_auth_menu_title'), table_name='vadmin_auth_menu') + op.create_index('ix_vadmin_auth_menu_name', 'vadmin_auth_menu', ['name'], unique=False) + op.drop_column('vadmin_auth_menu', 'title') + # ### end Alembic commands ### diff --git a/kinit-api/apps/vadmin/auth/crud.py b/kinit-api/apps/vadmin/auth/crud.py index 16fa007..65bc1c2 100644 --- a/kinit-api/apps/vadmin/auth/crud.py +++ b/kinit-api/apps/vadmin/auth/crud.py @@ -71,18 +71,13 @@ class MenuDal(DalBase): async def get_routers(self, user: models.VadminUser): """ 获取路由表 - export interface Menu { - name: string; // 菜单名 - icon?: string; // 菜单图标,如果没有,则会尝试使用route.meta.icon - path: string; // 菜单路径 - disabled?: boolean; // 是否禁用 - children?: Menu[]; // 子菜单 - tag: { // 菜单标签设置 - dot: boolean; // 为true则显示小圆点 - content: string'; // 内容 - type: 'error' | 'primary' | 'warn' | 'success'; // 类型 - }; - hideMenu?: boolean; // 是否隐藏菜单 + declare interface AppCustomRouteRecordRaw extends Omit { + name: string + meta: RouteMeta + component: string + path: string + redirect: string + children?: AppCustomRouteRecordRaw[] } """ if any([i.is_admin for i in user.roles]): @@ -94,6 +89,7 @@ class MenuDal(DalBase): for role in user.roles: role_obj = await RoleDal(self.db).get_data(role.id, options=[models.VadminRole.menus]) for menu in role_obj.menus: + # 该路由没有被禁用,并且菜单不是按钮 if not menu.disabled and menu.menu_type != "2": menus.add(menu) roots = filter(lambda i: not i.parent_id, menus) @@ -116,7 +112,7 @@ class MenuDal(DalBase): for root in nodes: router = schemas.RouterOut.from_orm(root) router.name = router.path.split("/")[-1].capitalize() - router.meta = schemas.Meta(title=root.title, icon=root.icon) + router.meta = schemas.Meta(title=root.title, icon=root.icon, hidden=root.hidden) if root.menu_type == "0": sons = filter(lambda i: i.parent_id == root.id, menus) router.children = self.generate_router_tree(menus, sons) diff --git a/kinit-api/apps/vadmin/auth/models/menu.py b/kinit-api/apps/vadmin/auth/models/menu.py index f947363..55ccfb6 100644 --- a/kinit-api/apps/vadmin/auth/models/menu.py +++ b/kinit-api/apps/vadmin/auth/models/menu.py @@ -23,7 +23,6 @@ class VadminMenu(BaseModel): # button = "2" title = Column(String(50), index=True, nullable=False, comment="名称") - title_zh = Column(String(50), comment="中文名称") # 选择框时使用 icon = Column(String(50), comment="菜单图标") redirect = Column(String(100), comment="重定向地址") component = Column(String(50), comment="前端组件地址") @@ -34,5 +33,10 @@ class VadminMenu(BaseModel): menu_type = Column(String(8), comment="菜单类型") parent_id = Column(ForeignKey("vadmin_auth_menu.id", ondelete='CASCADE'), comment="父菜单") perms = Column(String(50), comment="权限标识", unique=False, nullable=True, index=True) + noCache = Column(Boolean, comment="如果设置为true,则不会被 缓存(默认 false)", default=False) + breadcrumb = Column(Boolean, comment="如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)", default=True) + affix = Column(Boolean, comment="如果设置为true,则会一直固定在tag项中(默认 false)", default=False) + noTagsView = Column(Boolean, comment="如果设置为true,则不会出现在tag中(默认 false)", default=False) + canTo = Column(Boolean, comment="设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)", default=False) roles = relationship("VadminRole", back_populates='menus', secondary=vadmin_role_menus) diff --git a/kinit-api/apps/vadmin/auth/schemas/menu.py b/kinit-api/apps/vadmin/auth/schemas/menu.py index 3a5726e..2868d6b 100644 --- a/kinit-api/apps/vadmin/auth/schemas/menu.py +++ b/kinit-api/apps/vadmin/auth/schemas/menu.py @@ -40,6 +40,12 @@ class MenuSimpleOut(Menu): class Meta(BaseModel): title: str icon: Optional[str] = None + hidden: bool = False + noCache: Optional[bool] = True + breadcrumb: Optional[bool] = True + affix: Optional[bool] = False + noTagsView: Optional[bool] = False + canTo: Optional[bool] = False # 路由展示 @@ -48,10 +54,7 @@ class RouterOut(BaseModel): component: str path: str redirect: Optional[str] = None - perms: Optional[str] = None meta: Optional[Meta] = None - disabled: bool = False - hidden: bool = Field(False, alias='hideMenu') children: List['RouterOut'] = [] class Config: diff --git a/kinit-api/apps/vadmin/auth/utils/login.py b/kinit-api/apps/vadmin/auth/utils/login.py index e18d27b..84e49ca 100644 --- a/kinit-api/apps/vadmin/auth/utils/login.py +++ b/kinit-api/apps/vadmin/auth/utils/login.py @@ -25,8 +25,7 @@ from utils.response import SuccessResponse, ErrorResponse from application import settings from .auth_util import authenticate_user, create_access_token from apps.vadmin.record.models import VadminLoginRecord -from apps.vadmin.auth.crud import RoleDal, MenuDal -from apps.vadmin.auth.models import VadminRole +from apps.vadmin.auth.crud import MenuDal from .current import AdminAuth, full_admin app = APIRouter() @@ -53,7 +52,8 @@ async def login_for_access_token(request: Request, data: dict = Depends(authenti "telephone": user.telephone, "name": user.name, "nickname": user.nickname, - "avatar": user.avatar + "avatar": user.avatar, + "roles": [{"name": i.name, "value": i.role_key} for i in user.roles] } } await VadminLoginRecord.create_login_record(telephone=user.telephone, status=data.get("status"), request=request, @@ -61,19 +61,6 @@ async def login_for_access_token(request: Request, data: dict = Depends(authenti return SuccessResponse(result) -@app.get("/getUserInfo/", summary="获取当前登录用户基本信息") -async def get_user_info(auth: AdminAuth = Depends(full_admin)): - result = { - "id": auth.admin.id, - "telephone": auth.admin.telephone, - "name": auth.admin.name, - "nickname": auth.admin.nickname, - "avatar": auth.admin.avatar, - "roles": [{"name": i.name, "value": i.role_key} for i in auth.admin.roles] - } - return SuccessResponse(result) - - @app.get("/getMenuList/", summary="获取当前用户菜单树") async def get_menu_list(auth: AdminAuth = Depends(full_admin)): datas = await MenuDal(auth.db).get_routers(auth.admin)