版本升级:

1. 修复(kinit-api):utils/cache.py 日志模块导入问题修复
2. 修复(kinit-api):token解析失败会报错问题修复
3. 优化(kinit-api):用户登录认证失败返回值优化
4. 优化(kinit-api):获取redis方式统一改为redis_getter(request)
5. 优化(kini-api):文件IO修改为异步操作
6. 优化(kinit-api):关联创建人时将user_id改为create_user_id
7. 文档(kinit-api):kinit-api/README.md 加入查询数据文档
8. 修复(kinit-admin):用户无法导出问题修复
9. 优化(kinit-admin):角色新增与编辑框使用默认父子联动
10. 更新(kinit-admin):usePermissionStore 改为 useRouterStoreWithOut,因为此文件主要记录路由
11. 更新(kinit-admin):取消用户信息的持久化存储,改为仅保存在pinia store共享中,并添加roles,permissions信息
12. 修复(kinit-admin):菜单新增与编辑框,目录与菜单切换时会出现抖动问题修复
13. 优化(kinit-admin):src\hooks\web\useTable.ts 优化删除数据方法
14. 优化(kinit-admin):config/services.ts 新增返回403状态码时直接退出系统
15. 优化(kinit-admin):将store中的本文件使用store调用的,改为this
16. 更新(kinit-admin):路由拦截更新
17. 更新(kinit-api,kinit-admin,kinit-uni):取消接口地址最后面的 /
This commit is contained in:
ktianc 2023-05-14 23:21:00 +08:00
parent 71dc166516
commit c65ff06918
67 changed files with 605 additions and 459 deletions

View File

@ -7,17 +7,17 @@ import type {
} from './types' } from './types'
export const getBannersApi = (): Promise<IResponse<AnalysisBannersTypes[]>> => { export const getBannersApi = (): Promise<IResponse<AnalysisBannersTypes[]>> => {
return request.get({ url: '/vadmin/analysis/banners/' }) return request.get({ url: '/vadmin/analysis/banners' })
} }
export const getUserAccessSourceApi = (): Promise<IResponse<UserAccessSource[]>> => { export const getUserAccessSourceApi = (): Promise<IResponse<UserAccessSource[]>> => {
return request.get({ url: '/vadmin/analysis/user/access/source/' }) return request.get({ url: '/vadmin/analysis/user/access/source' })
} }
export const getWeeklyUserActivityApi = (): Promise<IResponse<WeeklyUserActivity[]>> => { export const getWeeklyUserActivityApi = (): Promise<IResponse<WeeklyUserActivity[]>> => {
return request.get({ url: '/vadmin/analysis/weekly/user/activity/' }) return request.get({ url: '/vadmin/analysis/weekly/user/activity' })
} }
export const getMonthlySalesApi = (): Promise<IResponse<MonthlySales[]>> => { export const getMonthlySalesApi = (): Promise<IResponse<MonthlySales[]>> => {
return request.get({ url: '/vadmin/analysis/monthly/sales/' }) return request.get({ url: '/vadmin/analysis/monthly/sales' })
} }

View File

@ -1,5 +1,5 @@
import request from '@/config/axios' import request from '@/config/axios'
export const getUserLoginDistributeApi = (): Promise<IResponse> => { export const getUserLoginDistributeApi = (): Promise<IResponse> => {
return request.get({ url: '/vadmin/record/analysis/user/login/distribute/' }) return request.get({ url: '/vadmin/record/analysis/user/login/distribute' })
} }

View File

@ -2,25 +2,25 @@ import request from '@/config/axios'
import type { WorkplaceTotal, Project, Dynamic, Team, RadarData, Shortcuts } from './types' import type { WorkplaceTotal, Project, Dynamic, Team, RadarData, Shortcuts } from './types'
export const getCountApi = (): Promise<IResponse<WorkplaceTotal>> => { export const getCountApi = (): Promise<IResponse<WorkplaceTotal>> => {
return request.get({ url: '/vadmin/workplace/total/' }) return request.get({ url: '/vadmin/workplace/total' })
} }
export const getProjectApi = (): Promise<IResponse<Project>> => { export const getProjectApi = (): Promise<IResponse<Project>> => {
return request.get({ url: '/vadmin/workplace/project/' }) return request.get({ url: '/vadmin/workplace/project' })
} }
export const getDynamicApi = (): Promise<IResponse<Dynamic[]>> => { export const getDynamicApi = (): Promise<IResponse<Dynamic[]>> => {
return request.get({ url: '/vadmin/workplace/dynamic/' }) return request.get({ url: '/vadmin/workplace/dynamic' })
} }
export const getTeamApi = (): Promise<IResponse<Team[]>> => { export const getTeamApi = (): Promise<IResponse<Team[]>> => {
return request.get({ url: '/vadmin/workplace/team/' }) return request.get({ url: '/vadmin/workplace/team' })
} }
export const getRadarApi = (): Promise<IResponse<RadarData[]>> => { export const getRadarApi = (): Promise<IResponse<RadarData[]>> => {
return request.get({ url: '/vadmin/workplace/radar/' }) return request.get({ url: '/vadmin/workplace/radar' })
} }
export const getShortcutsApi = (): Promise<IResponse<Shortcuts[]>> => { export const getShortcutsApi = (): Promise<IResponse<Shortcuts[]>> => {
return request.get({ url: '/vadmin/workplace/shortcuts/' }) return request.get({ url: '/vadmin/workplace/shortcuts' })
} }

View File

@ -2,13 +2,13 @@ import request from '@/config/axios'
import type { UserLoginType } from './types' import type { UserLoginType } from './types'
export const loginApi = (data: UserLoginType): Promise<IResponse> => { export const loginApi = (data: UserLoginType): Promise<IResponse> => {
return request.post({ url: '/auth/login/', data }) return request.post({ url: '/auth/login', data })
} }
export const getRoleMenusApi = (): Promise<IResponse<AppCustomRouteRecordRaw[]>> => { export const getRoleMenusApi = (): Promise<IResponse<AppCustomRouteRecordRaw[]>> => {
return request.get({ url: '/auth/getMenuList/' }) return request.get({ url: '/auth/getMenuList' })
} }
export const postSMSCodeApi = (params: any): Promise<IResponse> => { export const postSMSCodeApi = (params: any): Promise<IResponse> => {
return request.post({ url: '/vadmin/system/sms/send/', params }) return request.post({ url: '/vadmin/system/sms/send', params })
} }

View File

@ -2,25 +2,25 @@ import request from '@/config/axios'
import { List } from 'echarts' import { List } from 'echarts'
export const getMenuListApi = (params: any): Promise<IResponse> => { export const getMenuListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/auth/menus/', params }) return request.get({ url: '/vadmin/auth/menus', params })
} }
export const delMenuListApi = (data: List): Promise<IResponse> => { export const delMenuListApi = (data: List): Promise<IResponse> => {
return request.delete({ url: '/vadmin/auth/menus/', data }) return request.delete({ url: '/vadmin/auth/menus', data })
} }
export const addMenuListApi = (data: any): Promise<IResponse> => { export const addMenuListApi = (data: any): Promise<IResponse> => {
return request.post({ url: '/vadmin/auth/menus/', data }) return request.post({ url: '/vadmin/auth/menus', data })
} }
export const putMenuListApi = (data: any): Promise<IResponse> => { export const putMenuListApi = (data: any): Promise<IResponse> => {
return request.put({ url: `/vadmin/auth/menus/${data.id}/`, data }) return request.put({ url: `/vadmin/auth/menus/${data.id}`, data })
} }
export const getMenuTreeOptionsApi = (): Promise<IResponse> => { export const getMenuTreeOptionsApi = (): Promise<IResponse> => {
return request.get({ url: '/vadmin/auth/menus/tree/options/' }) return request.get({ url: '/vadmin/auth/menus/tree/options' })
} }
export const getMenuRoleTreeOptionsApi = (): Promise<IResponse> => { export const getMenuRoleTreeOptionsApi = (): Promise<IResponse> => {
return request.get({ url: '/vadmin/auth/menus/role/tree/options/' }) return request.get({ url: '/vadmin/auth/menus/role/tree/options' })
} }

View File

@ -1,25 +1,25 @@
import request from '@/config/axios' import request from '@/config/axios'
export const getRoleListApi = (params: any): Promise<IResponse> => { export const getRoleListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/auth/roles/', params }) return request.get({ url: '/vadmin/auth/roles', params })
} }
export const addRoleListApi = (data: any): Promise<IResponse> => { export const addRoleListApi = (data: any): Promise<IResponse> => {
return request.post({ url: '/vadmin/auth/roles/', data }) return request.post({ url: '/vadmin/auth/roles', data })
} }
export const delRoleListApi = (data: any): Promise<IResponse> => { export const delRoleListApi = (data: any): Promise<IResponse> => {
return request.delete({ url: '/vadmin/auth/roles/', data }) return request.delete({ url: '/vadmin/auth/roles', data })
} }
export const putRoleListApi = (data: any): Promise<IResponse> => { export const putRoleListApi = (data: any): Promise<IResponse> => {
return request.put({ url: `/vadmin/auth/roles/${data.id}/`, data }) return request.put({ url: `/vadmin/auth/roles/${data.id}`, data })
} }
export const getRoleApi = (dataId: number): Promise<IResponse> => { export const getRoleApi = (dataId: number): Promise<IResponse> => {
return request.get({ url: `/vadmin/auth/roles/${dataId}/` }) return request.get({ url: `/vadmin/auth/roles/${dataId}` })
} }
export const getRoleOptionsApi = (): Promise<IResponse> => { export const getRoleOptionsApi = (): Promise<IResponse> => {
return request.get({ url: `/vadmin/auth/roles/options/` }) return request.get({ url: `/vadmin/auth/roles/options` })
} }

View File

@ -1,57 +1,57 @@
import request from '@/config/axios' import request from '@/config/axios'
export const getUserListApi = (params: any): Promise<IResponse> => { export const getUserListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/auth/users/', params }) return request.get({ url: '/vadmin/auth/users', params })
} }
export const addUserListApi = (data: any): Promise<IResponse> => { export const addUserListApi = (data: any): Promise<IResponse> => {
return request.post({ url: '/vadmin/auth/users/', data }) return request.post({ url: '/vadmin/auth/users', data })
} }
export const delUserListApi = (data: any): Promise<IResponse> => { export const delUserListApi = (data: any): Promise<IResponse> => {
return request.delete({ url: '/vadmin/auth/users/', data }) return request.delete({ url: '/vadmin/auth/users', data })
} }
export const putUserListApi = (data: any): Promise<IResponse> => { export const putUserListApi = (data: any): Promise<IResponse> => {
return request.put({ url: `/vadmin/auth/users/${data.id}/`, data }) return request.put({ url: `/vadmin/auth/users/${data.id}`, data })
} }
export const getUserApi = (dataId: number): Promise<IResponse> => { export const getUserApi = (dataId: number): Promise<IResponse> => {
return request.get({ url: `/vadmin/auth/users/${dataId}/` }) return request.get({ url: `/vadmin/auth/users/${dataId}` })
} }
export const postCurrentUserResetPassword = (data: any): Promise<IResponse> => { export const postCurrentUserResetPassword = (data: any): Promise<IResponse> => {
return request.post({ url: `/vadmin/auth/user/current/reset/password/`, data }) return request.post({ url: `/vadmin/auth/user/current/reset/password`, data })
} }
export const postCurrentUserUpdateInfo = (data: any): Promise<IResponse> => { export const postCurrentUserUpdateInfo = (data: any): Promise<IResponse> => {
return request.post({ url: `/vadmin/auth/user/current/update/info/`, data }) return request.post({ url: `/vadmin/auth/user/current/update/info`, data })
} }
export const getCurrentAdminUserInfo = (): Promise<IResponse> => { export const getCurrentAdminUserInfo = (): Promise<IResponse> => {
return request.get({ url: `/vadmin/auth/user/admin/current/info/` }) return request.get({ url: `/vadmin/auth/user/admin/current/info` })
} }
export const postExportUserQueryListApi = (params: any, data: any): Promise<IResponse> => { export const postExportUserQueryListApi = (params: any, data: any): Promise<IResponse> => {
return request.post({ url: `/vadmin/auth/user/export/query/list/to/excel/`, params, data }) return request.post({ url: `/vadmin/auth/user/export/query/list/to/excel`, params, data })
} }
export const getImportTemplateApi = (): Promise<IResponse> => { export const getImportTemplateApi = (): Promise<IResponse> => {
return request.get({ url: `/vadmin/auth/user/download/import/template/` }) return request.get({ url: `/vadmin/auth/user/download/import/template` })
} }
export const postImportUserApi = (data: any): Promise<IResponse> => { export const postImportUserApi = (data: any): Promise<IResponse> => {
return request.post({ return request.post({
url: `/vadmin/auth/import/users/`, url: `/vadmin/auth/import/users`,
headersType: 'multipart/form-data', headersType: 'multipart/form-data',
data data
}) })
} }
export const postUsersInitPasswordSendSMSApi = (data: any): Promise<IResponse> => { export const postUsersInitPasswordSendSMSApi = (data: any): Promise<IResponse> => {
return request.post({ url: `/vadmin/auth/users/init/password/send/sms/`, data }) return request.post({ url: `/vadmin/auth/users/init/password/send/sms`, data })
} }
export const postUsersInitPasswordSendEmailApi = (data: any): Promise<IResponse> => { export const postUsersInitPasswordSendEmailApi = (data: any): Promise<IResponse> => {
return request.post({ url: `/vadmin/auth/users/init/password/send/email/`, data }) return request.post({ url: `/vadmin/auth/users/init/password/send/email`, data })
} }

View File

@ -2,46 +2,46 @@ import request from '@/config/axios'
// 常见问题类别 // 常见问题类别
export const getIssueCategoryListApi = (params: any): Promise<IResponse> => { export const getIssueCategoryListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/help/issue/categorys/', params }) return request.get({ url: '/vadmin/help/issue/categorys', params })
} }
export const addIssueCategoryApi = (data: any): Promise<IResponse> => { export const addIssueCategoryApi = (data: any): Promise<IResponse> => {
return request.post({ url: '/vadmin/help/issue/categorys/', data }) return request.post({ url: '/vadmin/help/issue/categorys', data })
} }
export const delIssueCategoryListApi = (data: any): Promise<IResponse> => { export const delIssueCategoryListApi = (data: any): Promise<IResponse> => {
return request.delete({ url: '/vadmin/help/issue/categorys/', data }) return request.delete({ url: '/vadmin/help/issue/categorys', data })
} }
export const putIssueCategoryApi = (data: any): Promise<IResponse> => { export const putIssueCategoryApi = (data: any): Promise<IResponse> => {
return request.put({ url: `/vadmin/help/issue/categorys/${data.id}/`, data }) return request.put({ url: `/vadmin/help/issue/categorys/${data.id}`, data })
} }
export const getIssueCategoryApi = (dataId: number): Promise<IResponse> => { export const getIssueCategoryApi = (dataId: number): Promise<IResponse> => {
return request.get({ url: `/vadmin/help/issue/categorys/${dataId}/` }) return request.get({ url: `/vadmin/help/issue/categorys/${dataId}` })
} }
export const getIssueCategoryOptionsApi = (): Promise<IResponse> => { export const getIssueCategoryOptionsApi = (): Promise<IResponse> => {
return request.get({ url: `/vadmin/help/issue/categorys/options/` }) return request.get({ url: `/vadmin/help/issue/categorys/options` })
} }
// 常见问题 // 常见问题
export const getIssueListApi = (params: any): Promise<IResponse> => { export const getIssueListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/help/issues/', params }) return request.get({ url: '/vadmin/help/issues', params })
} }
export const addIssueApi = (data: any): Promise<IResponse> => { export const addIssueApi = (data: any): Promise<IResponse> => {
return request.post({ url: '/vadmin/help/issues/', data }) return request.post({ url: '/vadmin/help/issues', data })
} }
export const delIssueListApi = (data: any): Promise<IResponse> => { export const delIssueListApi = (data: any): Promise<IResponse> => {
return request.delete({ url: '/vadmin/help/issues/', data }) return request.delete({ url: '/vadmin/help/issues', data })
} }
export const putIssueApi = (data: any): Promise<IResponse> => { export const putIssueApi = (data: any): Promise<IResponse> => {
return request.put({ url: `/vadmin/help/issues/${data.id}/`, data }) return request.put({ url: `/vadmin/help/issues/${data.id}`, data })
} }
export const getIssueApi = (dataId: number): Promise<IResponse> => { export const getIssueApi = (dataId: number): Promise<IResponse> => {
return request.get({ url: `/vadmin/help/issues/${dataId}/` }) return request.get({ url: `/vadmin/help/issues/${dataId}` })
} }

