版本升级:

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'
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[]>> => {
return request.get({ url: '/vadmin/analysis/user/access/source/' })
return request.get({ url: '/vadmin/analysis/user/access/source' })
}
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[]>> => {
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'
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'
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>> => {
return request.get({ url: '/vadmin/workplace/project/' })
return request.get({ url: '/vadmin/workplace/project' })
}
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[]>> => {
return request.get({ url: '/vadmin/workplace/team/' })
return request.get({ url: '/vadmin/workplace/team' })
}
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[]>> => {
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'
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[]>> => {
return request.get({ url: '/auth/getMenuList/' })
return request.get({ url: '/auth/getMenuList' })
}
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'
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> => {
return request.delete({ url: '/vadmin/auth/menus/', data })
return request.delete({ url: '/vadmin/auth/menus', data })
}
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> => {
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> => {
return request.get({ url: '/vadmin/auth/menus/tree/options/' })
return request.get({ url: '/vadmin/auth/menus/tree/options' })
}
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'
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> => {
return request.post({ url: '/vadmin/auth/roles/', data })
return request.post({ url: '/vadmin/auth/roles', data })
}
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> => {
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> => {
return request.get({ url: `/vadmin/auth/roles/${dataId}/` })
return request.get({ url: `/vadmin/auth/roles/${dataId}` })
}
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'
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> => {
return request.post({ url: '/vadmin/auth/users/', data })
return request.post({ url: '/vadmin/auth/users', data })
}
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> => {
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> => {
return request.get({ url: `/vadmin/auth/users/${dataId}/` })
return request.get({ url: `/vadmin/auth/users/${dataId}` })
}
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> => {
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> => {
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> => {
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> => {
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> => {
return request.post({
url: `/vadmin/auth/import/users/`,
url: `/vadmin/auth/import/users`,
headersType: 'multipart/form-data',
data
})
}
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> => {
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> => {
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> => {
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> => {
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> => {
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> => {
return request.get({ url: `/vadmin/help/issue/categorys/${dataId}/` })
return request.get({ url: `/vadmin/help/issue/categorys/${dataId}` })
}
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> => {
return request.get({ url: '/vadmin/help/issues/', params })
return request.get({ url: '/vadmin/help/issues', params })
}
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> => {
return request.delete({ url: '/vadmin/help/issues/', data })
return request.delete({ url: '/vadmin/help/issues', data })
}
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> => {
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'
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> => {
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> => {
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> => {
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> => {
return request.get({ url: `/vadmin/system/dict/types/${dataId}/` })
return request.get({ url: `/vadmin/system/dict/types/${dataId}` })
}
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> => {
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> => {
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> => {
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> => {
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> => {
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> => {
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> => {
return request.post({
url: `/vadmin/system/files/`,
url: `/vadmin/system/files`,
headersType: 'multipart/form-data',
data
})

View File

@ -1,5 +1,5 @@
import request from '@/config/axios'
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'
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'
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> => {
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> => {
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> => {
return request.get({ url: '/vadmin/system/settings/base/config/' })
return request.get({ url: '/vadmin/system/settings/base/config' })
}
// 获取系统隐私协议
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> => {
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 { ref, watch, computed, unref, defineComponent, TransitionGroup } from 'vue'
import { useRouter } from 'vue-router'
import { usePermissionStore } from '@/store/modules/permission'
import { useRouterStore } from '@/store/modules/router'
import { filterBreadcrumb } from './helper'
import { filter, treeToList } from '@/utils/tree'
import type { RouteLocationNormalizedLoaded, RouteMeta } from 'vue-router'
@ -29,10 +29,10 @@ export default defineComponent({
const levelList = ref<AppRouteRecordRaw[]>([])
const permissionStore = usePermissionStore()
const routerStore = useRouterStore()
const menuRouters = computed(() => {
const routers = permissionStore.getRouters
const routers = routerStore.getRouters
return filterBreadcrumb(routers)
})

View File

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

View File

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

View File

@ -2,7 +2,7 @@
import { onMounted, watch, computed, unref, ref, nextTick } from 'vue'
import { useRouter } 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 { useAppStore } from '@/store/modules/app'
import { useI18n } from '@/hooks/web/useI18n'
@ -21,9 +21,9 @@ const { t } = useI18n()
const { currentRoute, push, replace } = useRouter()
const permissionStore = usePermissionStore()
const routerStore = useRouterStore()
const routers = computed(() => permissionStore.getRouters)
const routers = computed(() => routerStore.getRouters)
const tagsViewStore = useTagsViewStore()
@ -108,14 +108,14 @@ const toLastView = () => {
push(latestView)
} else {
if (
unref(currentRoute).path === permissionStore.getAddRouters[0].path ||
unref(currentRoute).path === permissionStore.getAddRouters[0].redirect
unref(currentRoute).path === routerStore.getAddRouters[0].path ||
unref(currentRoute).path === routerStore.getAddRouters[0].redirect
) {
addTags()
return
}
// 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) => {
let { message } = error
const authStore = useAuthStore()
const status = error.response?.status
switch (status) {
case 400:
@ -112,12 +113,13 @@ service.interceptors.response.use(
break
case 401:
// 强制要求重新登录因账号已冻结账号已过期手机号码错误刷新token无效等问题导致
const authStore = useAuthStore()
authStore.logout()
message = '认证已过期,请重新登录'
break
case 403:
message = '拒绝访问'
// 强制要求重新登录,因无系统权限,而进入到系统访问等问题导致
authStore.logout()
message = '无权限访问,请联系管理员'
break
case 404:
message = `请求地址出错: ${error.response?.config.url}`

View File

@ -1,18 +1,16 @@
import type { App, Directive, DirectiveBinding } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useCache } from '@/hooks/web/useCache'
import { intersection } from 'lodash-es'
import { isArray } from '@/utils/is'
import { useAppStoreWithOut } from '@/store/modules/app'
import { useAuthStoreWithOut } from '@/store/modules/auth'
const { t } = useI18n()
const { wsCache } = useCache()
const appStore = useAppStoreWithOut()
const authStore = useAuthStoreWithOut()
// 全部权限
const all_permission = ['*.*.*']
const hasPermission = (value: string | string[]): boolean => {
const permissions = wsCache.get(appStore.getUserInfo).permissions as string[]
const permissions = authStore.getPermissions
if (!value) {
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 { useI18n } from '@/hooks/web/useI18n'
import { TableSetPropsType } from '@/types/table'
import { isEmpty } from '@/utils/is'
const { t } = useI18n()
@ -34,7 +35,7 @@ interface TableObject<T = any> {
tableData: T[]
params: any
loading: boolean
currentRow: Nullable<T>
currentRow: Recordable | null
}
type TableOrderChange = {
@ -111,7 +112,7 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
return table
}
const delData = async (ids: string[] | number[]) => {
const delData = async (ids: string[] | number[] | number) => {
if (config?.delListApi) {
const res = await config.delListApi(ids)
if (res) {
@ -166,29 +167,47 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
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()
if (multiple) {
if (!tableRef?.selections.length) {
ElMessage.warning(t('common.delNoData'))
return
let value: string[] | number[] | number = []
if (isEmpty(ids)) {
if (multiple) {
if (!tableRef?.selections.length) {
ElMessage.warning(t('common.delNoData'))
return
} else {
value = tableRef?.selections.map((item) => item.id)
}
} else {
if (!tableObject.currentRow) {
ElMessage.warning(t('common.delNoData'))
return
} else {
value = tableObject.currentRow.id
}
}
} else {
if (!tableObject.currentRow) {
ElMessage.warning(t('common.delNoData'))
return
}
value = ids
}
if (message) {
ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), {
confirmButtonText: t('common.delOk'),
cancelButtonText: t('common.delCancel'),
type: 'warning'
}).then(async () => {
await delData(ids)
await delData(value)
})
} 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 { useTitle } from '@/hooks/web/useTitle'
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 { getRoleMenusApi } from '@/api/login'
import { useAuthStoreWithOut } from '@/store/modules/auth'
const permissionStore = usePermissionStoreWithOut()
const Routertore = useRouterStoreWithOut()
const appStore = useAppStoreWithOut()
const authStore = useAuthStoreWithOut()
@ -25,16 +25,16 @@ const whiteList = ['/login', '/docs/privacy', '/docs/agreement'] // 不重定向
router.beforeEach(async (to, from, next) => {
start()
loadStart()
if (wsCache.get(appStore.getUserInfo)) {
if (wsCache.get(appStore.getToken)) {
if (to.path === '/login') {
next({ path: '/' })
} else if (to.path === '/reset/password') {
next()
} else {
if (!authStore.getIsUser) {
await authStore.getUserInfo()
await authStore.setUserInfo()
}
if (permissionStore.getIsAddRouters) {
if (Routertore.getIsAddRouters) {
next()
return
}
@ -44,14 +44,14 @@ router.beforeEach(async (to, from, next) => {
const { wsCache } = useCache()
const routers = res.data || []
wsCache.set('roleRouters', routers)
await permissionStore.generateRoutes(routers).catch(() => {})
permissionStore.getAddRouters.forEach((route) => {
await Routertore.generateRoutes(routers).catch(() => {})
Routertore.getAddRouters.forEach((route) => {
router.addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
})
const redirectPath = from.query.redirect || to.path
const redirect = decodeURIComponent(redirectPath as string)
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect }
permissionStore.setIsAddRouters(true)
Routertore.setIsAddRouters(true)
next(nextData)
}
} else {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -65,12 +65,8 @@ const updateAction = async (row: any) => {
//
const delData = async (row: any) => {
tableObject.currentRow = row
const { delListApi } = methods
loading.value = true
await delListApi([row.id], false).finally(() => {
loading.value = false
})
await delListApi(true, [row.id])
}
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: {
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
loading.value = true
const selections = ref([] as any[])
if (multiple) {
if (row) {
selections.value = [row.id]
} else {
selections.value = await getSelections()
selections.value = selections.value.map((item) => item.id)
} else {
tableObject.currentRow = row
selections.value = [row.id]
}
await delListApi(selections.value, multiple).finally(() => {
loading.value = false
})
await delListApi(true, selections.value)
}
const writeRef = ref<ComponentRef<typeof Write>>()
@ -224,7 +223,7 @@ const handleCommand = (command: string) => {
} else if (command === 'd') {
sendPasswordToEmail()
} else if (command === 'e') {
delDatas(null, true)
delDatas(null)
}
}
</script>
@ -256,7 +255,7 @@ const handleCommand = (command: string) => {
<ElButton @click="sendPasswordToEmail">重置密码通知邮件</ElButton>
</ElCol>
<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 :span="1.5" v-if="mobile">
<ElDropdown trigger="click" @command="handleCommand">
@ -325,7 +324,7 @@ const handleCommand = (command: string) => {
v-hasPermi="['auth.user.delete']"
link
size="small"
@click="delDatas(row, false)"
@click="delDatas(row)"
v-if="authStore.getUser.id !== row.id && row.id !== 1"
>
{{ t('exampleDemo.del') }}

View File

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

View File

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

View File

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

View File

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

View File

@ -83,7 +83,7 @@ python main.py init
python main.py run
```
## 其他
## 其他操作
在线文档地址(在配置文件里面设置路径或者关闭)
@ -92,6 +92,7 @@ http://127.0.0.1:9000/docs
```
Git更新ignore文件直接修改gitignore是不会生效的需要先去掉已经托管的文件修改完成之后再重新添加并提交。
```
第一步:
git rm -r --cached .
@ -115,4 +116,173 @@ python main.py migrate
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
@ -20,11 +20,11 @@ DEBUG = True
DEMO = not DEBUG
"""演示功能白名单"""
DEMO_WHITE_LIST_PATH = [
"/auth/login/",
"/auth/token/refresh/",
"/auth/wx/login/",
"/vadmin/system/dict/types/details/",
"/vadmin/auth/user/export/query/list/to/excel/"
"/auth/login",
"/auth/token/refresh",
"/auth/wx/login",
"/vadmin/system/dict/types/details",
"/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())):
data = [
{
@ -33,7 +33,7 @@ async def get_banners(auth: Auth = Depends(AllUserAuth())):
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())):
data = [
{"value": 1000, "name": 'analysis.directAccess'},
@ -45,7 +45,7 @@ async def get_user_access_source(auth: Auth = Depends(AllUserAuth())):
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())):
data = [
{"value": 13253, "name": 'analysis.monday'},
@ -59,7 +59,7 @@ async def get_weekly_user_activity(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(data)
@app.get("/monthly/sales/", summary="每月销售额")
@app.get("/monthly/sales", summary="每月销售额")
async def get_monthly_sales(auth: Auth = Depends(AllUserAuth())):
data = [
{"estimate": 100, "actual": 120, "name": 'analysis.january'},

View File

@ -59,7 +59,7 @@ class VadminUser(BaseModel):
self.last_login = datetime.datetime.now()
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.post("/login/", summary="手机号密码登录", description="员工登录通道限制最多输错次数达到最大值后将is_active=False")
@app.post("/login", summary="手机号密码登录", description="员工登录通道限制最多输错次数达到最大值后将is_active=False")
async def login_for_access_token(
request: Request,
data: LoginForm,
@ -74,7 +74,7 @@ async def login_for_access_token(
return ErrorResponse(msg=str(e))
@app.post("/wx/login/", summary="微信服务端一键登录", description="员工登录通道")
@app.post("/wx/login", summary="微信服务端一键登录", description="员工登录通道")
async def wx_login_for_access_token(
request: Request,
data: WXLoginForm,
@ -116,12 +116,12 @@ async def wx_login_for_access_token(
return SuccessResponse(resp)
@app.get("/getMenuList/", summary="获取当前用户菜单树")
@app.get("/getMenuList", summary="获取当前用户菜单树")
async def get_menu_list(auth: Auth = Depends(FullAdminAuth())):
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")):
error_code = status.HTTP_401_UNAUTHORIZED
try:

View File

@ -11,6 +11,7 @@ from fastapi import Request
from application import settings
import jwt
from apps.vadmin.auth import models
from core.database import redis_getter
from .validation import LoginValidation, LoginForm, LoginResult
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)
result = await sms.check_sms_code(data.password)
if result:

View File

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

View File

@ -11,6 +11,7 @@ from pydantic import BaseModel, validator
from sqlalchemy.ext.asyncio import AsyncSession
from application.settings import DEFAULT_AUTH_ERROR_MAX_NUMBER, DEMO
from apps.vadmin.auth import crud, schemas
from core.database import redis_getter
from core.validator import vali_telephone
from typing import Optional
from utils.count import Count
@ -64,7 +65,7 @@ class LoginValidation:
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 = Count(request.app.state.redis, count_key)
count = Count(redis_getter(request), count_key)
if not result.status:
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(
params: UserParams = Depends(),
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.list"]))
@ -36,12 +36,12 @@ async def get_users(
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"]))):
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"]))):
if auth.user.id in ids.ids:
return ErrorResponse("不能删除当前登录用户")
@ -51,7 +51,7 @@ async def delete_users(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu
return SuccessResponse("删除成功")
@app.put("/users/{data_id}/", summary="更新用户信息")
@app.put("/users/{data_id}", summary="更新用户信息")
async def put_user(
data_id: int,
data: schemas.UserUpdate,
@ -60,7 +60,7 @@ async def put_user(
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(
data_id: int,
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))
@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())):
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())):
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())):
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())):
result = schemas.UserOut.from_orm(auth.user).dict()
result["permissions"] = list(FullAdminAuth.get_user_permissions(auth.user))
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(
header: list = Body(..., title="表头与对应字段"),
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))
@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())):
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"]))):
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(
request: Request,
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))
@app.post("/users/init/password/send/email/", summary="初始化所选用户密码并发送通知邮件")
@app.post("/users/init/password/send/email", summary="初始化所选用户密码并发送通知邮件")
async def post_users_init_password_send_email(
request: Request,
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))
@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)):
result = await crud.UserDal(auth.db).update_wx_server_openid(code, auth.user, rd)
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(
params: RoleParams = Depends(),
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.list"]))
@ -151,12 +151,12 @@ async def get_roles(
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"]))):
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"]))):
if 1 in ids.ids:
return ErrorResponse("不能删除管理员角色")
@ -164,7 +164,7 @@ async def delete_roles(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu
return SuccessResponse("删除成功")
@app.put("/roles/{data_id}/", summary="更新角色信息")
@app.put("/roles/{data_id}", summary="更新角色信息")
async def put_role(
data_id: int,
data: schemas.RoleIn,
@ -175,12 +175,12 @@ async def put_role(
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"]))):
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(
data_id: int,
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"]))):
datas = await crud.MenuDal(auth.db).get_tree_list(mode=1)
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"]))):
datas = await crud.MenuDal(auth.db).get_tree_list(mode=2)
return SuccessResponse(datas)
@app.get("/menus/role/tree/options/", summary="获取菜单列表树信息,角色权限使用")
@app.get("/menus/role/tree/options", summary="获取菜单列表树信息,角色权限使用")
async def get_menus_treeselect(
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))
):
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"]))):
if menu.parent_id:
menu.alwaysShow = False
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"]))):
await crud.MenuDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功")
@app.put("/menus/{data_id}/", summary="更新菜单信息")
@app.put("/menus/{data_id}", summary="更新菜单信息")
async def put_menus(
data_id: int,
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))
@app.get("/menus/{data_id}/", summary="获取菜单信息")
@app.get("/menus/{data_id}", summary="获取菜单信息")
async def put_menus(
data_id: int,
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))
@app.get("/role/menus/tree/{role_id}/", summary="获取菜单列表树信息以及角色菜单权限ID角色权限使用")
@app.get("/role/menus/tree/{role_id}", summary="获取菜单列表树信息以及角色菜单权限ID角色权限使用")
async def get_role_menu_tree(
role_id: int,
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))

View File

@ -5,11 +5,13 @@
# @File : crud.py
# @IDE : PyCharm
# @desc : 帮助中心 - 增删改查
from typing import List
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload
from core.crud import DalBase
from . import models, schemas
from apps.vadmin.auth import models as vadminAuthModels
class IssueDal(DalBase):
@ -30,5 +32,34 @@ class IssueDal(DalBase):
class IssueCategoryDal(DalBase):
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')
user_id = Column(ForeignKey("vadmin_auth_user.id", ondelete='SET NULL'), comment="创建人")
user = relationship("VadminUser", foreign_keys=user_id)
create_user_id = Column(ForeignKey("vadmin_auth_user.id", ondelete='SET NULL'), comment="创建人")
create_user = relationship("VadminUser", foreign_keys=create_user_id)
class VadminIssue(BaseModel):
@ -37,6 +37,6 @@ class VadminIssue(BaseModel):
view_number = Column(Integer, default=0, comment="查看次数")
is_active = Column(Boolean, default=True, comment="是否可见")
user_id = Column(ForeignKey("vadmin_auth_user.id", ondelete='SET NULL'), comment="创建人")
user = relationship("VadminUser", foreign_keys=user_id)
create_user_id = Column(ForeignKey("vadmin_auth_user.id", ondelete='SET NULL'), comment="创建人")
create_user = relationship("VadminUser", foreign_keys=create_user_id)

View File

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

View File

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

View File

@ -16,7 +16,7 @@ class IssueCategoryPlatformOut(BaseModel):
name: Optional[str] = None
platform: Optional[str] = None
is_active: Optional[bool] = None
user_id: Optional[int] = None
create_user_id: Optional[int] = None
id: int
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())):
model = models.VadminIssueCategory
options = [joinedload(model.user)]
options = [joinedload(model.create_user)]
schema = schemas.IssueCategoryListOut
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())
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())):
schema = schemas.IssueCategoryOptionsOut
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())):
data.user_id = auth.user.id
data.create_user_id = auth.user.id
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())):
await crud.IssueCategoryDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
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())):
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())):
schema = schemas.IssueCategorySimpleOut
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)):
model = models.VadminIssueCategory
options = [joinedload(model.issues)]
schema = schemas.IssueCategoryPlatformOut
result = await crud.IssueCategoryDal(db).\
get_datas(platform=platform, is_active=True, v_schema=schema, v_options=options)
result = await crud.IssueCategoryDal(db).get_datas(
platform=platform,
is_active=True,
v_schema=schema,
v_options=options
)
return SuccessResponse(result)
###########################################################
# 问题管理
###########################################################
@app.get("/issues/", summary="获取问题列表")
@app.get("/issues", summary="获取问题列表")
async def get_issues(p: params.IssueParams = Depends(), auth: Auth = Depends(AllUserAuth())):
model = models.VadminIssue
options = [joinedload(model.user), joinedload(model.category)]
options = [joinedload(model.create_user), joinedload(model.category)]
schema = schemas.IssueListOut
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())
return SuccessResponse(datas, count=count)
@app.post("/issues/", summary="创建问题")
@app.post("/issues", summary="创建问题")
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))
@app.delete("/issues/", summary="批量删除问题", description="硬删除")
@app.delete("/issues", summary="批量删除问题", description="硬删除")
async def delete_issues(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.IssueDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
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())):
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)):
schema = schemas.IssueSimpleOut
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)):
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())):
datas = await crud.LoginRecordDal(auth.db).get_datas(**p.dict())
count = await crud.LoginRecordDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count)
@app.get("/operations/", summary="获取操作日志列表")
@app.get("/operations", summary="获取操作日志列表")
async def get_record_operation(
p: OperationParams = Depends(),
db: DatabaseManage = Depends(get_database),
@ -37,7 +37,7 @@ async def get_record_operation(
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())):
datas = await crud.SMSSendRecordDal(auth.db).get_datas(**p.dict())
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())):
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:
continue
# 将上传的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)
await self.db.execute(sql)
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())):
datas = await crud.DictTypeDal(auth.db).get_datas(**p.dict())
count = await crud.DictTypeDal(auth.db).get_count(**p.to_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())):
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())):
await crud.DictTypeDal(auth.db).delete_datas(ids=ids.ids)
return SuccessResponse("删除成功")
@app.post("/dict/types/details/", summary="获取多个字典类型下的字典元素列表")
@app.post("/dict/types/details", summary="获取多个字典类型下的字典元素列表")
async def post_dicts_details(
auth: Auth = Depends(AllUserAuth()),
dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表")
@ -55,17 +55,17 @@ async def post_dicts_details(
return SuccessResponse(datas)
@app.get("/dict/types/options/", summary="获取字典类型选择项")
@app.get("/dict/types/options", summary="获取字典类型选择项")
async def get_dicts_options(auth: Auth = Depends(AllUserAuth())):
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())):
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())):
schema = schemas.DictTypeSimpleOut
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())):
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())):
if not params.dict_type_id:
return ErrorResponse(msg="未获取到字典类型!")
@ -88,18 +88,18 @@ async def get_dict_details(params: DictDetailParams = Depends(), auth: Auth = De
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())):
await crud.DictDetailsDal(auth.db).delete_datas(ids.ids, v_soft=False)
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())):
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())):
schema = schemas.DictDetailsSimpleOut
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(...)):
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_image(path, file)
if not result:
@ -116,7 +116,7 @@ async def upload_image_to_oss(file: UploadFile, path: str = Form(...)):
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(...)):
manage = FileManage(file, path)
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)):
sms = AliyunSMS(rd, telephone)
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())):
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())):
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)):
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)):
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())):
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())):
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())):
data = {
"project": 40,
@ -29,7 +29,7 @@ async def get_total(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(data)
@app.get("/project/", summary="获取项目")
@app.get("/project", summary="获取项目")
async def get_project():
data = [
{
@ -84,7 +84,7 @@ async def get_project():
return SuccessResponse(data)
@app.get("/dynamic/", summary="获取动态")
@app.get("/dynamic", summary="获取动态")
async def get_dynamic():
data = [
{
@ -99,7 +99,7 @@ async def get_dynamic():
return SuccessResponse(data)
@app.get("/team/", summary="获取团队信息")
@app.get("/team", summary="获取团队信息")
async def get_team():
data = [
{
@ -130,7 +130,7 @@ async def get_team():
return SuccessResponse(data)
@app.get("/shortcuts/", summary="获取快捷操作")
@app.get("/shortcuts", summary="获取快捷操作")
async def get_shortcuts():
data = [
{

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
# @desc : 缓存
from typing import List
from core import logger
from core.logger import logger
from core.database import db_getter
from apps.vadmin.system.crud import SettingsTabDal
import json
@ -18,7 +18,7 @@ from utils import status
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):
self.rd = rd

View File

@ -4,15 +4,17 @@
# @File : file_manage.py
# @IDE : PyCharm
# @desc : 保存图片到本地
import asyncio
import datetime
import os
import shutil
from application.settings import TEMP_DIR, STATIC_ROOT, BASE_DIR, STATIC_URL, STATIC_DIR
from fastapi import UploadFile
import sys
from pathlib import Path
from core.exception import CustomException
from utils.file.file_base import FileBase
from aiopathlib import AsyncPath as Path
from aiopathlib import AsyncPath
import aioshutil
@ -41,8 +43,7 @@ class FileManage(FileBase):
path = self.path
if sys.platform == "win32":
path = self.path.replace("/", "\\")
# save_path = os.path.join(STATIC_ROOT, path)
save_path = Path(STATIC_ROOT) / path
save_path = AsyncPath(STATIC_ROOT) / path
if not await save_path.parent.exists():
await save_path.parent.mkdir(parents=True, exist_ok=True)
await save_path.write_bytes(await self.file.read())
@ -52,21 +53,20 @@ class FileManage(FileBase):
}
@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")
# file_dir = os.path.join(TEMP_DIR, date)
file_dir = Path(TEMP_DIR) / date
file_dir = AsyncPath(TEMP_DIR) / date
if not await file_dir.exists():
await file_dir.mkdir(parents=True, exist_ok=True)
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)
@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":
src = src.replace("/", "\\")
dst = dst.replace("/", "\\")
src = os.path.join(BASE_DIR, src)
if not os.path.exists(os.path.dirname(dst)):
os.mkdir(os.path.dirname(dst))
src = Path(BASE_DIR) / src
dst = Path(dst)
if not src.exists():
raise CustomException("源文件不存在!")
if not dst.parent.exists():
dst.parent.mkdir(parents=True, exist_ok=True)
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] == "/":
src = src.lstrip("/")
if sys.platform == "win32":
src = src.replace("/", "\\")
dst = dst.replace("/", "\\")
src = Path(BASE_DIR) / src
dst = Path(dst)
src = AsyncPath(BASE_DIR) / src
if not await src.exists():
raise CustomException("源文件不存在!")
dst = AsyncPath(dst)
if not await dst.parent.exists():
await dst.parent.mkdir(parents=True, exist_ok=True)
await aioshutil.copyfile(src, dst)
if __name__ == '__main__':
# src = r"D:\ktianc\private\vv-reserve\reserve-api\static\system\2022-12-07\16703958210ab33912.ico"
# dst = r"D:\ktianc\private\vv-reserve\reserve-api\static\system\favicon.ico"
# shutil.copyfile(src, dst)
pass
_src = r"D:\programming\ktianc\project\kinit-pro\kinit-api\static\system\favicon.ico"
_dst = r"D:\programming\ktianc\project\kinit-pro\kinit-api\static\system\2022-12-07\16703958210ab33912.ico"
asyncio.run(FileManage.async_copy(_src, _dst))
# FileManage.copy(_src, _dst)

View File

@ -8,18 +8,18 @@ export function login(telephone, password) {
method: '0',
platform: '1'
}
return request.post('/auth/login/', data)
return request.post('/auth/login', data)
}
// 获取用户详细信息
export function getInfo() {
return request.get('/vadmin/auth/user/admin/current/info/')
return request.get('/vadmin/auth/user/admin/current/info')
}
// 更新用户openid
export function setUserOpenid(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',
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) {
return request.post('/vadmin/auth/user/current/update/info/', data)
return request.post('/vadmin/auth/user/current/update/info', 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) {
return request.upload('/vadmin/auth/user/current/update/avatar/', {
return request.upload('/vadmin/auth/user/current/update/avatar', {
filePath: filePath,
name: 'file'
})

View File

@ -2,15 +2,15 @@ import request from '@/common/request/request.js'
// 获取平台中的常见问题类别列表
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) {
return request.get(`/vadmin/help/issues/${dataId}/`)
return request.get(`/vadmin/help/issues/${dataId}`)
}
// 更新常见问题查看次数+1
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) {
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() {
return request.get('/vadmin/system/settings/base/config/')
return request.get('/vadmin/system/settings/base/config')
}