View File

@ -1,49 +1,49 @@
import request from '@/config/axios' import request from '@/config/axios'
export const getDictTypeListApi = (params: any): Promise<IResponse> => { export const getDictTypeListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/system/dict/types/', params }) return request.get({ url: '/vadmin/system/dict/types', params })
} }
export const addDictTypeListApi = (data: any): Promise<IResponse> => { export const addDictTypeListApi = (data: any): Promise<IResponse> => {
return request.post({ url: '/vadmin/system/dict/types/', data }) return request.post({ url: '/vadmin/system/dict/types', data })
} }
export const delDictTypeListApi = (data: any): Promise<IResponse> => { export const delDictTypeListApi = (data: any): Promise<IResponse> => {
return request.delete({ url: '/vadmin/system/dict/types/', data }) return request.delete({ url: '/vadmin/system/dict/types', data })
} }
export const putDictTypeListApi = (data: any): Promise<IResponse> => { export const putDictTypeListApi = (data: any): Promise<IResponse> => {
return request.put({ url: `/vadmin/system/dict/types/${data.id}/`, data }) return request.put({ url: `/vadmin/system/dict/types/${data.id}`, data })
} }
export const getDictTypeApi = (dataId: number): Promise<IResponse> => { export const getDictTypeApi = (dataId: number): Promise<IResponse> => {
return request.get({ url: `/vadmin/system/dict/types/${dataId}/` }) return request.get({ url: `/vadmin/system/dict/types/${dataId}` })
} }
export const getDictTypeOptionsApi = (): Promise<IResponse> => { export const getDictTypeOptionsApi = (): Promise<IResponse> => {
return request.get({ url: `/vadmin/system/dict/types/options/` }) return request.get({ url: `/vadmin/system/dict/types/options` })
} }
export const getDictTypeDetailsApi = (data: any): Promise<IResponse> => { export const getDictTypeDetailsApi = (data: any): Promise<IResponse> => {
return request.post({ url: `/vadmin/system/dict/types/details/`, data }) return request.post({ url: `/vadmin/system/dict/types/details`, data })
} }
export const getDictDetailsListApi = (params: any): Promise<IResponse> => { export const getDictDetailsListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/system/dict/details/', params }) return request.get({ url: '/vadmin/system/dict/details', params })
} }
export const addDictDetailsListApi = (data: any): Promise<IResponse> => { export const addDictDetailsListApi = (data: any): Promise<IResponse> => {
return request.post({ url: '/vadmin/system/dict/details/', data }) return request.post({ url: '/vadmin/system/dict/details', data })
} }
export const delDictDetailsListApi = (data: any): Promise<IResponse> => { export const delDictDetailsListApi = (data: any): Promise<IResponse> => {
return request.delete({ url: '/vadmin/system/dict/details/', data }) return request.delete({ url: '/vadmin/system/dict/details', data })
} }
export const putDictDetailsListApi = (data: any): Promise<IResponse> => { export const putDictDetailsListApi = (data: any): Promise<IResponse> => {
return request.put({ url: `/vadmin/system/dict/details/${data.id}/`, data }) return request.put({ url: `/vadmin/system/dict/details/${data.id}`, data })
} }
export const getDictDetailsApi = (dataId: number): Promise<IResponse> => { export const getDictDetailsApi = (dataId: number): Promise<IResponse> => {
return request.get({ url: `/vadmin/system/dict/details/${dataId}/` }) return request.get({ url: `/vadmin/system/dict/details/${dataId}` })
} }

View File

@ -2,7 +2,7 @@ import request from '@/config/axios'
export const addFilesListApi = (data: any): Promise<IResponse> => { export const addFilesListApi = (data: any): Promise<IResponse> => {
return request.post({ return request.post({
url: `/vadmin/system/files/`, url: `/vadmin/system/files`,
headersType: 'multipart/form-data', headersType: 'multipart/form-data',
data data
}) })

View File

@ -1,5 +1,5 @@
import request from '@/config/axios' import request from '@/config/axios'
export const getRecordLoginListApi = (params: any): Promise<IResponse> => { export const getRecordLoginListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/record/logins/', params }) return request.get({ url: '/vadmin/record/logins', params })
} }

View File

@ -1,5 +1,5 @@
import request from '@/config/axios' import request from '@/config/axios'
export const getRecordOperationListApi = (params: any): Promise<IResponse> => { export const getRecordOperationListApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/record/operations/', params }) return request.get({ url: '/vadmin/record/operations', params })
} }

View File

@ -1,28 +1,28 @@
import request from '@/config/axios' import request from '@/config/axios'
export const getSystemSettingsTabsApi = (params: any): Promise<IResponse> => { export const getSystemSettingsTabsApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/system/settings/tabs/', params }) return request.get({ url: '/vadmin/system/settings/tabs', params })
} }
export const getSystemSettingsApi = (params: any): Promise<IResponse> => { export const getSystemSettingsApi = (params: any): Promise<IResponse> => {
return request.get({ url: '/vadmin/system/settings/tabs/values/', params }) return request.get({ url: '/vadmin/system/settings/tabs/values', params })
} }
export const putSystemSettingsApi = (data: any): Promise<IResponse> => { export const putSystemSettingsApi = (data: any): Promise<IResponse> => {
return request.put({ url: '/vadmin/system/settings/tabs/values/', data }) return request.put({ url: '/vadmin/system/settings/tabs/values', data })
} }
// 获取系统基础配置,每次进入系统时使用 // 获取系统基础配置,每次进入系统时使用
export const getSystemBaseConfigApi = (): Promise<IResponse> => { export const getSystemBaseConfigApi = (): Promise<IResponse> => {
return request.get({ url: '/vadmin/system/settings/base/config/' }) return request.get({ url: '/vadmin/system/settings/base/config' })
} }
// 获取系统隐私协议 // 获取系统隐私协议
export const getSystemPrivacyApi = (): Promise<IResponse> => { export const getSystemPrivacyApi = (): Promise<IResponse> => {
return request.get({ url: '/vadmin/system/settings/privacy/' }) return request.get({ url: '/vadmin/system/settings/privacy' })
} }
// 获取系统用户协议 // 获取系统用户协议
export const getSystemAgreementApi = (): Promise<IResponse> => { export const getSystemAgreementApi = (): Promise<IResponse> => {
return request.get({ url: '/vadmin/system/settings/agreement/' }) return request.get({ url: '/vadmin/system/settings/agreement' })
} }

View File

@ -2,7 +2,7 @@
import { ElBreadcrumb, ElBreadcrumbItem } from 'element-plus' import { ElBreadcrumb, ElBreadcrumbItem } from 'element-plus'
import { ref, watch, computed, unref, defineComponent, TransitionGroup } from 'vue' import { ref, watch, computed, unref, defineComponent, TransitionGroup } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { usePermissionStore } from '@/store/modules/permission' import { useRouterStore } from '@/store/modules/router'
import { filterBreadcrumb } from './helper' import { filterBreadcrumb } from './helper'
import { filter, treeToList } from '@/utils/tree' import { filter, treeToList } from '@/utils/tree'
import type { RouteLocationNormalizedLoaded, RouteMeta } from 'vue-router' import type { RouteLocationNormalizedLoaded, RouteMeta } from 'vue-router'
@ -29,10 +29,10 @@ export default defineComponent({
const levelList = ref<AppRouteRecordRaw[]>([]) const levelList = ref<AppRouteRecordRaw[]>([])
const permissionStore = usePermissionStore() const routerStore = useRouterStore()
const menuRouters = computed(() => { const menuRouters = computed(() => {
const routers = permissionStore.getRouters const routers = routerStore.getRouters
return filterBreadcrumb(routers) return filterBreadcrumb(routers)
}) })

View File

@ -2,7 +2,7 @@
import { computed, defineComponent, unref, PropType } from 'vue' import { computed, defineComponent, unref, PropType } from 'vue'
import { ElMenu, ElScrollbar } from 'element-plus' import { ElMenu, ElScrollbar } from 'element-plus'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { usePermissionStore } from '@/store/modules/permission' import { useRouterStore } from '@/store/modules/router'
import { useRenderMenuItem } from './components/useRenderMenuItem' import { useRenderMenuItem } from './components/useRenderMenuItem'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { isUrl } from '@/utils/is' import { isUrl } from '@/utils/is'
@ -28,7 +28,7 @@ export default defineComponent({
const { push, currentRoute } = useRouter() const { push, currentRoute } = useRouter()
const permissionStore = usePermissionStore() const routerStore = useRouterStore()
const menuMode = computed((): 'vertical' | 'horizontal' => { const menuMode = computed((): 'vertical' | 'horizontal' => {
// //
@ -42,7 +42,7 @@ export default defineComponent({
}) })
const routers = computed(() => const routers = computed(() =>
unref(layout) === 'cutMenu' ? permissionStore.getMenuTabRouters : permissionStore.getRouters unref(layout) === 'cutMenu' ? routerStore.getMenuTabRouters : routerStore.getRouters
) )
const collapse = computed(() => appStore.getCollapse) const collapse = computed(() => appStore.getCollapse)

View File

@ -1,5 +1,5 @@
<script lang="tsx"> <script lang="tsx">
import { usePermissionStore } from '@/store/modules/permission' import { useRouterStore } from '@/store/modules/router'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { computed, unref, defineComponent, watch, ref, onMounted } from 'vue' import { computed, unref, defineComponent, watch, ref, onMounted } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -30,9 +30,9 @@ export default defineComponent({
const fixedMenu = computed(() => appStore.getFixedMenu) const fixedMenu = computed(() => appStore.getFixedMenu)
const permissionStore = usePermissionStore() const routerStore = useRouterStore()
const routers = computed(() => permissionStore.getRouters) const routers = computed(() => routerStore.getRouters)
const tabRouters = computed(() => unref(routers).filter((v) => !v?.meta?.hidden)) const tabRouters = computed(() => unref(routers).filter((v) => !v?.meta?.hidden))
@ -51,7 +51,7 @@ export default defineComponent({
tabActive.value = path tabActive.value = path
if (children) { if (children) {
permissionStore.setMenuTabRouters( routerStore.setMenuTabRouters(
cloneDeep(children).map((v) => { cloneDeep(children).map((v) => {
v.path = pathResolve(unref(tabActive), v.path) v.path = pathResolve(unref(tabActive), v.path)
return v return v
@ -108,7 +108,7 @@ export default defineComponent({
showMenu.value = unref(fixedMenu) ? true : !unref(showMenu) showMenu.value = unref(fixedMenu) ? true : !unref(showMenu)
} }
if (unref(showMenu)) { if (unref(showMenu)) {
permissionStore.setMenuTabRouters( routerStore.setMenuTabRouters(
cloneDeep(item.children).map((v) => { cloneDeep(item.children).map((v) => {
v.path = pathResolve(unref(tabActive), v.path) v.path = pathResolve(unref(tabActive), v.path)
return v return v
@ -117,7 +117,7 @@ export default defineComponent({
} }
} else { } else {
push(item.path) push(item.path)
permissionStore.setMenuTabRouters([]) routerStore.setMenuTabRouters([])
showMenu.value = false showMenu.value = false
} }
} }

View File

@ -2,7 +2,7 @@
import { onMounted, watch, computed, unref, ref, nextTick } from 'vue' import { onMounted, watch, computed, unref, ref, nextTick } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router' import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router'
import { usePermissionStore } from '@/store/modules/permission' import { useRouterStore } from '@/store/modules/router'
import { useTagsViewStore } from '@/store/modules/tagsView' import { useTagsViewStore } from '@/store/modules/tagsView'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -21,9 +21,9 @@ const { t } = useI18n()
const { currentRoute, push, replace } = useRouter() const { currentRoute, push, replace } = useRouter()
const permissionStore = usePermissionStore() const routerStore = useRouterStore()
const routers = computed(() => permissionStore.getRouters) const routers = computed(() => routerStore.getRouters)
const tagsViewStore = useTagsViewStore() const tagsViewStore = useTagsViewStore()
@ -108,14 +108,14 @@ const toLastView = () => {
push(latestView) push(latestView)
} else { } else {
if ( if (
unref(currentRoute).path === permissionStore.getAddRouters[0].path || unref(currentRoute).path === routerStore.getAddRouters[0].path ||
unref(currentRoute).path === permissionStore.getAddRouters[0].redirect unref(currentRoute).path === routerStore.getAddRouters[0].redirect
) { ) {
addTags() addTags()
return return
} }
// You can set another route // You can set another route
push(permissionStore.getAddRouters[0].path) push(routerStore.getAddRouters[0].path)
} }
} }

View File

@ -105,6 +105,7 @@ service.interceptors.response.use(
}, },
(error: AxiosError) => { (error: AxiosError) => {
let { message } = error let { message } = error
const authStore = useAuthStore()
const status = error.response?.status const status = error.response?.status
switch (status) { switch (status) {
case 400: case 400:
@ -112,12 +113,13 @@ service.interceptors.response.use(
break break
case 401: case 401:
// 强制要求重新登录因账号已冻结账号已过期手机号码错误刷新token无效等问题导致 // 强制要求重新登录因账号已冻结账号已过期手机号码错误刷新token无效等问题导致
const authStore = useAuthStore()
authStore.logout() authStore.logout()
message = '认证已过期,请重新登录' message = '认证已过期,请重新登录'
break break
case 403: case 403:
message = '拒绝访问' // 强制要求重新登录,因无系统权限,而进入到系统访问等问题导致
authStore.logout()
message = '无权限访问,请联系管理员'
break break
case 404: case 404:
message = `请求地址出错: ${error.response?.config.url}` message = `请求地址出错: ${error.response?.config.url}`

View File

@ -1,18 +1,16 @@
import type { App, Directive, DirectiveBinding } from 'vue' import type { App, Directive, DirectiveBinding } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useCache } from '@/hooks/web/useCache'
import { intersection } from 'lodash-es' import { intersection } from 'lodash-es'
import { isArray } from '@/utils/is' import { isArray } from '@/utils/is'
import { useAppStoreWithOut } from '@/store/modules/app' import { useAuthStoreWithOut } from '@/store/modules/auth'
const { t } = useI18n() const { t } = useI18n()
const { wsCache } = useCache() const authStore = useAuthStoreWithOut()
const appStore = useAppStoreWithOut()
// 全部权限 // 全部权限
const all_permission = ['*.*.*'] const all_permission = ['*.*.*']
const hasPermission = (value: string | string[]): boolean => { const hasPermission = (value: string | string[]): boolean => {
const permissions = wsCache.get(appStore.getUserInfo).permissions as string[] const permissions = authStore.getPermissions
if (!value) { if (!value) {
throw new Error(t('permission.hasPermission')) throw new Error(t('permission.hasPermission'))
} }

View File

@ -5,6 +5,7 @@ import { get } from 'lodash-es'
import type { TableProps } from '@/components/Table/src/types' import type { TableProps } from '@/components/Table/src/types'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { TableSetPropsType } from '@/types/table' import { TableSetPropsType } from '@/types/table'
import { isEmpty } from '@/utils/is'
const { t } = useI18n() const { t } = useI18n()
@ -34,7 +35,7 @@ interface TableObject<T = any> {
tableData: T[] tableData: T[]
params: any params: any
loading: boolean loading: boolean
currentRow: Nullable<T> currentRow: Recordable | null
} }
type TableOrderChange = { type TableOrderChange = {
@ -111,7 +112,7 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
return table return table
} }
const delData = async (ids: string[] | number[]) => { const delData = async (ids: string[] | number[] | number) => {
if (config?.delListApi) { if (config?.delListApi) {
const res = await config.delListApi(ids) const res = await config.delListApi(ids)
if (res) { if (res) {
@ -166,29 +167,47 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
methods.getList() methods.getList()
}, },
// 删除数据 // 删除数据
delListApi: async (ids: string[] | number[], multiple: boolean, message = true) => { // 如果存在 ids则直接使用 ids 中的值进行删除
// 如果不存在 ids则判断 multiple 的值来进行删除
// 如果 multiple 为 true则说明是多选框获取多选框中的数据删除
// 如果为 false则说明是点击按钮则获取当前选择行数据进行删除
delListApi: async (
multiple: boolean,
ids: string[] | number[] | number = [],
message = true
) => {
const tableRef = await getTable() const tableRef = await getTable()
let value: string[] | number[] | number = []
if (isEmpty(ids)) {
if (multiple) { if (multiple) {
if (!tableRef?.selections.length) { if (!tableRef?.selections.length) {
ElMessage.warning(t('common.delNoData')) ElMessage.warning(t('common.delNoData'))
return return
} else {
value = tableRef?.selections.map((item) => item.id)
} }
} else { } else {
if (!tableObject.currentRow) { if (!tableObject.currentRow) {
ElMessage.warning(t('common.delNoData')) ElMessage.warning(t('common.delNoData'))
return return
} else {
value = tableObject.currentRow.id
} }
} }
} else {
value = ids
}
if (message) { if (message) {
ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), { ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), {
confirmButtonText: t('common.delOk'), confirmButtonText: t('common.delOk'),
cancelButtonText: t('common.delCancel'), cancelButtonText: t('common.delCancel'),
type: 'warning' type: 'warning'
}).then(async () => { }).then(async () => {
await delData(ids) await delData(value)
}) })
} else { } else {
await delData(ids) await delData(value)
} }
}, },
// 导出筛选列表 // 导出筛选列表

View File

@ -4,12 +4,12 @@ import { useCache } from '@/hooks/web/useCache'
import type { RouteRecordRaw } from 'vue-router' import type { RouteRecordRaw } from 'vue-router'
import { useTitle } from '@/hooks/web/useTitle' import { useTitle } from '@/hooks/web/useTitle'
import { useNProgress } from '@/hooks/web/useNProgress' import { useNProgress } from '@/hooks/web/useNProgress'
import { usePermissionStoreWithOut } from '@/store/modules/permission' import { useRouterStoreWithOut } from '@/store/modules/router'
import { usePageLoading } from '@/hooks/web/usePageLoading' import { usePageLoading } from '@/hooks/web/usePageLoading'
import { getRoleMenusApi } from '@/api/login' import { getRoleMenusApi } from '@/api/login'
import { useAuthStoreWithOut } from '@/store/modules/auth' import { useAuthStoreWithOut } from '@/store/modules/auth'
const permissionStore = usePermissionStoreWithOut() const Routertore = useRouterStoreWithOut()
const appStore = useAppStoreWithOut() const appStore = useAppStoreWithOut()
const authStore = useAuthStoreWithOut() const authStore = useAuthStoreWithOut()
@ -25,16 +25,16 @@ const whiteList = ['/login', '/docs/privacy', '/docs/agreement'] // 不重定向
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
start() start()
loadStart() loadStart()
if (wsCache.get(appStore.getUserInfo)) { if (wsCache.get(appStore.getToken)) {
if (to.path === '/login') { if (to.path === '/login') {
next({ path: '/' }) next({ path: '/' })
} else if (to.path === '/reset/password') { } else if (to.path === '/reset/password') {
next() next()
} else { } else {
if (!authStore.getIsUser) { if (!authStore.getIsUser) {
await authStore.getUserInfo() await authStore.setUserInfo()
} }
if (permissionStore.getIsAddRouters) { if (Routertore.getIsAddRouters) {
next() next()
return return
} }
@ -44,14 +44,14 @@ router.beforeEach(async (to, from, next) => {
const { wsCache } = useCache() const { wsCache } = useCache()
const routers = res.data || [] const routers = res.data || []
wsCache.set('roleRouters', routers) wsCache.set('roleRouters', routers)
await permissionStore.generateRoutes(routers).catch(() => {}) await Routertore.generateRoutes(routers).catch(() => {})
permissionStore.getAddRouters.forEach((route) => { Routertore.getAddRouters.forEach((route) => {
router.addRoute(route as RouteRecordRaw) // 动态添加可访问路由表 router.addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
}) })
const redirectPath = from.query.redirect || to.path const redirectPath = from.query.redirect || to.path
const redirect = decodeURIComponent(redirectPath as string) const redirect = decodeURIComponent(redirectPath as string)
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect } const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect }
permissionStore.setIsAddRouters(true) Routertore.setIsAddRouters(true)
next(nextData) next(nextData)
} }
} else { } else {

View File

@ -27,7 +27,6 @@ interface AppState {
pageLoading: boolean pageLoading: boolean
layout: LayoutType layout: LayoutType
title: string title: string
userInfo: string
isDark: boolean isDark: boolean
currentSize: ElementPlusSize currentSize: ElementPlusSize
sizeMap: ElementPlusSize[] sizeMap: ElementPlusSize[]
@ -45,7 +44,6 @@ interface AppState {
export const useAppStore = defineStore('app', { export const useAppStore = defineStore('app', {
state: (): AppState => { state: (): AppState => {
return { return {
userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突
token: 'Token', // 存储Token字段 token: 'Token', // 存储Token字段
refreshToken: 'RefreshToken', // 存储刷新Token字段 refreshToken: 'RefreshToken', // 存储刷新Token字段
sizeMap: ['default', 'large', 'small'], sizeMap: ['default', 'large', 'small'],
@ -166,9 +164,6 @@ export const useAppStore = defineStore('app', {
getTitle(): string { getTitle(): string {
return this.title return this.title
}, },
getUserInfo(): string {
return this.userInfo
},
getToken(): string { getToken(): string {
return this.token return this.token
}, },

View File

@ -8,12 +8,14 @@ import { getCurrentAdminUserInfo } from '@/api/vadmin/auth/user'
import { resetRouter } from '@/router' import { resetRouter } from '@/router'
import { useTagsViewStore } from '@/store/modules/tagsView' import { useTagsViewStore } from '@/store/modules/tagsView'
import router from '@/router' import router from '@/router'
import { ElMessage } from 'element-plus'
const { wsCache } = useCache() const { wsCache } = useCache()
export interface UserState { export interface UserState {
id?: number id?: number
telephone?: string telephone?: string
email?: string
name?: string name?: string
nickname?: string nickname?: string
avatar?: string avatar?: string
@ -23,14 +25,18 @@ export interface UserState {
} }
export interface AuthState { export interface AuthState {
user: UserState user: UserState // 当前用户基本信息
isUser: boolean isUser: boolean // 是否已经登录并获取到用户信息
roles: string[] // 当前用户角色 role_key 列表
permissions: string[] // 当前用户权限列表
} }
export const useAuthStore = defineStore('auth', { export const useAuthStore = defineStore('auth', {
state: (): AuthState => { state: (): AuthState => {
return { return {
user: {}, user: {},
roles: [],
permissions: [],
isUser: false isUser: false
} }
}, },
@ -38,6 +44,12 @@ export const useAuthStore = defineStore('auth', {
getUser(): UserState { getUser(): UserState {
return this.user return this.user
}, },
getRoles(): string[] {
return this.roles
},
getPermissions(): string[] {
return this.permissions
},
getIsUser(): boolean { getIsUser(): boolean {
return this.isUser return this.isUser
} }
@ -50,35 +62,45 @@ export const useAuthStore = defineStore('auth', {
const appStore = useAppStore() const appStore = useAppStore()
wsCache.set(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`) wsCache.set(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`)
wsCache.set(appStore.getRefreshToken, res.data.refresh_token) wsCache.set(appStore.getRefreshToken, res.data.refresh_token)
// 存储用户信息 // 获取当前登录用户的信息
const auth = useAuthStore() await this.setUserInfo()
await auth.getUserInfo()
} }
return res return res
}, },
logout() { logout(message?: string) {
wsCache.clear() wsCache.clear()
this.user = {} this.user = {}
this.roles = []
this.permissions = []
this.isUser = false this.isUser = false
const tagsViewStore = useTagsViewStore() const tagsViewStore = useTagsViewStore()
tagsViewStore.delAllViews() tagsViewStore.delAllViews()
resetRouter() resetRouter()
router.push('/login') router.push('/login')
if (message) {
ElMessage.error(message)
}
}, },
// 这里更新用户是自己在个人中心更新自己的用户信息,不包括在用户列表中更新的,所以不包含权限角色等信息
// 用户信息取消使用持久化存储,仅使用共享存储
updateUser(data: UserState) { updateUser(data: UserState) {
this.user.gender = data.gender this.user.gender = data.gender
this.user.name = data.name this.user.name = data.name
this.user.nickname = data.nickname this.user.nickname = data.nickname
this.user.telephone = data.telephone this.user.telephone = data.telephone
const appStore = useAppStore()
wsCache.set(appStore.getUserInfo, this.user)
}, },
async getUserInfo() { // 获取用户详细信息,包括角色,权限
// 用户信息取消使用持久化存储,仅使用共享存储
async setUserInfo() {
const res = await getCurrentAdminUserInfo() const res = await getCurrentAdminUserInfo()
const appStore = useAppStore()
wsCache.set(appStore.getUserInfo, res.data)
this.isUser = true this.isUser = true
this.user = res.data this.user = res.data
this.roles = res.data.roles.map((item) => {
if (!item.disabled) {
return item.role_key
}
})
this.permissions = res.data.permissions
} }
} }
}) })

View File

@ -4,15 +4,15 @@ import { generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper'
import { store } from '../index' import { store } from '../index'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
export interface PermissionState { export interface RouterState {
routers: AppRouteRecordRaw[] routers: AppRouteRecordRaw[]
addRouters: AppRouteRecordRaw[] addRouters: AppRouteRecordRaw[]
isAddRouters: boolean isAddRouters: boolean
menuTabRouters: AppRouteRecordRaw[] menuTabRouters: AppRouteRecordRaw[]
} }
export const usePermissionStore = defineStore('permission', { export const useRouterStore = defineStore('router', {
state: (): PermissionState => ({ state: (): RouterState => ({
routers: [], routers: [],
addRouters: [], addRouters: [],
isAddRouters: false, isAddRouters: false,
@ -63,6 +63,6 @@ export const usePermissionStore = defineStore('permission', {
} }
}) })
export const usePermissionStoreWithOut = () => { export const useRouterStoreWithOut = () => {
return usePermissionStore(store) return useRouterStore(store)
} }

View File

@ -2,7 +2,7 @@
import { useTimeAgo } from '@/hooks/web/useTimeAgo' import { useTimeAgo } from '@/hooks/web/useTimeAgo'
import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus' import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { ref, reactive } from 'vue' import { ref, reactive, computed } from 'vue'
import { CountTo } from '@/components/CountTo' import { CountTo } from '@/components/CountTo'
import { formatTime } from '@/utils' import { formatTime } from '@/utils'
import { Highlight } from '@/components/Highlight' import { Highlight } from '@/components/Highlight'
@ -20,11 +20,10 @@ import type {
Team, Team,
Shortcuts Shortcuts
} from '@/api/dashboard/workplace/types' } from '@/api/dashboard/workplace/types'
import { useCache } from '@/hooks/web/useCache'
import { useAppStoreWithOut } from '@/store/modules/app'
import avatar from '@/assets/imgs/avatar.jpg' import avatar from '@/assets/imgs/avatar.jpg'
import { useAuthStoreWithOut } from '@/store/modules/auth'
const { wsCache } = useCache() const authStore = useAuthStoreWithOut()
const loading = ref(true) const loading = ref(true)
@ -97,9 +96,7 @@ getAllApi()
const { t } = useI18n() const { t } = useI18n()
const appStore = useAppStoreWithOut() const user = computed(() => authStore.getUser)
const user = wsCache.get(appStore.getUserInfo)
</script> </script>
<template> <template>

View File

@ -6,7 +6,7 @@ import { ElButton, ElCheckbox, ElLink } from 'element-plus'
import { useForm } from '@/hooks/web/useForm' import { useForm } from '@/hooks/web/useForm'
import { getRoleMenusApi } from '@/api/login' import { getRoleMenusApi } from '@/api/login'
import { useAuthStoreWithOut } from '@/store/modules/auth' import { useAuthStoreWithOut } from '@/store/modules/auth'
import { usePermissionStore } from '@/store/modules/permission' import { useRouterStore } from '@/store/modules/router'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router' import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
import { UserLoginType } from '@/api/login/types' import { UserLoginType } from '@/api/login/types'
@ -18,7 +18,7 @@ const emit = defineEmits(['to-telephone-code'])
const { required } = useValidator() const { required } = useValidator()
const permissionStore = usePermissionStore() const routerStore = useRouterStore()
const { currentRoute, addRoute, push } = useRouter() const { currentRoute, addRoute, push } = useRouter()
@ -157,12 +157,12 @@ const getMenu = async () => {
const { wsCache } = useCache() const { wsCache } = useCache()
const routers = res.data || [] const routers = res.data || []
wsCache.set('roleRouters', routers) wsCache.set('roleRouters', routers)
await permissionStore.generateRoutes(routers).catch(() => {}) await routerStore.generateRoutes(routers).catch(() => {})
permissionStore.getAddRouters.forEach((route) => { routerStore.getAddRouters.forEach((route) => {
addRoute(route as RouteRecordRaw) // 访 addRoute(route as RouteRecordRaw) // 访
}) })
permissionStore.setIsAddRouters(true) routerStore.setIsAddRouters(true)
push({ path: redirect.value || permissionStore.addRouters[0].path }) push({ path: redirect.value || routerStore.addRouters[0].path })
} }
} }

View File

@ -12,7 +12,7 @@ import { useAuthStoreWithOut } from '@/store/modules/auth'
import { RouteLocationNormalizedLoaded, useRouter, RouteRecordRaw } from 'vue-router' import { RouteLocationNormalizedLoaded, useRouter, RouteRecordRaw } from 'vue-router'
import { getRoleMenusApi } from '@/api/login' import { getRoleMenusApi } from '@/api/login'
import { useCache } from '@/hooks/web/useCache' import { useCache } from '@/hooks/web/useCache'
import { usePermissionStore } from '@/store/modules/permission' import { useRouterStore } from '@/store/modules/router'
const emit = defineEmits(['to-login']) const emit = defineEmits(['to-login'])
@ -20,7 +20,7 @@ const { register, elFormRef, methods } = useForm()
const { t } = useI18n() const { t } = useI18n()
const { required } = useValidator() const { required } = useValidator()
const { currentRoute, addRoute, push } = useRouter() const { currentRoute, addRoute, push } = useRouter()
const permissionStore = usePermissionStore() const routerStore = useRouterStore()
const schema = reactive<FormSchema[]>([ const schema = reactive<FormSchema[]>([
{ {
@ -157,12 +157,12 @@ const getMenu = async () => {
const { wsCache } = useCache() const { wsCache } = useCache()
const routers = res.data || [] const routers = res.data || []
wsCache.set('roleRouters', routers) wsCache.set('roleRouters', routers)
await permissionStore.generateRoutes(routers).catch(() => {}) await routerStore.generateRoutes(routers).catch(() => {})
permissionStore.getAddRouters.forEach((route) => { routerStore.getAddRouters.forEach((route) => {
addRoute(route as RouteRecordRaw) // 访 addRoute(route as RouteRecordRaw) // 访
}) })
permissionStore.setIsAddRouters(true) routerStore.setIsAddRouters(true)
push({ path: redirect.value || permissionStore.addRouters[0].path }) push({ path: redirect.value || routerStore.addRouters[0].path })
} }
} }
</script> </script>

View File

@ -4,7 +4,7 @@ import { Form } from '@/components/Form'
import { ElButton } from 'element-plus' import { ElButton } from 'element-plus'
import { useForm } from '@/hooks/web/useForm' import { useForm } from '@/hooks/web/useForm'
import { postCurrentUserResetPassword } from '@/api/vadmin/auth/user' import { postCurrentUserResetPassword } from '@/api/vadmin/auth/user'
import { usePermissionStore } from '@/store/modules/permission' import { useRouterStore } from '@/store/modules/router'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import type { RouteRecordRaw, RouteLocationNormalizedLoaded } from 'vue-router' import type { RouteRecordRaw, RouteLocationNormalizedLoaded } from 'vue-router'
import { getRoleMenusApi } from '@/api/login' import { getRoleMenusApi } from '@/api/login'
@ -21,7 +21,7 @@ const { required } = useValidator()
const footer = computed(() => appStore.getFooter) const footer = computed(() => appStore.getFooter)
const permissionStore = usePermissionStore() const routerStore = useRouterStore()
const { addRoute, push, currentRoute } = useRouter() const { addRoute, push, currentRoute } = useRouter()
@ -124,12 +124,12 @@ const getMenu = async () => {
const { wsCache } = useCache() const { wsCache } = useCache()
const routers = res.data || [] const routers = res.data || []
wsCache.set('roleRouters', routers) wsCache.set('roleRouters', routers)
await permissionStore.generateRoutes(routers).catch(() => {}) await routerStore.generateRoutes(routers).catch(() => {})
permissionStore.getAddRouters.forEach((route) => { routerStore.getAddRouters.forEach((route) => {
addRoute(route as RouteRecordRaw) // 访 addRoute(route as RouteRecordRaw) // 访
}) })
permissionStore.setIsAddRouters(true) routerStore.setIsAddRouters(true)
push({ path: redirect.value || permissionStore.addRouters[0].path }) push({ path: redirect.value || routerStore.addRouters[0].path })
} }
} }
</script> </script>

View File

@ -76,7 +76,7 @@ defineExpose({
</script> </script>
<template> <template>
<Form :rules="rules" @register="register"> <Form :rules="rules" @register="register" :labelWidth="100">
<template #icon="form"> <template #icon="form">
<div style="display: flex; justify-content: space-between"> <div style="display: flex; justify-content: space-between">
<ElInput <ElInput

View File

@ -49,7 +49,6 @@ const { register, elTableRef, tableObject, methods } = useTable({
const dialogVisible = ref(false) const dialogVisible = ref(false)
const dialogTitle = ref('') const dialogTitle = ref('')
const delLoading = ref(false)
const actionType = ref('') const actionType = ref('')
const parentId = ref() const parentId = ref()
@ -74,12 +73,8 @@ const updateAction = (row: any) => {
// //
const delData = async (row: any) => { const delData = async (row: any) => {
parentId.value = null parentId.value = null
tableObject.currentRow = row
const { delListApi } = methods const { delListApi } = methods
delLoading.value = true await delListApi(true, [row.id])
await delListApi([row.id], false).finally(() => {
delLoading.value = false
})
} }
// //

View File

@ -75,7 +75,7 @@ defineExpose({
let selectAll = ref(false) let selectAll = ref(false)
let defaultExpandAll = ref(true) let defaultExpandAll = ref(true)
let checkStrictly = ref(false) let checkStrictly = ref(true) //
// key // key
const getTreeNodeKeys = (nodes: Recordable[]): number[] => { const getTreeNodeKeys = (nodes: Recordable[]): number[] => {

View File

@ -65,12 +65,8 @@ const updateAction = async (row: any) => {
// //
const delData = async (row: any) => { const delData = async (row: any) => {
tableObject.currentRow = row
const { delListApi } = methods const { delListApi } = methods
loading.value = true await delListApi(true, [row.id])
await delListApi([row.id], false).finally(() => {
loading.value = false
})
} }
const writeRef = ref<ComponentRef<typeof Write>>() const writeRef = ref<ComponentRef<typeof Write>>()

View File

@ -1,98 +0,0 @@
<script setup lang="ts">
import { ElButton, ElTable, ElTableColumn, ElPopconfirm, ElMessage, ElTag } from 'element-plus'
import { postUsersInitPasswordSendSMSApi } from '@/api/vadmin/auth/user'
import { ref, PropType } from 'vue'
const emit = defineEmits(['getList'])
const props = defineProps({
selections: {
type: Object as PropType<Recordable[]>
}
})
const tableData = ref(JSON.parse(JSON.stringify(props.selections)))
const loading = ref(false)
const handleDelete = (index: number) => {
tableData.value.splice(index, 1)
}
const initPassword = async () => {
loading.value = true
const ids = tableData.value
.filter((item) => item.reset_password_status !== true)
.map((item) => item.id)
if (ids.length <= 0) {
return ElMessage.warning('已全部重置完成,无需重复操作')
}
const res = await postUsersInitPasswordSendSMSApi(ids).finally(() => {
loading.value = false
})
if (res) {
tableData.value = res.data
ElMessage.success('重置成功')
}
}
</script>
<template>
<div>
<div class="flex justify-between">
<span>已选用户列表</span>
<ElButton
type="primary"
:disabled="tableData?.length === 0"
:loading="loading"
@click="initPassword"
>确认重置并发送短信通知</ElButton
>
</div>
<ElTable
:data="tableData"
:stripe="true"
:border="true"
style="width: 100%"
class="mt-10px"
max-height="500px"
>
<ElTableColumn prop="id" label="用户编号" width="100" align="center" />
<ElTableColumn prop="name" label="姓名" width="120" align="center" />
<ElTableColumn prop="telephone" label="手机号" width="120" align="center" />
<ElTableColumn prop="reset_password_status" label="重置状态" width="100" align="center">
<template #default="scope">
<ElTag v-if="scope.row.reset_password_status === true" type="success" effect="dark">
重置成功
</ElTag>
<ElTag v-else-if="scope.row.reset_password_status === false" type="danger" effect="dark">
重置失败
</ElTag>
<ElTag v-else type="warning" effect="dark"> 待重置 </ElTag>
</template>
</ElTableColumn>
<ElTableColumn prop="send_sms_status" label="发送状态" width="100" align="center">
<template #default="scope">
<ElTag v-if="scope.row.send_sms_status === true" type="success" effect="dark">
发送成功
</ElTag>
<ElTag v-else-if="scope.row.send_sms_status === false" type="danger" effect="dark">
发送失败
</ElTag>
<ElTag v-else type="warning" effect="dark"> 待发送 </ElTag>
</template>
</ElTableColumn>
<ElTableColumn prop="send_sms_msg" label="描述" align="center" />
<ElTableColumn fixed="right" label="操作" width="100" align="center">
<template #default="scope">
<ElPopconfirm title="确认移除吗?" @confirm="handleDelete(scope.$index)">
<template #reference>
<ElButton v-if="scope.row.send_sms_status !== true" link type="primary" size="small"
>移除</ElButton
>
</template>
</ElPopconfirm>
</template>
</ElTableColumn>
</ElTable>
</div>
</template>

View File

@ -71,6 +71,9 @@ const { register, elTableRef, tableObject, methods } = useTable({
}, },
defaultParams: { defaultParams: {
is_active: true is_active: true
},
props: {
columns
} }
}) })
@ -100,20 +103,16 @@ const updateAction = async (row: any) => {
} }
// //
const delDatas = async (row: any, multiple: boolean) => { const delDatas = async (row: any) => {
const { delListApi, getSelections } = methods const { delListApi, getSelections } = methods
loading.value = true
const selections = ref([] as any[]) const selections = ref([] as any[])
if (multiple) { if (row) {
selections.value = [row.id]
} else {
selections.value = await getSelections() selections.value = await getSelections()
selections.value = selections.value.map((item) => item.id) selections.value = selections.value.map((item) => item.id)
} else {
tableObject.currentRow = row
selections.value = [row.id]
} }
await delListApi(selections.value, multiple).finally(() => { await delListApi(true, selections.value)
loading.value = false
})
} }
const writeRef = ref<ComponentRef<typeof Write>>() const writeRef = ref<ComponentRef<typeof Write>>()
@ -224,7 +223,7 @@ const handleCommand = (command: string) => {
} else if (command === 'd') { } else if (command === 'd') {
sendPasswordToEmail() sendPasswordToEmail()
} else if (command === 'e') { } else if (command === 'e') {
delDatas(null, true) delDatas(null)
} }
} }
</script> </script>
@ -256,7 +255,7 @@ const handleCommand = (command: string) => {
<ElButton @click="sendPasswordToEmail">重置密码通知邮件</ElButton> <ElButton @click="sendPasswordToEmail">重置密码通知邮件</ElButton>
</ElCol> </ElCol>
<ElCol :span="1.5" v-hasPermi="['auth.user.delete']" v-if="!mobile"> <ElCol :span="1.5" v-hasPermi="['auth.user.delete']" v-if="!mobile">
<ElButton type="danger" @click="delDatas(null, true)">批量删除</ElButton> <ElButton type="danger" @click="delDatas(null)">批量删除</ElButton>
</ElCol> </ElCol>
<ElCol :span="1.5" v-if="mobile"> <ElCol :span="1.5" v-if="mobile">
<ElDropdown trigger="click" @command="handleCommand"> <ElDropdown trigger="click" @command="handleCommand">
@ -325,7 +324,7 @@ const handleCommand = (command: string) => {
v-hasPermi="['auth.user.delete']" v-hasPermi="['auth.user.delete']"
link link
size="small" size="small"
@click="delDatas(row, false)" @click="delDatas(row)"
v-if="authStore.getUser.id !== row.id && row.id !== 1" v-if="authStore.getUser.id !== row.id && row.id !== 1"
> >
{{ t('exampleDemo.del') }} {{ t('exampleDemo.del') }}

View File

@ -58,7 +58,6 @@ watch(
} }
) )
const loading = ref(false)
const searchSetSchemaList = ref([] as FormSetPropsType[]) const searchSetSchemaList = ref([] as FormSetPropsType[])
const getOptions = async () => { const getOptions = async () => {
@ -84,12 +83,8 @@ const updateAction = async (row: any) => {
// //
const delData = async (row: any) => { const delData = async (row: any) => {
tableObject.currentRow = row
const { delListApi } = methods const { delListApi } = methods
loading.value = true await delListApi(true, [row.id])
await delListApi([row.id], false).finally(() => {
loading.value = false
})
} }
getList() getList()

View File

@ -103,12 +103,8 @@ const updateAction = async (row: any) => {
// //
const delData = async (row: any) => { const delData = async (row: any) => {
tableObject.currentRow = row
const { delListApi } = methods const { delListApi } = methods
loading.value = true await delListApi(true, [row.id])
await delListApi([row.id], false).finally(() => {
loading.value = false
})
} }
const writeRef = ref<ComponentRef<typeof Write>>() const writeRef = ref<ComponentRef<typeof Write>>()

View File

@ -89,12 +89,8 @@ const updateAction = async (row: any) => {
// //
const delData = async (row: any) => { const delData = async (row: any) => {
tableObject.currentRow = row
const { delListApi } = methods const { delListApi } = methods
loading.value = true await delListApi(true, [row.id])
await delListApi([row.id], false).finally(() => {
loading.value = false
})
} }
const writeRef = ref<ComponentRef<typeof Write>>() const writeRef = ref<ComponentRef<typeof Write>>()

View File

@ -64,12 +64,8 @@ const updateAction = async (row: any) => {
// //
const delData = async (row: any) => { const delData = async (row: any) => {
tableObject.currentRow = row
const { delListApi } = methods const { delListApi } = methods
loading.value = true await delListApi(true, [row.id])
await delListApi([row.id], false).finally(() => {
loading.value = false
})
} }
// //

View File

@ -83,7 +83,7 @@ python main.py init
python main.py run python main.py run
``` ```
## 其他 ## 其他操作
在线文档地址(在配置文件里面设置路径或者关闭) 在线文档地址(在配置文件里面设置路径或者关闭)
@ -92,6 +92,7 @@ http://127.0.0.1:9000/docs
``` ```
Git更新ignore文件直接修改gitignore是不会生效的需要先去掉已经托管的文件修改完成之后再重新添加并提交。 Git更新ignore文件直接修改gitignore是不会生效的需要先去掉已经托管的文件修改完成之后再重新添加并提交。
``` ```
第一步: 第一步:
git rm -r --cached . git rm -r --cached .
@ -116,3 +117,172 @@ python main.py migrate --env dev
``` ```
生成迁移文件后会在alembic迁移目录中的version目录中多个迁移文件 生成迁移文件后会在alembic迁移目录中的version目录中多个迁移文件
## 查询数据
### 查询过滤
```python
# 日期查询
# 值的类型str
# 值得格式:%Y-%m-%d2023-05-14
字段名称=("date", 值)
# 模糊查询
# 值的类型: str
字段名称=("like", 值)
# in 查询
# 值的类型list
字段名称=("in", 值)
# 时间区间查询
# 值的类型tuple 或者 list
字段名称=("between", 值)
# 月份查询
# 值的类型str
# 值的格式:%Y-%m2023-05
字段名称=("month", 值)
# 不等于查询
字段名称=("!=", 值)
# 大于查询
字段名称=(">", 值)
# 等于 None
字段名称=("None")
# 不等于 None
字段名称=("not None")
```
代码部分:
![image-20230514113859232](D:\programming\ktianc\project\kinit-pro\images\image-20230514113859232.png)
示例:
查询所有用户id为1或2或 4或6并且email不为空并且名称包括李
```python
users = UserDal(db).get_datas(limit=0, id=("in", [1,2,4,6]), email=("not None"), name=("like", "李"))
```
### v_join_query
外键字段查询,内连接
以常见问题类别表为例:
首先需要在 `crud.py/IssueCategoryDal``__init__` 方法中定义 `key_models`
```python
class IssueCategoryDal(DalBase):
def __init__(self, db: AsyncSession):
key_models = {
# 外键字段名,也可以自定义
"create_user": {
# 外键对应的orm模型
"model": vadminAuthModels.VadminUser,
# 如果对同一个模型只有一个外键关联时,下面这个 onclause 可以省略不写,一个以上时必须写,需要分清楚要查询的是哪个
# 这里其实可以省略不写,但是为了演示这里写出来了
"onclause": models.VadminIssueCategory.create_user_id == vadminAuthModels.VadminUser.id
}
}
super(IssueCategoryDal, self).__init__(
db,
models.VadminIssueCategory,
schemas.IssueCategorySimpleOut,
key_models
)
```
使用案例:
```python
async def test(self):
"""
v_join_query 示例方法
获取用户名称包含李 创建出的常见问题类别
"""
v_join_query = {
# 与 key_models 中定义的外键字段名定义的一样
"create_user": {
# 外键表字段名:查询值
"name": ("like", "李")
}
}
v_options = [joinedload(self.model.create_user)]
datas = self.get_datas(limit=0, v_join_query=v_join_query, v_options=v_options)
```
完整案例:
```python
class IssueCategoryDal(DalBase):
def __init__(self, db: AsyncSession):
key_models = {
# 外键字段名,也可以自定义
"create_user": {
# 外键对应的orm模型
"model": vadminAuthModels.VadminUser,
# 如果对同一个模型只有一个外键关联时,下面这个 onclause 可以省略不写,一个以上时必须写,需要分清楚要查询的是哪个
# 这里其实可以省略不写,但是为了演示这里写出来了
"onclause": models.VadminIssueCategory.create_user_id == vadminAuthModels.VadminUser.id
}
}
super(IssueCategoryDal, self).__init__(
db,
models.VadminIssueCategory,
schemas.IssueCategorySimpleOut,
key_models
)
async def test(self):
"""
v_join_query 示例方法
获取用户名称包含李 创建出的常见问题类别
"""
v_join_query = {
# 与 key_models 中定义的外键字段名定义的一样
"create_user": {
# 外键表字段名:查询值
"name": ("like", "李")
}
}
v_options = [joinedload(self.model.create_user)]
datas = self.get_datas(limit=0, v_join_query=v_join_query, v_options=v_options)
```
### v_or
或逻辑运算查询
语法:
```python
# 普通查询
v_or = [(字段名称, 值), (字段名称, 值), ... ]
# 模糊查询
v_or = [(字段名称, ("like", 值)), (字段名称, ("like", 值)), ... ]
# 组合查询
v_or = [(字段名称, ("like", 值)), (字段名称, ("in", [值, 值, 值, ...])), ... ]
# 外键查询,需要先定义 key_models
v_or = [("fk", key_models 中定义的外键字段名, 外键表字段名称, ("like", 值)), ("fk", key_models 中定义的外键字段名, 外键表字段名称, ("like", 值)), ... ]
```
比如查询一个用户手机号为`13409090909`或者`15390909090`
```python
v_or = [("telephone", "13409090909"), ("telephone", "15390909090") ]
user = UserDal(db).get_data(v_or=v_or)
```
的version目录中多个迁移文件

View File

@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
""" """
系统版本 系统版本
""" """
VERSION = "1.7.9" VERSION = "1.8.0"
"""安全警告: 不要在生产中打开调试运行!""" """安全警告: 不要在生产中打开调试运行!"""
DEBUG = True DEBUG = True
@ -20,11 +20,11 @@ DEBUG = True
DEMO = not DEBUG DEMO = not DEBUG
"""演示功能白名单""" """演示功能白名单"""
DEMO_WHITE_LIST_PATH = [ DEMO_WHITE_LIST_PATH = [
"/auth/login/", "/auth/login",
"/auth/token/refresh/", "/auth/token/refresh",
"/auth/wx/login/", "/auth/wx/login",
"/vadmin/system/dict/types/details/", "/vadmin/system/dict/types/details",
"/vadmin/auth/user/export/query/list/to/excel/" "/vadmin/auth/user/export/query/list/to/excel"
] ]
""" """

View File

@ -17,7 +17,7 @@ app = APIRouter()
########################################################### ###########################################################
# 图表数据 # 图表数据
########################################################### ###########################################################
@app.get("/banners/", summary="轮播图") @app.get("/banners", summary="轮播图")
async def get_banners(auth: Auth = Depends(AllUserAuth())): async def get_banners(auth: Auth = Depends(AllUserAuth())):
data = [ data = [
{ {
@ -33,7 +33,7 @@ async def get_banners(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(data) return SuccessResponse(data)
@app.get("/user/access/source/", summary="用户来源") @app.get("/user/access/source", summary="用户来源")
async def get_user_access_source(auth: Auth = Depends(AllUserAuth())): async def get_user_access_source(auth: Auth = Depends(AllUserAuth())):
data = [ data = [
{"value": 1000, "name": 'analysis.directAccess'}, {"value": 1000, "name": 'analysis.directAccess'},
@ -45,7 +45,7 @@ async def get_user_access_source(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(data) return SuccessResponse(data)
@app.get("/weekly/user/activity/", summary="每周用户活跃量") @app.get("/weekly/user/activity", summary="每周用户活跃量")
async def get_weekly_user_activity(auth: Auth = Depends(AllUserAuth())): async def get_weekly_user_activity(auth: Auth = Depends(AllUserAuth())):
data = [ data = [
{"value": 13253, "name": 'analysis.monday'}, {"value": 13253, "name": 'analysis.monday'},
@ -59,7 +59,7 @@ async def get_weekly_user_activity(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(data) return SuccessResponse(data)
@app.get("/monthly/sales/", summary="每月销售额") @app.get("/monthly/sales", summary="每月销售额")
async def get_monthly_sales(auth: Auth = Depends(AllUserAuth())): async def get_monthly_sales(auth: Auth = Depends(AllUserAuth())):
data = [ data = [
{"estimate": 100, "actual": 120, "name": 'analysis.january'}, {"estimate": 100, "actual": 120, "name": 'analysis.january'},

View File

@ -59,7 +59,7 @@ class VadminUser(BaseModel):
self.last_login = datetime.datetime.now() self.last_login = datetime.datetime.now()
await db.flush() await db.flush()
async def is_admin(self) -> bool: def is_admin(self) -> bool:
""" """
获取该用户是否拥有最高权限 获取该用户是否拥有最高权限

View File

@ -39,7 +39,7 @@ import jwt
app = APIRouter() app = APIRouter()
@app.post("/login/", summary="手机号密码登录", description="员工登录通道限制最多输错次数达到最大值后将is_active=False") @app.post("/login", summary="手机号密码登录", description="员工登录通道限制最多输错次数达到最大值后将is_active=False")
async def login_for_access_token( async def login_for_access_token(
request: Request, request: Request,
data: LoginForm, data: LoginForm,
@ -74,7 +74,7 @@ async def login_for_access_token(
return ErrorResponse(msg=str(e)) return ErrorResponse(msg=str(e))
@app.post("/wx/login/", summary="微信服务端一键登录", description="员工登录通道") @app.post("/wx/login", summary="微信服务端一键登录", description="员工登录通道")
async def wx_login_for_access_token( async def wx_login_for_access_token(
request: Request, request: Request,
data: WXLoginForm, data: WXLoginForm,
@ -116,12 +116,12 @@ async def wx_login_for_access_token(
return SuccessResponse(resp) return SuccessResponse(resp)
@app.get("/getMenuList/", summary="获取当前用户菜单树") @app.get("/getMenuList", summary="获取当前用户菜单树")
async def get_menu_list(auth: Auth = Depends(FullAdminAuth())): async def get_menu_list(auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(await MenuDal(auth.db).get_routers(auth.user)) return SuccessResponse(await MenuDal(auth.db).get_routers(auth.user))
@app.post("/token/refresh/", summary="刷新Token") @app.post("/token/refresh", summary="刷新Token")
async def token_refresh(refresh: str = Body(..., title="刷新Token")): async def token_refresh(refresh: str = Body(..., title="刷新Token")):
error_code = status.HTTP_401_UNAUTHORIZED error_code = status.HTTP_401_UNAUTHORIZED
try: try:

View File

@ -11,6 +11,7 @@ from fastapi import Request
from application import settings from application import settings
import jwt import jwt
from apps.vadmin.auth import models from apps.vadmin.auth import models
from core.database import redis_getter
from .validation import LoginValidation, LoginForm, LoginResult from .validation import LoginValidation, LoginForm, LoginResult
from utils.aliyun_sms import AliyunSMS from utils.aliyun_sms import AliyunSMS
@ -35,7 +36,7 @@ class LoginManage:
""" """
验证用户短信验证码 验证用户短信验证码
""" """
rd = request.app.state.redis rd = redis_getter(request)
sms = AliyunSMS(rd, data.telephone) sms = AliyunSMS(rd, data.telephone)
result = await sms.check_sms_code(data.password) result = await sms.check_sms_code(data.password)
if result: if result:

View File

@ -25,12 +25,17 @@ class Auth(BaseModel):
class AuthValidation: class AuthValidation:
""" """
用于用户每次调用接口时验证用户提交的token是否正确并从token中获取用户信息 用于用户每次调用接口时验证用户提交的token是否正确并从token中获取用户信息
""" """
# status_code = 401 时表示强制要求重新登录因账号已冻结账号已过期手机号码错误刷新token无效等问题导致
# 只有 code = 401 时,表示 token 过期,要求刷新 token
# 只有 code = 错误值时,只是报错,不重新登陆
error_code = status.HTTP_401_UNAUTHORIZED error_code = status.HTTP_401_UNAUTHORIZED
warning_code = status.HTTP_ERROR
# status_code = 403 时,表示强制要求重新登录,因无系统权限,而进入到系统访问等问题导致
@classmethod @classmethod
def validate_token(cls, request: Request, token: str | None) -> str: def validate_token(cls, request: Request, token: str | None) -> str:
@ -38,14 +43,23 @@ class AuthValidation:
验证用户 token 验证用户 token
""" """
if not token: if not token:
raise CustomException(msg="请您先登录!", code=status.HTTP_ERROR) raise CustomException(
msg="请您先登录!",
code=status.HTTP_403_FORBIDDEN,
status_code=status.HTTP_403_FORBIDDEN
)
try: try:
# TODO token解析失败问题解决
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
telephone: str = payload.get("sub") telephone: str = payload.get("sub")
exp: int = payload.get("exp") exp: int = payload.get("exp")
is_refresh: bool = payload.get("is_refresh") is_refresh: bool = payload.get("is_refresh")
if telephone is None or is_refresh: if telephone is None or is_refresh:
raise CustomException(msg="未认证,请您重新登录", code=cls.error_code) raise CustomException(
msg="未认证,请您重新登录",
code=status.HTTP_403_FORBIDDEN,
status_code=status.HTTP_403_FORBIDDEN
)
# 计算当前时间 + 缓冲时间是否大于等于 JWT 过期时间 # 计算当前时间 + 缓冲时间是否大于等于 JWT 过期时间
buffer_time = (datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_CACHE_MINUTES)).timestamp() buffer_time = (datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_CACHE_MINUTES)).timestamp()
# print("过期时间", exp, datetime.fromtimestamp(exp)) # print("过期时间", exp, datetime.fromtimestamp(exp))
@ -55,10 +69,14 @@ class AuthValidation:
request.scope["if-refresh"] = 1 request.scope["if-refresh"] = 1
else: else:
request.scope["if-refresh"] = 0 request.scope["if-refresh"] = 0
except jwt.exceptions.InvalidSignatureError: except (jwt.exceptions.InvalidSignatureError, jwt.exceptions.DecodeError):
raise CustomException(msg="无效认证,请您重新登录", code=cls.error_code) raise CustomException(
msg="无效认证,请您重新登录",
code=status.HTTP_403_FORBIDDEN,
status_code=status.HTTP_403_FORBIDDEN
)
except jwt.exceptions.ExpiredSignatureError: except jwt.exceptions.ExpiredSignatureError:
raise CustomException(msg="认证已过期,请您重新登录", code=cls.error_code) raise CustomException(msg="认证已过期,请您重新登录", code=cls.error_code, status_code=cls.error_code)
return telephone return telephone
@classmethod @classmethod
@ -84,7 +102,7 @@ class AuthValidation:
""" """
获取员工用户所有权限列表 获取员工用户所有权限列表
""" """
if any([role.is_admin for role in user.roles]): if user.is_admin():
return {'*.*.*'} return {'*.*.*'}
permissions = set() permissions = set()
for role_obj in user.roles: for role_obj in user.roles:

View File

@ -11,6 +11,7 @@ from pydantic import BaseModel, validator
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from application.settings import DEFAULT_AUTH_ERROR_MAX_NUMBER, DEMO from application.settings import DEFAULT_AUTH_ERROR_MAX_NUMBER, DEMO
from apps.vadmin.auth import crud, schemas from apps.vadmin.auth import crud, schemas
from core.database import redis_getter
from core.validator import vali_telephone from core.validator import vali_telephone
from typing import Optional from typing import Optional
from utils.count import Count from utils.count import Count
@ -64,7 +65,7 @@ class LoginValidation:
result = await self.func(self, data=data, user=user, request=request) result = await self.func(self, data=data, user=user, request=request)
count_key = f"{data.telephone}_password_auth" if data.method == '0' else f"{data.telephone}_sms_auth" count_key = f"{data.telephone}_password_auth" if data.method == '0' else f"{data.telephone}_sms_auth"
count = Count(request.app.state.redis, count_key) count = Count(redis_getter(request), count_key)
if not result.status: if not result.status:
self.result.msg = result.msg self.result.msg = result.msg

View File

@ -23,7 +23,7 @@ app = APIRouter()
########################################################### ###########################################################
# 用户管理 # 用户管理
########################################################### ###########################################################
@app.get("/users/", summary="获取用户列表") @app.get("/users", summary="获取用户列表")
async def get_users( async def get_users(
params: UserParams = Depends(), params: UserParams = Depends(),
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.list"])) auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.list"]))
@ -36,12 +36,12 @@ async def get_users(
return SuccessResponse(datas, count=count) return SuccessResponse(datas, count=count)
@app.post("/users/", summary="创建用户") @app.post("/users", summary="创建用户")
async def create_user(data: schemas.UserIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create"]))): async def create_user(data: schemas.UserIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create"]))):
return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data)) return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data))
@app.delete("/users/", summary="批量删除用户", description="软删除,删除后清空所关联的角色") @app.delete("/users", summary="批量删除用户", description="软删除,删除后清空所关联的角色")
async def delete_users(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.delete"]))): async def delete_users(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.delete"]))):
if auth.user.id in ids.ids: if auth.user.id in ids.ids:
return ErrorResponse("不能删除当前登录用户") return ErrorResponse("不能删除当前登录用户")
@ -51,7 +51,7 @@ async def delete_users(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu
return SuccessResponse("删除成功") return SuccessResponse("删除成功")
@app.put("/users/{data_id}/", summary="更新用户信息") @app.put("/users/{data_id}", summary="更新用户信息")
async def put_user( async def put_user(
data_id: int, data_id: int,
data: schemas.UserUpdate, data: schemas.UserUpdate,
@ -60,7 +60,7 @@ async def put_user(
return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data)) return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data))
@app.get("/users/{data_id}/", summary="获取用户信息") @app.get("/users/{data_id}", summary="获取用户信息")
async def get_user( async def get_user(
data_id: int, data_id: int,
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.view", "auth.user.update"])) auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.view", "auth.user.update"]))
@ -71,29 +71,29 @@ async def get_user(
return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema)) return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema))
@app.post("/user/current/reset/password/", summary="重置当前用户密码") @app.post("/user/current/reset/password", summary="重置当前用户密码")
async def user_current_reset_password(data: schemas.ResetPwd, auth: Auth = Depends(AllUserAuth())): async def user_current_reset_password(data: schemas.ResetPwd, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data)) return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data))
@app.post("/user/current/update/info/", summary="更新当前用户基本信息") @app.post("/user/current/update/info", summary="更新当前用户基本信息")
async def post_user_current_update_info(data: schemas.UserUpdateBaseInfo, auth: Auth = Depends(AllUserAuth())): async def post_user_current_update_info(data: schemas.UserUpdateBaseInfo, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data)) return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data))
@app.post("/user/current/update/avatar/", summary="更新当前用户头像") @app.post("/user/current/update/avatar", summary="更新当前用户头像")
async def post_user_current_update_avatar(file: UploadFile, auth: Auth = Depends(AllUserAuth())): async def post_user_current_update_avatar(file: UploadFile, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file)) return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file))
@app.get("/user/admin/current/info/", summary="获取当前管理员信息") @app.get("/user/admin/current/info", summary="获取当前管理员信息")
async def get_user_admin_current_info(auth: Auth = Depends(FullAdminAuth())): async def get_user_admin_current_info(auth: Auth = Depends(FullAdminAuth())):
result = schemas.UserOut.from_orm(auth.user).dict() result = schemas.UserOut.from_orm(auth.user).dict()
result["permissions"] = list(FullAdminAuth.get_user_permissions(auth.user)) result["permissions"] = list(FullAdminAuth.get_user_permissions(auth.user))
return SuccessResponse(result) return SuccessResponse(result)
@app.post("/user/export/query/list/to/excel/", summary="导出用户查询列表为excel") @app.post("/user/export/query/list/to/excel", summary="导出用户查询列表为excel")
async def post_user_export_query_list( async def post_user_export_query_list(
header: list = Body(..., title="表头与对应字段"), header: list = Body(..., title="表头与对应字段"),
params: UserParams = Depends(), params: UserParams = Depends(),
@ -102,17 +102,17 @@ async def post_user_export_query_list(
return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params)) return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params))
@app.get("/user/download/import/template/", summary="下载最新批量导入用户模板") @app.get("/user/download/import/template", summary="下载最新批量导入用户模板")
async def get_user_download_new_import_template(auth: Auth = Depends(AllUserAuth())): async def get_user_download_new_import_template(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).download_import_template()) return SuccessResponse(await crud.UserDal(auth.db).download_import_template())
@app.post("/import/users/", summary="批量导入用户") @app.post("/import/users", summary="批量导入用户")
async def post_import_users(file: UploadFile, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.import"]))): async def post_import_users(file: UploadFile, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.import"]))):
return SuccessResponse(await crud.UserDal(auth.db).import_users(file)) return SuccessResponse(await crud.UserDal(auth.db).import_users(file))
@app.post("/users/init/password/send/sms/", summary="初始化所选用户密码并发送通知短信") @app.post("/users/init/password/send/sms", summary="初始化所选用户密码并发送通知短信")
async def post_users_init_password( async def post_users_init_password(
request: Request, request: Request,
ids: IdList = Depends(), ids: IdList = Depends(),
@ -122,7 +122,7 @@ async def post_users_init_password(
return SuccessResponse(await crud.UserDal(auth.db).init_password_send_sms(ids.ids, rd)) return SuccessResponse(await crud.UserDal(auth.db).init_password_send_sms(ids.ids, rd))
@app.post("/users/init/password/send/email/", summary="初始化所选用户密码并发送通知邮件") @app.post("/users/init/password/send/email", summary="初始化所选用户密码并发送通知邮件")
async def post_users_init_password_send_email( async def post_users_init_password_send_email(
request: Request, request: Request,
ids: IdList = Depends(), ids: IdList = Depends(),
@ -132,7 +132,7 @@ async def post_users_init_password_send_email(
return SuccessResponse(await crud.UserDal(auth.db).init_password_send_email(ids.ids, rd)) return SuccessResponse(await crud.UserDal(auth.db).init_password_send_email(ids.ids, rd))
@app.put("/users/wx/server/openid/", summary="更新当前用户服务端微信平台openid") @app.put("/users/wx/server/openid", summary="更新当前用户服务端微信平台openid")
async def put_user_wx_server_openid(code: str, auth: Auth = Depends(AllUserAuth()), rd: Redis = Depends(redis_getter)): async def put_user_wx_server_openid(code: str, auth: Auth = Depends(AllUserAuth()), rd: Redis = Depends(redis_getter)):
result = await crud.UserDal(auth.db).update_wx_server_openid(code, auth.user, rd) result = await crud.UserDal(auth.db).update_wx_server_openid(code, auth.user, rd)
return SuccessResponse(result) return SuccessResponse(result)
@ -141,7 +141,7 @@ async def put_user_wx_server_openid(code: str, auth: Auth = Depends(AllUserAuth(
########################################################### ###########################################################
# 角色管理 # 角色管理
########################################################### ###########################################################
@app.get("/roles/", summary="获取角色列表") @app.get("/roles", summary="获取角色列表")
async def get_roles( async def get_roles(
params: RoleParams = Depends(), params: RoleParams = Depends(),
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.list"])) auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.list"]))
@ -151,12 +151,12 @@ async def get_roles(
return SuccessResponse(datas, count=count) return SuccessResponse(datas, count=count)
@app.post("/roles/", summary="创建角色信息") @app.post("/roles", summary="创建角色信息")
async def create_role(role: schemas.RoleIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create"]))): async def create_role(role: schemas.RoleIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create"]))):
return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role)) return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role))
@app.delete("/roles/", summary="批量删除角色", description="硬删除, 如果存在用户关联则无法删除") @app.delete("/roles", summary="批量删除角色", description="硬删除, 如果存在用户关联则无法删除")
async def delete_roles(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.delete"]))): async def delete_roles(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.delete"]))):
if 1 in ids.ids: if 1 in ids.ids:
return ErrorResponse("不能删除管理员角色") return ErrorResponse("不能删除管理员角色")
@ -164,7 +164,7 @@ async def delete_roles(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu
return SuccessResponse("删除成功") return SuccessResponse("删除成功")
@app.put("/roles/{data_id}/", summary="更新角色信息") @app.put("/roles/{data_id}", summary="更新角色信息")
async def put_role( async def put_role(
data_id: int, data_id: int,
data: schemas.RoleIn, data: schemas.RoleIn,
@ -175,12 +175,12 @@ async def put_role(
return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data)) return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data))
@app.get("/roles/options/", summary="获取角色选择项") @app.get("/roles/options", summary="获取角色选择项")
async def get_role_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create", "auth.user.update"]))): async def get_role_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create", "auth.user.update"]))):
return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas()) return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas())
@app.get("/roles/{data_id}/", summary="获取角色信息") @app.get("/roles/{data_id}", summary="获取角色信息")
async def get_role( async def get_role(
data_id: int, data_id: int,
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.view", "auth.role.update"])) auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.view", "auth.role.update"]))
@ -194,39 +194,39 @@ async def get_role(
########################################################### ###########################################################
# 菜单管理 # 菜单管理
########################################################### ###########################################################
@app.get("/menus/", summary="获取菜单列表") @app.get("/menus", summary="获取菜单列表")
async def get_menus(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.list"]))): async def get_menus(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.list"]))):
datas = await crud.MenuDal(auth.db).get_tree_list(mode=1) datas = await crud.MenuDal(auth.db).get_tree_list(mode=1)
return SuccessResponse(datas) return SuccessResponse(datas)
@app.get("/menus/tree/options/", summary="获取菜单树选择项,添加/修改菜单时使用") @app.get("/menus/tree/options", summary="获取菜单树选择项,添加/修改菜单时使用")
async def get_menus_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create", "auth.menu.update"]))): async def get_menus_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create", "auth.menu.update"]))):
datas = await crud.MenuDal(auth.db).get_tree_list(mode=2) datas = await crud.MenuDal(auth.db).get_tree_list(mode=2)
return SuccessResponse(datas) return SuccessResponse(datas)
@app.get("/menus/role/tree/options/", summary="获取菜单列表树信息,角色权限使用") @app.get("/menus/role/tree/options", summary="获取菜单列表树信息,角色权限使用")
async def get_menus_treeselect( async def get_menus_treeselect(
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"])) auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))
): ):
return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3)) return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3))
@app.post("/menus/", summary="创建菜单信息") @app.post("/menus", summary="创建菜单信息")
async def create_menu(menu: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create"]))): async def create_menu(menu: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create"]))):
if menu.parent_id: if menu.parent_id:
menu.alwaysShow = False menu.alwaysShow = False
return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu)) return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu))
@app.delete("/menus/", summary="批量删除菜单", description="硬删除, 如果存在角色关联则无法删除") @app.delete("/menus", summary="批量删除菜单", description="硬删除, 如果存在角色关联则无法删除")
async def delete_menus(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.delete"]))): async def delete_menus(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.delete"]))):
await crud.MenuDal(auth.db).delete_datas(ids.ids, v_soft=False) await crud.MenuDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功") return SuccessResponse("删除成功")
@app.put("/menus/{data_id}/", summary="更新菜单信息") @app.put("/menus/{data_id}", summary="更新菜单信息")
async def put_menus( async def put_menus(
data_id: int, data_id: int,
data: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.update"])) data: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.update"]))
@ -234,7 +234,7 @@ async def put_menus(
return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data)) return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data))
@app.get("/menus/{data_id}/", summary="获取菜单信息") @app.get("/menus/{data_id}", summary="获取菜单信息")
async def put_menus( async def put_menus(
data_id: int, data_id: int,
auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.view", "auth.menu.update"])) auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.view", "auth.menu.update"]))
@ -243,7 +243,7 @@ async def put_menus(
return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema)) return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema))
@app.get("/role/menus/tree/{role_id}/", summary="获取菜单列表树信息以及角色菜单权限ID角色权限使用") @app.get("/role/menus/tree/{role_id}", summary="获取菜单列表树信息以及角色菜单权限ID角色权限使用")
async def get_role_menu_tree( async def get_role_menu_tree(
role_id: int, role_id: int,
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"])) auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))

View File

@ -5,11 +5,13 @@
# @File : crud.py # @File : crud.py
# @IDE : PyCharm # @IDE : PyCharm
# @desc : 帮助中心 - 增删改查 # @desc : 帮助中心 - 增删改查
from typing import List
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload
from core.crud import DalBase from core.crud import DalBase
from . import models, schemas from . import models, schemas
from apps.vadmin.auth import models as vadminAuthModels
class IssueDal(DalBase): class IssueDal(DalBase):
@ -30,5 +32,34 @@ class IssueDal(DalBase):
class IssueCategoryDal(DalBase): class IssueCategoryDal(DalBase):
def __init__(self, db: AsyncSession): def __init__(self, db: AsyncSession):
super(IssueCategoryDal, self).__init__(db, models.VadminIssueCategory, schemas.IssueCategorySimpleOut) key_models = {
# 外键字段名,也可以自定义
"create_user": {
# 外键对应的orm模型
"model": vadminAuthModels.VadminUser,
# 如果对同一个模型只有一个外键关联时,下面这个 onclause 可以省略不写,一个以上时必须写,需要分清楚要查询的是哪个
# 这里其实可以省略不写,但是为了演示这里写出来了
"onclause": models.VadminIssueCategory.create_user_id == vadminAuthModels.VadminUser.id
}
}
super(IssueCategoryDal, self).__init__(
db,
models.VadminIssueCategory,
schemas.IssueCategorySimpleOut,
key_models
)
async def test(self):
"""
v_join_query 示例方法
获取用户名称包含李 创建出的常见问题类别
"""
v_join_query = {
# 与 key_models 中定义的外键字段名定义的一样
"create_user": {
# 外键表字段名:查询值
"name": ("like", "")
}
}
v_options = [joinedload(self.model.create_user)]
datas = self.get_datas(limit=0, v_join_query=v_join_query, v_options=v_options)

View File

@ -21,8 +21,8 @@ class VadminIssueCategory(BaseModel):
issues = relationship("VadminIssue", back_populates='category') issues = relationship("VadminIssue", back_populates='category')
user_id = Column(ForeignKey("vadmin_auth_user.id", ondelete='SET NULL'), comment="创建人") create_user_id = Column(ForeignKey("vadmin_auth_user.id", ondelete='SET NULL'), comment="创建人")
user = relationship("VadminUser", foreign_keys=user_id) create_user = relationship("VadminUser", foreign_keys=create_user_id)
class VadminIssue(BaseModel): class VadminIssue(BaseModel):
@ -37,6 +37,6 @@ class VadminIssue(BaseModel):
view_number = Column(Integer, default=0, comment="查看次数") view_number = Column(Integer, default=0, comment="查看次数")
is_active = Column(Boolean, default=True, comment="是否可见") is_active = Column(Boolean, default=True, comment="是否可见")
user_id = Column(ForeignKey("vadmin_auth_user.id", ondelete='SET NULL'), comment="创建人") create_user_id = Column(ForeignKey("vadmin_auth_user.id", ondelete='SET NULL'), comment="创建人")
user = relationship("VadminUser", foreign_keys=user_id) create_user = relationship("VadminUser", foreign_keys=create_user_id)

View File

@ -15,7 +15,7 @@ from .issue_category import IssueCategorySimpleOut
class Issue(BaseModel): class Issue(BaseModel):
category_id: Optional[int] = None category_id: Optional[int] = None
user_id: Optional[int] = None create_user_id: Optional[int] = None
title: Optional[str] = None title: Optional[str] = None
content: Optional[str] = None content: Optional[str] = None
@ -33,7 +33,7 @@ class IssueSimpleOut(Issue):
class IssueListOut(IssueSimpleOut): class IssueListOut(IssueSimpleOut):
user: UserSimpleOut create_user: UserSimpleOut
category: IssueCategorySimpleOut category: IssueCategorySimpleOut
class Config: class Config:

View File

@ -18,7 +18,7 @@ class IssueCategory(BaseModel):
platform: Optional[str] = None platform: Optional[str] = None
is_active: Optional[bool] = None is_active: Optional[bool] = None
user_id: Optional[int] = None create_user_id: Optional[int] = None
class IssueCategorySimpleOut(IssueCategory): class IssueCategorySimpleOut(IssueCategory):
@ -31,7 +31,7 @@ class IssueCategorySimpleOut(IssueCategory):
class IssueCategoryListOut(IssueCategorySimpleOut): class IssueCategoryListOut(IssueCategorySimpleOut):
user: UserSimpleOut create_user: UserSimpleOut
class Config: class Config:
orm_mode = True orm_mode = True

View File

@ -16,7 +16,7 @@ class IssueCategoryPlatformOut(BaseModel):
name: Optional[str] = None name: Optional[str] = None
platform: Optional[str] = None platform: Optional[str] = None
is_active: Optional[bool] = None is_active: Optional[bool] = None
user_id: Optional[int] = None create_user_id: Optional[int] = None
id: int id: int
update_datetime: DatetimeStr update_datetime: DatetimeStr

View File

@ -22,91 +22,95 @@ app = APIRouter()
########################################################### ###########################################################
# 类别管理 # 类别管理
########################################################### ###########################################################
@app.get("/issue/categorys/", summary="获取类别列表") @app.get("/issue/categorys", summary="获取类别列表")
async def get_issue_categorys(p: params.IssueCategoryParams = Depends(), auth: Auth = Depends(AllUserAuth())): async def get_issue_categorys(p: params.IssueCategoryParams = Depends(), auth: Auth = Depends(AllUserAuth())):
model = models.VadminIssueCategory model = models.VadminIssueCategory
options = [joinedload(model.user)] options = [joinedload(model.create_user)]
schema = schemas.IssueCategoryListOut schema = schemas.IssueCategoryListOut
datas = await crud.IssueCategoryDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema) datas = await crud.IssueCategoryDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema)
count = await crud.IssueCategoryDal(auth.db).get_count(**p.to_count()) count = await crud.IssueCategoryDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count) return SuccessResponse(datas, count=count)
@app.get("/issue/categorys/options/", summary="获取类别选择项") @app.get("/issue/categorys/options", summary="获取类别选择项")
async def get_issue_categorys_options(auth: Auth = Depends(AllUserAuth())): async def get_issue_categorys_options(auth: Auth = Depends(AllUserAuth())):
schema = schemas.IssueCategoryOptionsOut schema = schemas.IssueCategoryOptionsOut
return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema)) return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema))
@app.post("/issue/categorys/", summary="创建类别") @app.post("/issue/categorys", summary="创建类别")
async def create_issue_category(data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())): async def create_issue_category(data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())):
data.user_id = auth.user.id data.create_user_id = auth.user.id
return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data)) return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data))
@app.delete("/issue/categorys/", summary="批量删除类别", description="硬删除") @app.delete("/issue/categorys", summary="批量删除类别", description="硬删除")
async def delete_issue_categorys(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())): async def delete_issue_categorys(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.IssueCategoryDal(auth.db).delete_datas(ids=ids.ids, v_soft=False) await crud.IssueCategoryDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
return SuccessResponse("删除成功") return SuccessResponse("删除成功")
@app.put("/issue/categorys/{data_id}/", summary="更新类别信息") @app.put("/issue/categorys/{data_id}", summary="更新类别信息")
async def put_issue_category(data_id: int, data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())): async def put_issue_category(data_id: int, data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data)) return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data))
@app.get("/issue/categorys/{data_id}/", summary="获取类别信息") @app.get("/issue/categorys/{data_id}", summary="获取类别信息")
async def get_issue_category(data_id: int, auth: Auth = Depends(AllUserAuth())): async def get_issue_category(data_id: int, auth: Auth = Depends(AllUserAuth())):
schema = schemas.IssueCategorySimpleOut schema = schemas.IssueCategorySimpleOut
return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema)) return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema))
@app.get("/issue/categorys/platform/{platform}/", summary="获取平台中的常见问题类别列表") @app.get("/issue/categorys/platform/{platform}", summary="获取平台中的常见问题类别列表")
async def get_issue_category_platform(platform: str, db: AsyncSession = Depends(db_getter)): async def get_issue_category_platform(platform: str, db: AsyncSession = Depends(db_getter)):
model = models.VadminIssueCategory model = models.VadminIssueCategory
options = [joinedload(model.issues)] options = [joinedload(model.issues)]
schema = schemas.IssueCategoryPlatformOut schema = schemas.IssueCategoryPlatformOut
result = await crud.IssueCategoryDal(db).\ result = await crud.IssueCategoryDal(db).get_datas(
get_datas(platform=platform, is_active=True, v_schema=schema, v_options=options) platform=platform,
is_active=True,
v_schema=schema,
v_options=options
)
return SuccessResponse(result) return SuccessResponse(result)
########################################################### ###########################################################
# 问题管理 # 问题管理
########################################################### ###########################################################
@app.get("/issues/", summary="获取问题列表") @app.get("/issues", summary="获取问题列表")
async def get_issues(p: params.IssueParams = Depends(), auth: Auth = Depends(AllUserAuth())): async def get_issues(p: params.IssueParams = Depends(), auth: Auth = Depends(AllUserAuth())):
model = models.VadminIssue model = models.VadminIssue
options = [joinedload(model.user), joinedload(model.category)] options = [joinedload(model.create_user), joinedload(model.category)]
schema = schemas.IssueListOut schema = schemas.IssueListOut
datas = await crud.IssueDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema) datas = await crud.IssueDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema)
count = await crud.IssueDal(auth.db).get_count(**p.to_count()) count = await crud.IssueDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count) return SuccessResponse(datas, count=count)
@app.post("/issues/", summary="创建问题") @app.post("/issues", summary="创建问题")
async def create_issue(data: schemas.Issue, auth: Auth = Depends(AllUserAuth())): async def create_issue(data: schemas.Issue, auth: Auth = Depends(AllUserAuth())):
data.user_id = auth.user.id data.create_user_id = auth.user.id
return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data)) return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data))
@app.delete("/issues/", summary="批量删除问题", description="硬删除") @app.delete("/issues", summary="批量删除问题", description="硬删除")
async def delete_issues(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())): async def delete_issues(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.IssueDal(auth.db).delete_datas(ids=ids.ids, v_soft=False) await crud.IssueDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
return SuccessResponse("删除成功") return SuccessResponse("删除成功")
@app.put("/issues/{data_id}/", summary="更新问题信息") @app.put("/issues/{data_id}", summary="更新问题信息")
async def put_issue(data_id: int, data: schemas.Issue, auth: Auth = Depends(AllUserAuth())): async def put_issue(data_id: int, data: schemas.Issue, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data)) return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data))
@app.get("/issues/{data_id}/", summary="获取问题信息") @app.get("/issues/{data_id}", summary="获取问题信息")
async def get_issue(data_id: int, db: AsyncSession = Depends(db_getter)): async def get_issue(data_id: int, db: AsyncSession = Depends(db_getter)):
schema = schemas.IssueSimpleOut schema = schemas.IssueSimpleOut
return SuccessResponse(await crud.IssueDal(db).get_data(data_id, v_schema=schema)) return SuccessResponse(await crud.IssueDal(db).get_data(data_id, v_schema=schema))
@app.get("/issues/add/view/number/{data_id}/", summary="更新常见问题查看次数+1") @app.get("/issues/add/view/number/{data_id}", summary="更新常见问题查看次数+1")
async def issue_add_view_number(data_id: int, db: AsyncSession = Depends(db_getter)): async def issue_add_view_number(data_id: int, db: AsyncSession = Depends(db_getter)):
return SuccessResponse(await crud.IssueDal(db).add_view_number(data_id)) return SuccessResponse(await crud.IssueDal(db).add_view_number(data_id))

View File

@ -19,14 +19,14 @@ app = APIRouter()
########################################################### ###########################################################
# 日志管理 # 日志管理
########################################################### ###########################################################
@app.get("/logins/", summary="获取登录日志列表") @app.get("/logins", summary="获取登录日志列表")
async def get_record_login(p: LoginParams = Depends(), auth: Auth = Depends(AllUserAuth())): async def get_record_login(p: LoginParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.LoginRecordDal(auth.db).get_datas(**p.dict()) datas = await crud.LoginRecordDal(auth.db).get_datas(**p.dict())
count = await crud.LoginRecordDal(auth.db).get_count(**p.to_count()) count = await crud.LoginRecordDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count) return SuccessResponse(datas, count=count)
@app.get("/operations/", summary="获取操作日志列表") @app.get("/operations", summary="获取操作日志列表")
async def get_record_operation( async def get_record_operation(
p: OperationParams = Depends(), p: OperationParams = Depends(),
db: DatabaseManage = Depends(get_database), db: DatabaseManage = Depends(get_database),
@ -37,7 +37,7 @@ async def get_record_operation(
return SuccessResponse(datas, count=count) return SuccessResponse(datas, count=count)
@app.get("/sms/send/list/", summary="获取短信发送列表") @app.get("/sms/send/list", summary="获取短信发送列表")
async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUserAuth())): async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.SMSSendRecordDal(auth.db).get_datas(**p.dict()) datas = await crud.SMSSendRecordDal(auth.db).get_datas(**p.dict())
count = await crud.SMSSendRecordDal(auth.db).get_count(**p.to_count()) count = await crud.SMSSendRecordDal(auth.db).get_count(**p.to_count())
@ -47,6 +47,6 @@ async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUs
########################################################### ###########################################################
# 日志分析 # 日志分析
########################################################### ###########################################################
@app.get("/analysis/user/login/distribute/", summary="获取用户登录分布情况列表") @app.get("/analysis/user/login/distribute", summary="获取用户登录分布情况列表")
async def get_user_login_distribute(auth: Auth = Depends(AllUserAuth())): async def get_user_login_distribute(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.LoginRecordDal(auth.db).get_user_distribute()) return SuccessResponse(await crud.LoginRecordDal(auth.db).get_user_distribute())

View File

@ -95,7 +95,7 @@ class SettingsDal(DalBase):
if ico.config_value == web_ico: if ico.config_value == web_ico:
continue continue
# 将上传的ico路径替换到static/system/favicon.ico文件 # 将上传的ico路径替换到static/system/favicon.ico文件
FileManage.copy(value, os.path.join(STATIC_ROOT, "system/favicon.ico")) await FileManage.async_copy(value, os.path.join(STATIC_ROOT, "system/favicon.ico"))
sql = update(self.model).where(self.model.config_key == "web_ico").values(config_value=web_ico) sql = update(self.model).where(self.model.config_key == "web_ico").values(config_value=web_ico)
await self.db.execute(sql) await self.db.execute(sql)
else: else:

View File

@ -28,25 +28,25 @@ app = APIRouter()
########################################################### ###########################################################
# 字典类型管理 # 字典类型管理
########################################################### ###########################################################
@app.get("/dict/types/", summary="获取字典类型列表") @app.get("/dict/types", summary="获取字典类型列表")
async def get_dict_types(p: DictTypeParams = Depends(), auth: Auth = Depends(AllUserAuth())): async def get_dict_types(p: DictTypeParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.DictTypeDal(auth.db).get_datas(**p.dict()) datas = await crud.DictTypeDal(auth.db).get_datas(**p.dict())
count = await crud.DictTypeDal(auth.db).get_count(**p.to_count()) count = await crud.DictTypeDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count) return SuccessResponse(datas, count=count)
@app.post("/dict/types/", summary="创建字典类型") @app.post("/dict/types", summary="创建字典类型")
async def create_dict_types(data: schemas.DictType, auth: Auth = Depends(AllUserAuth())): async def create_dict_types(data: schemas.DictType, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).create_data(data=data)) return SuccessResponse(await crud.DictTypeDal(auth.db).create_data(data=data))
@app.delete("/dict/types/", summary="批量删除字典类型") @app.delete("/dict/types", summary="批量删除字典类型")
async def delete_dict_types(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())): async def delete_dict_types(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.DictTypeDal(auth.db).delete_datas(ids=ids.ids) await crud.DictTypeDal(auth.db).delete_datas(ids=ids.ids)
return SuccessResponse("删除成功") return SuccessResponse("删除成功")
@app.post("/dict/types/details/", summary="获取多个字典类型下的字典元素列表") @app.post("/dict/types/details", summary="获取多个字典类型下的字典元素列表")
async def post_dicts_details( async def post_dicts_details(
auth: Auth = Depends(AllUserAuth()), auth: Auth = Depends(AllUserAuth()),
dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表") dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表")
@ -55,17 +55,17 @@ async def post_dicts_details(
return SuccessResponse(datas) return SuccessResponse(datas)
@app.get("/dict/types/options/", summary="获取字典类型选择项") @app.get("/dict/types/options", summary="获取字典类型选择项")
async def get_dicts_options(auth: Auth = Depends(AllUserAuth())): async def get_dicts_options(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas()) return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas())
@app.put("/dict/types/{data_id}/", summary="更新字典类型") @app.put("/dict/types/{data_id}", summary="更新字典类型")
async def put_dict_types(data_id: int, data: schemas.DictType, auth: Auth = Depends(AllUserAuth())): async def put_dict_types(data_id: int, data: schemas.DictType, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).put_data(data_id, data)) return SuccessResponse(await crud.DictTypeDal(auth.db).put_data(data_id, data))
@app.get("/dict/types/{data_id}/", summary="获取字典类型详细") @app.get("/dict/types/{data_id}", summary="获取字典类型详细")
async def get_dict_type(data_id: int, auth: Auth = Depends(AllUserAuth())): async def get_dict_type(data_id: int, auth: Auth = Depends(AllUserAuth())):
schema = schemas.DictTypeSimpleOut schema = schemas.DictTypeSimpleOut
return SuccessResponse(await crud.DictTypeDal(auth.db).get_data(data_id, None, v_schema=schema)) return SuccessResponse(await crud.DictTypeDal(auth.db).get_data(data_id, None, v_schema=schema))
@ -74,12 +74,12 @@ async def get_dict_type(data_id: int, auth: Auth = Depends(AllUserAuth())):
########################################################### ###########################################################
# 字典元素管理 # 字典元素管理
########################################################### ###########################################################
@app.post("/dict/details/", summary="创建字典元素") @app.post("/dict/details", summary="创建字典元素")
async def create_dict_details(data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())): async def create_dict_details(data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictDetailsDal(auth.db).create_data(data=data)) return SuccessResponse(await crud.DictDetailsDal(auth.db).create_data(data=data))
@app.get("/dict/details/", summary="获取单个字典类型下的字典元素列表,分页") @app.get("/dict/details", summary="获取单个字典类型下的字典元素列表,分页")
async def get_dict_details(params: DictDetailParams = Depends(), auth: Auth = Depends(AllUserAuth())): async def get_dict_details(params: DictDetailParams = Depends(), auth: Auth = Depends(AllUserAuth())):
if not params.dict_type_id: if not params.dict_type_id:
return ErrorResponse(msg="未获取到字典类型!") return ErrorResponse(msg="未获取到字典类型!")
@ -88,18 +88,18 @@ async def get_dict_details(params: DictDetailParams = Depends(), auth: Auth = De
return SuccessResponse(datas, count=count) return SuccessResponse(datas, count=count)
@app.delete("/dict/details/", summary="批量删除字典元素", description="硬删除") @app.delete("/dict/details", summary="批量删除字典元素", description="硬删除")
async def delete_dict_details(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())): async def delete_dict_details(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.DictDetailsDal(auth.db).delete_datas(ids.ids, v_soft=False) await crud.DictDetailsDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功") return SuccessResponse("删除成功")
@app.put("/dict/details/{data_id}/", summary="更新字典元素") @app.put("/dict/details/{data_id}", summary="更新字典元素")
async def put_dict_details(data_id: int, data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())): async def put_dict_details(data_id: int, data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictDetailsDal(auth.db).put_data(data_id, data)) return SuccessResponse(await crud.DictDetailsDal(auth.db).put_data(data_id, data))
@app.get("/dict/details/{data_id}/", summary="获取字典元素详情") @app.get("/dict/details/{data_id}", summary="获取字典元素详情")
async def get_dict_detail(data_id: int, auth: Auth = Depends(AllUserAuth())): async def get_dict_detail(data_id: int, auth: Auth = Depends(AllUserAuth())):
schema = schemas.DictDetailsSimpleOut schema = schemas.DictDetailsSimpleOut
return SuccessResponse(await crud.DictDetailsDal(auth.db).get_data(data_id, None, v_schema=schema)) return SuccessResponse(await crud.DictDetailsDal(auth.db).get_data(data_id, None, v_schema=schema))
@ -108,7 +108,7 @@ async def get_dict_detail(data_id: int, auth: Auth = Depends(AllUserAuth())):
########################################################### ###########################################################
# 文件上传管理 # 文件上传管理
########################################################### ###########################################################
@app.post("/upload/image/to/oss/", summary="上传图片到阿里云OSS") @app.post("/upload/image/to/oss", summary="上传图片到阿里云OSS")
async def upload_image_to_oss(file: UploadFile, path: str = Form(...)): async def upload_image_to_oss(file: UploadFile, path: str = Form(...)):
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_image(path, file) result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_image(path, file)
if not result: if not result:
@ -116,7 +116,7 @@ async def upload_image_to_oss(file: UploadFile, path: str = Form(...)):
return SuccessResponse(result) return SuccessResponse(result)
@app.post("/upload/image/to/local/", summary="上传图片到本地") @app.post("/upload/image/to/local", summary="上传图片到本地")
async def upload_image_to_local(file: UploadFile, path: str = Form(...)): async def upload_image_to_local(file: UploadFile, path: str = Form(...)):
manage = FileManage(file, path) manage = FileManage(file, path)
path = await manage.save_image_local() path = await manage.save_image_local()
@ -126,7 +126,7 @@ async def upload_image_to_local(file: UploadFile, path: str = Form(...)):
########################################################### ###########################################################
# 短信服务管理 # 短信服务管理
########################################################### ###########################################################
@app.post("/sms/send/", summary="发送短信验证码(阿里云服务)") @app.post("/sms/send", summary="发送短信验证码(阿里云服务)")
async def sms_send(telephone: str, rd: Redis = Depends(redis_getter)): async def sms_send(telephone: str, rd: Redis = Depends(redis_getter)):
sms = AliyunSMS(rd, telephone) sms = AliyunSMS(rd, telephone)
return SuccessResponse(await sms.main_async(AliyunSMS.Scene.login)) return SuccessResponse(await sms.main_async(AliyunSMS.Scene.login))
@ -135,31 +135,31 @@ async def sms_send(telephone: str, rd: Redis = Depends(redis_getter)):
########################################################### ###########################################################
# 系统配置管理 # 系统配置管理
########################################################### ###########################################################
@app.get("/settings/tabs/", summary="获取系统配置标签列表") @app.get("/settings/tabs", summary="获取系统配置标签列表")
async def get_settings_tabs(classify: str, auth: Auth = Depends(FullAdminAuth())): async def get_settings_tabs(classify: str, auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=classify)) return SuccessResponse(await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=classify))
@app.get("/settings/tabs/values/", summary="获取系统配置标签下的信息") @app.get("/settings/tabs/values", summary="获取系统配置标签下的信息")
async def get_settings_tabs_values(tab_id: int, auth: Auth = Depends(FullAdminAuth())): async def get_settings_tabs_values(tab_id: int, auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id)) return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id))
@app.put("/settings/tabs/values/", summary="更新系统配置信息") @app.put("/settings/tabs/values", summary="更新系统配置信息")
async def put_settings_tabs_values(datas: dict = Body(...), auth: Auth = Depends(FullAdminAuth()), rd: Redis = Depends(redis_getter)): async def put_settings_tabs_values(datas: dict = Body(...), auth: Auth = Depends(FullAdminAuth()), rd: Redis = Depends(redis_getter)):
return SuccessResponse(await crud.SettingsDal(auth.db).update_datas(datas, rd)) return SuccessResponse(await crud.SettingsDal(auth.db).update_datas(datas, rd))
@app.get("/settings/base/config/", summary="获取系统基础配置", description="每次进入系统中时使用") @app.get("/settings/base/config", summary="获取系统基础配置", description="每次进入系统中时使用")
async def get_setting_base_config(db: AsyncSession = Depends(db_getter)): async def get_setting_base_config(db: AsyncSession = Depends(db_getter)):
return SuccessResponse(await crud.SettingsDal(db).get_base_config()) return SuccessResponse(await crud.SettingsDal(db).get_base_config())
@app.get("/settings/privacy/", summary="获取隐私协议") @app.get("/settings/privacy", summary="获取隐私协议")
async def get_settings_privacy(auth: Auth = Depends(FullAdminAuth())): async def get_settings_privacy(auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value) return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value)
@app.get("/settings/agreement/", summary="获取用户协议") @app.get("/settings/agreement", summary="获取用户协议")
async def get_settings_agreement(auth: Auth = Depends(FullAdminAuth())): async def get_settings_agreement(auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value) return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value)

View File

@ -19,7 +19,7 @@ app = APIRouter()
########################################################### ###########################################################
# 工作区管理 # 工作区管理
########################################################### ###########################################################
@app.get("/total/", summary="获取统计") @app.get("/total", summary="获取统计")
async def get_total(auth: Auth = Depends(AllUserAuth())): async def get_total(auth: Auth = Depends(AllUserAuth())):
data = { data = {
"project": 40, "project": 40,
@ -29,7 +29,7 @@ async def get_total(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(data) return SuccessResponse(data)
@app.get("/project/", summary="获取项目") @app.get("/project", summary="获取项目")
async def get_project(): async def get_project():
data = [ data = [
{ {
@ -84,7 +84,7 @@ async def get_project():
return SuccessResponse(data) return SuccessResponse(data)
@app.get("/dynamic/", summary="获取动态") @app.get("/dynamic", summary="获取动态")
async def get_dynamic(): async def get_dynamic():
data = [ data = [
{ {
@ -99,7 +99,7 @@ async def get_dynamic():
return SuccessResponse(data) return SuccessResponse(data)
@app.get("/team/", summary="获取团队信息") @app.get("/team", summary="获取团队信息")
async def get_team(): async def get_team():
data = [ data = [
{ {
@ -130,7 +130,7 @@ async def get_team():
return SuccessResponse(data) return SuccessResponse(data)
@app.get("/shortcuts/", summary="获取快捷操作") @app.get("/shortcuts", summary="获取快捷操作")
async def get_shortcuts(): async def get_shortcuts():
data = [ data = [
{ {

View File

@ -31,7 +31,6 @@ def create_async_engine_session(database_url: str):
pool_timeout=20, # 池中没有连接最多等待的时间,否则报错 pool_timeout=20, # 池中没有连接最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
:param database_type: 数据库类型
:param database_url: 数据库地址 :param database_url: 数据库地址
:return: :return:
""" """

View File

@ -47,7 +47,7 @@ def register_exception(app: FastAPI):
print(exc.desc) print(exc.desc)
print(exc.msg) print(exc.msg)
return JSONResponse( return JSONResponse(
status_code=200, status_code=exc.status_code,
content={"message": exc.msg, "code": exc.code}, content={"message": exc.msg, "code": exc.code},
) )

View File

@ -39,7 +39,7 @@ idna==3.4
importlib-metadata==5.0.0 importlib-metadata==5.0.0
importlib-resources==5.10.0 importlib-resources==5.10.0
jmespath==0.10.0 jmespath==0.10.0
loguru==0.6.0 loguru==0.7.0
Mako==1.2.4 Mako==1.2.4
MarkupSafe==2.1.1 MarkupSafe==2.1.1
motor==3.1.1 motor==3.1.1

View File

@ -7,7 +7,7 @@
# @desc : 缓存 # @desc : 缓存
from typing import List from typing import List
from core import logger from core.logger import logger
from core.database import db_getter from core.database import db_getter
from apps.vadmin.system.crud import SettingsTabDal from apps.vadmin.system.crud import SettingsTabDal
import json import json
@ -18,7 +18,7 @@ from utils import status
class Cache: class Cache:
DEFAULT_TAB_NAMES = ["wx_server", "aliyun_sms", "aliyun_oss"] DEFAULT_TAB_NAMES = ["wx_server", "aliyun_sms", "aliyun_oss", "web_email"]
def __init__(self, rd: Redis): def __init__(self, rd: Redis):
self.rd = rd self.rd = rd

View File

@ -4,15 +4,17 @@
# @File : file_manage.py # @File : file_manage.py
# @IDE : PyCharm # @IDE : PyCharm
# @desc : 保存图片到本地 # @desc : 保存图片到本地
import asyncio
import datetime import datetime
import os import os
import shutil import shutil
from application.settings import TEMP_DIR, STATIC_ROOT, BASE_DIR, STATIC_URL, STATIC_DIR from application.settings import TEMP_DIR, STATIC_ROOT, BASE_DIR, STATIC_URL, STATIC_DIR
from fastapi import UploadFile from fastapi import UploadFile
import sys import sys
from pathlib import Path
from core.exception import CustomException
from utils.file.file_base import FileBase from utils.file.file_base import FileBase
from aiopathlib import AsyncPath as Path from aiopathlib import AsyncPath
import aioshutil import aioshutil
@ -41,8 +43,7 @@ class FileManage(FileBase):
path = self.path path = self.path
if sys.platform == "win32": if sys.platform == "win32":
path = self.path.replace("/", "\\") path = self.path.replace("/", "\\")
# save_path = os.path.join(STATIC_ROOT, path) save_path = AsyncPath(STATIC_ROOT) / path
save_path = Path(STATIC_ROOT) / path
if not await save_path.parent.exists(): if not await save_path.parent.exists():
await save_path.parent.mkdir(parents=True, exist_ok=True) await save_path.parent.mkdir(parents=True, exist_ok=True)
await save_path.write_bytes(await self.file.read()) await save_path.write_bytes(await self.file.read())
@ -52,21 +53,20 @@ class FileManage(FileBase):
} }
@staticmethod @staticmethod
async def save_tmp_file(file: UploadFile): async def save_tmp_file(file: UploadFile) -> str:
""" """
保存临时文件 保存临时文件
""" """
date = datetime.datetime.strftime(datetime.datetime.now(), "%Y%m%d") date = datetime.datetime.strftime(datetime.datetime.now(), "%Y%m%d")
# file_dir = os.path.join(TEMP_DIR, date) file_dir = AsyncPath(TEMP_DIR) / date
file_dir = Path(TEMP_DIR) / date
if not await file_dir.exists(): if not await file_dir.exists():
await file_dir.mkdir(parents=True, exist_ok=True) await file_dir.mkdir(parents=True, exist_ok=True)
filename = file_dir / str(int(datetime.datetime.now().timestamp())) + file.filename filename = file_dir / str(int(datetime.datetime.now().timestamp())) + file.filename
await filename.write_bytes(await self.file.read()) await filename.write_bytes(await file.read())
return str(filename) return str(filename)
@staticmethod @staticmethod
def copy(src: str, dst: str): def copy(src: str, dst: str) -> None:
""" """
复制文件 复制文件
根目录为项目根目录传过来的文件路径均为相对路径 根目录为项目根目录传过来的文件路径均为相对路径
@ -79,25 +79,39 @@ class FileManage(FileBase):
if sys.platform == "win32": if sys.platform == "win32":
src = src.replace("/", "\\") src = src.replace("/", "\\")
dst = dst.replace("/", "\\") dst = dst.replace("/", "\\")
src = os.path.join(BASE_DIR, src) src = Path(BASE_DIR) / src
if not os.path.exists(os.path.dirname(dst)): dst = Path(dst)
os.mkdir(os.path.dirname(dst)) if not src.exists():
raise CustomException("源文件不存在!")
if not dst.parent.exists():
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(src, dst) shutil.copyfile(src, dst)
async def async_copy(src: str, dst: str): @staticmethod
async def async_copy(src: str, dst: str) -> None:
"""
异步复制文件
根目录为项目根目录传过来的文件路径均为相对路径
:param src: 原始文件
:param dst: 目标路径绝对路径
"""
if src[0] == "/": if src[0] == "/":
src = src.lstrip("/") src = src.lstrip("/")
if sys.platform == "win32": if sys.platform == "win32":
src = src.replace("/", "\\") src = src.replace("/", "\\")
dst = dst.replace("/", "\\") dst = dst.replace("/", "\\")
src = Path(BASE_DIR) / src src = AsyncPath(BASE_DIR) / src
dst = Path(dst) if not await src.exists():
raise CustomException("源文件不存在!")
dst = AsyncPath(dst)
if not await dst.parent.exists(): if not await dst.parent.exists():
await dst.parent.mkdir(parents=True, exist_ok=True) await dst.parent.mkdir(parents=True, exist_ok=True)
await aioshutil.copyfile(src, dst) await aioshutil.copyfile(src, dst)
if __name__ == '__main__': if __name__ == '__main__':
# src = r"D:\ktianc\private\vv-reserve\reserve-api\static\system\2022-12-07\16703958210ab33912.ico" _src = r"D:\programming\ktianc\project\kinit-pro\kinit-api\static\system\favicon.ico"
# dst = r"D:\ktianc\private\vv-reserve\reserve-api\static\system\favicon.ico" _dst = r"D:\programming\ktianc\project\kinit-pro\kinit-api\static\system\2022-12-07\16703958210ab33912.ico"
# shutil.copyfile(src, dst) asyncio.run(FileManage.async_copy(_src, _dst))
pass # FileManage.copy(_src, _dst)

View File

@ -8,18 +8,18 @@ export function login(telephone, password) {
method: '0', method: '0',
platform: '1' platform: '1'
} }
return request.post('/auth/login/', data) return request.post('/auth/login', data)
} }
// 获取用户详细信息 // 获取用户详细信息
export function getInfo() { export function getInfo() {
return request.get('/vadmin/auth/user/admin/current/info/') return request.get('/vadmin/auth/user/admin/current/info')
} }
// 更新用户openid // 更新用户openid
export function setUserOpenid(code) { export function setUserOpenid(code) {
const params = { code } const params = { code }
return request.put('/vadmin/auth/users/wx/server/openid/', {}, { params: params }) return request.put('/vadmin/auth/users/wx/server/openid', {}, { params: params })
} }
// 使用微信一键登录 // 使用微信一键登录
@ -29,5 +29,5 @@ export function wxCodeLogin(code) {
method: '2', method: '2',
platform: '1' platform: '1'
} }
return request.post('/auth/wx/login/', data) return request.post('/auth/wx/login', data)
} }

View File

@ -2,17 +2,17 @@ import request from '@/common/request/request'
// 更新当前用户基本信息 // 更新当前用户基本信息
export function updateCurrentUser(data) { export function updateCurrentUser(data) {
return request.post('/vadmin/auth/user/current/update/info/', data) return request.post('/vadmin/auth/user/current/update/info', data)
} }
// 重置当前用户密码 // 重置当前用户密码
export function postCurrentUserResetPassword(data) { export function postCurrentUserResetPassword(data) {
return request.post('/vadmin/auth/user/current/reset/password/', data) return request.post('/vadmin/auth/user/current/reset/password', data)
} }
// 更新当前用户头像 // 更新当前用户头像
export function postCurrentUserUploadAvatar(filePath) { export function postCurrentUserUploadAvatar(filePath) {
return request.upload('/vadmin/auth/user/current/update/avatar/', { return request.upload('/vadmin/auth/user/current/update/avatar', {
filePath: filePath, filePath: filePath,
name: 'file' name: 'file'
}) })

View File

@ -2,15 +2,15 @@ import request from '@/common/request/request.js'
// 获取平台中的常见问题类别列表 // 获取平台中的常见问题类别列表
export function getIssueCategoryList() { export function getIssueCategoryList() {
return request.get('/vadmin/help/issue/categorys/platform/1/') return request.get('/vadmin/help/issue/categorys/platform/1')
} }
// 获取问题详情 // 获取问题详情
export function getIssue(dataId) { export function getIssue(dataId) {
return request.get(`/vadmin/help/issues/${dataId}/`) return request.get(`/vadmin/help/issues/${dataId}`)
} }
// 更新常见问题查看次数+1 // 更新常见问题查看次数+1
export function updateIssueAddViewNumber(dataId) { export function updateIssueAddViewNumber(dataId) {
return request.get(`/vadmin/help/issues/add/view/number/${dataId}/`) return request.get(`/vadmin/help/issues/add/view/number/${dataId}`)
} }

View File

@ -2,5 +2,5 @@ import request from '@/common/request/request.js'
// 获取多个字典类型下的字典元素列表 // 获取多个字典类型下的字典元素列表
export function getDictTypeDetailsApi(data) { export function getDictTypeDetailsApi(data) {
return request.post('/vadmin/system/dict/types/details/', data) return request.post('/vadmin/system/dict/types/details', data)
} }

View File

@ -2,5 +2,5 @@ import request from '@/common/request/request'
// 获取系统基本配置 // 获取系统基本配置
export function getSystemBaseConfigApi() { export function getSystemBaseConfigApi() {
return request.get('/vadmin/system/settings/base/config/') return request.get('/vadmin/system/settings/base/config')
} }