This commit is contained in:
ktianc 2022-10-10 16:40:41 +08:00
parent 7e7c6c2340
commit 5e6251880b
19 changed files with 432 additions and 40 deletions

View File

@ -38,7 +38,7 @@ github地址https://gitee.com/ktianc/kinit👩👦👦
- [x] 👨‍🎓用户管理:用户是系统操作者,该功能主要完成系统用户配置。 - [x] 👨‍🎓用户管理:用户是系统操作者,该功能主要完成系统用户配置。
- [ ] 🏡个人主页:配置用户个人信息,密码修改等。 - [x] 🏡个人主页:配置用户个人信息,密码修改等。
- [x] 📚字典管理:对系统中经常使用的一些较为固定的数据进行维护。 - [x] 📚字典管理:对系统中经常使用的一些较为固定的数据进行维护。
@ -48,7 +48,7 @@ github地址https://gitee.com/ktianc/kinit👩👦👦
- [ ] 🗓️操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 - [ ] 🗓️操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
- [ ] 🔒登录认证:目前支持用户使用手机号+密码方式登录。 - [x] 🔒登录认证:目前支持用户使用手机号+密码方式登录。
说明:新建用户密码默认为手机号后六位; 说明:新建用户密码默认为手机号后六位;

View File

@ -35,42 +35,42 @@ export default [
name: 'Github', name: 'Github',
icon: 'akar-icons:github-fill', icon: 'akar-icons:github-fill',
message: 'workplace.introduction', message: 'workplace.introduction',
personal: 'Archer', personal: 'kinit',
time: new Date() time: new Date()
}, },
{ {
name: 'Vue', name: 'Vue',
icon: 'logos:vue', icon: 'logos:vue',
message: 'workplace.introduction', message: 'workplace.introduction',
personal: 'Archer', personal: 'kinit',
time: new Date() time: new Date()
}, },
{ {
name: 'Angular', name: 'Angular',
icon: 'logos:angular-icon', icon: 'logos:angular-icon',
message: 'workplace.introduction', message: 'workplace.introduction',
personal: 'Archer', personal: 'kinit',
time: new Date() time: new Date()
}, },
{ {
name: 'React', name: 'React',
icon: 'logos:react', icon: 'logos:react',
message: 'workplace.introduction', message: 'workplace.introduction',
personal: 'Archer', personal: 'kinit',
time: new Date() time: new Date()
}, },
{ {
name: 'Webpack', name: 'Webpack',
icon: 'logos:webpack', icon: 'logos:webpack',
message: 'workplace.introduction', message: 'workplace.introduction',
personal: 'Archer', personal: 'kinit',
time: new Date() time: new Date()
}, },
{ {
name: 'Vite', name: 'Vite',
icon: 'vscode-icons:file-type-vite', icon: 'vscode-icons:file-type-vite',
message: 'workplace.introduction', message: 'workplace.introduction',
personal: 'Archer', personal: 'kinit',
time: new Date() time: new Date()
} }
] ]

View File

@ -23,3 +23,11 @@ export const getUserApi = (dataId: number): Promise<IResponse> => {
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> => {
return request.post({ url: `/vadmin/auth/user/current/update/info/`, data })
}
export const getCurrentUserInfo = (): Promise<IResponse> => {
return request.get({ url: `/vadmin/auth/user/current/info/` })
}

View File

@ -1,12 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus' import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useCache } from '@/hooks/web/useCache'
import { resetRouter } from '@/router' import { resetRouter } from '@/router'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { useTagsViewStore } from '@/store/modules/tagsView' import { useTagsViewStore } from '@/store/modules/tagsView'
import { useAppStoreWithOut } from '@/store/modules/app' import { useAuthStoreWithOut } from '@/store/modules/auth'
const tagsViewStore = useTagsViewStore() const tagsViewStore = useTagsViewStore()
@ -16,9 +15,9 @@ const prefixCls = getPrefixCls('user-info')
const { t } = useI18n() const { t } = useI18n()
const { wsCache } = useCache() const authStore = useAuthStoreWithOut()
const { replace } = useRouter() const { replace, push } = useRouter()
const loginOut = () => { const loginOut = () => {
ElMessageBox.confirm(t('common.loginOutMessage'), t('common.reminder'), { ElMessageBox.confirm(t('common.loginOutMessage'), t('common.reminder'), {
@ -27,7 +26,7 @@ const loginOut = () => {
type: 'warning' type: 'warning'
}) })
.then(async () => { .then(async () => {
wsCache.clear() authStore.logout()
tagsViewStore.delAllViews() tagsViewStore.delAllViews()
resetRouter() // resetRouter() //
replace('/login') replace('/login')
@ -39,9 +38,11 @@ const toDocument = () => {
window.open('https://element-plus-admin-doc.cn/') window.open('https://element-plus-admin-doc.cn/')
} }
const appStore = useAppStoreWithOut() const toHome = () => {
push('/system/home')
}
const user = wsCache.get(appStore.getUserInfo) const user = authStore.getUser
</script> </script>
<template> <template>
@ -59,7 +60,7 @@ const user = wsCache.get(appStore.getUserInfo)
<template #dropdown> <template #dropdown>
<ElDropdownMenu> <ElDropdownMenu>
<ElDropdownItem> <ElDropdownItem>
<div @click="toDocument">个人主页</div> <div @click="toHome">个人主页</div>
</ElDropdownItem> </ElDropdownItem>
<ElDropdownItem> <ElDropdownItem>
<div @click="toDocument">前端项目文档</div> <div @click="toDocument">前端项目文档</div>

View File

@ -201,7 +201,7 @@ export default {
yield: '产量', yield: '产量',
dynamic: '动态', dynamic: '动态',
push: '推送', push: '推送',
pushCode: 'Archer 推送 代码到 Github', pushCode: 'kinit 推送 代码到 Github',
follow: '关注' follow: '关注'
}, },
formDemo: { formDemo: {

View File

@ -7,10 +7,12 @@ import { useNProgress } from '@/hooks/web/useNProgress'
import { usePermissionStoreWithOut } from '@/store/modules/permission' import { usePermissionStoreWithOut } from '@/store/modules/permission'
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'
const permissionStore = usePermissionStoreWithOut() const permissionStore = usePermissionStoreWithOut()
const appStore = useAppStoreWithOut() const appStore = useAppStoreWithOut()
const authStore = useAuthStoreWithOut()
const { wsCache } = useCache() const { wsCache } = useCache()
@ -27,6 +29,9 @@ router.beforeEach(async (to, from, next) => {
if (to.path === '/login') { if (to.path === '/login') {
next({ path: '/' }) next({ path: '/' })
} else { } else {
if (!authStore.getIsUser) {
await authStore.getUserInfo()
}
if (permissionStore.getIsAddRouters) { if (permissionStore.getIsAddRouters) {
next() next()
return return

View File

@ -4,26 +4,47 @@ import { UserLoginType } from '@/api/login/types'
import { loginApi } from '@/api/login' import { loginApi } from '@/api/login'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useCache } from '@/hooks/web/useCache' import { useCache } from '@/hooks/web/useCache'
import { getCurrentUserInfo } from '@/api/vadmin/auth/user'
const appStore = useAppStore() const appStore = useAppStore()
const { wsCache } = useCache() const { wsCache } = useCache()
export interface UserState {
id?: number
telephone?: string
name?: string
nickname?: string
avatar?: string
gender?: string
roles?: Recordable[]
create_datetime?: string
}
export interface AuthState {
user: UserState
isUser: boolean
}
export const useAuthStore = defineStore({ export const useAuthStore = defineStore({
id: 'auth', id: 'auth',
state: () => { state: (): AuthState => {
return {} return {
user: {},
isUser: false
}
}, },
persist: { persist: {
// 开启持久化存储 // 开启持久化存储
enabled: true, enabled: true
strategies: [ },
{ getters: {
key: 'authStore', getUser(): UserState {
storage: localStorage return this.user
} },
] getIsUser(): boolean {
return this.isUser
}
}, },
getters: {},
actions: { actions: {
async login(formData: UserLoginType) { async login(formData: UserLoginType) {
const res = await loginApi(formData) const res = await loginApi(formData)
@ -31,8 +52,24 @@ export const useAuthStore = defineStore({
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.getUserInfo, res.data.user) wsCache.set(appStore.getUserInfo, res.data.user)
this.user = res.data.user
} }
return res return res
},
logout() {
wsCache.clear()
this.user = {}
},
updateUser(data: UserState) {
this.user.gender = data.gender
this.user.name = data.name
this.user.nickname = data.nickname
wsCache.set(appStore.getUserInfo, this.user)
},
async getUserInfo() {
const res = await getCurrentUserInfo()
wsCache.set(appStore.getUserInfo, res.data)
this.user = res.data
} }
} }
}) })

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { asyncRouterMap, constantRouterMap } from '@/router' import { constantRouterMap } from '@/router'
import { generateRoutesFn1, generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper' import { generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper'
import { store } from '../index' import { store } from '../index'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'

View File

@ -0,0 +1,73 @@
<script setup lang="ts">
import { ElCard, ElRow, ElCol, ElTabs, ElTabPane, ElAvatar } from 'element-plus'
import { ref } from 'vue'
import InfoWrite from './components/InfoWrite.vue'
import PasswordWrite from './components/PasswordWrite.vue'
import { useAuthStoreWithOut } from '@/store/modules/auth'
import avatar from '@/assets/imgs/avatar.jpg'
import { selectDictLabel, DictDetail } from '@/utils/dict'
import { useDictStore } from '@/store/modules/dict'
const activeName = ref('info')
const authStore = useAuthStoreWithOut()
let genderOptions = ref<DictDetail[]>([])
const getOptions = async () => {
const dictStore = useDictStore()
const dictOptions = await dictStore.getDictObj(['sys_vadmin_gender'])
genderOptions.value = dictOptions.sys_vadmin_gender
}
getOptions()
const user = authStore.getUser
</script>
<template>
<ElRow :gutter="20">
<ElCol :xs="24" :sm="12" :md="8">
<ElCard shadow="hover" class="pb-30px">
<div class="text-center">
<ElAvatar :size="80" :src="user.avatar ? user.avatar : avatar" />
<p style="font-size: 24px">{{ user.name }}</p>
</div>
<div class="pl-20px pt-30px">
<div class="leading-relaxed">
<span class="pl-10px w-80px inline-block">姓名:</span>
<span class="pl-10px">{{ user.name }}</span>
</div>
<div class="leading-relaxed">
<span class="pl-10px w-80px inline-block">昵称:</span>
<span class="pl-10px">{{ user.nickname }}</span>
</div>
<div class="leading-relaxed">
<span class="pl-10px w-80px inline-block">手机号:</span>
<span class="pl-10px">{{ user.telephone }}</span>
</div>
<div class="leading-relaxed">
<span class="pl-10px w-80px inline-block">性别:</span>
<span class="pl-10px">{{ selectDictLabel(genderOptions, user.gender as string) }}</span>
</div>
<div class="leading-relaxed">
<span class="pl-10px w-80px inline-block">创建时间:</span>
<span class="pl-10px">{{ user.create_datetime }}</span>
</div>
</div>
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :md="16">
<ElCard shadow="hover">
<ElTabs v-model="activeName">
<ElTabPane label="基本信息" name="info">
<InfoWrite />
</ElTabPane>
<ElTabPane label="修改密码" name="password">
<PasswordWrite />
</ElTabPane>
</ElTabs>
</ElCard>
</ElCol>
</ElRow>
</template>

View File

@ -0,0 +1,120 @@
<script setup lang="ts">
import { reactive, unref, ref } from 'vue'
import { Form } from '@/components/Form'
import { ElButton } from 'element-plus'
import { useForm } from '@/hooks/web/useForm'
import { postCurrentUserUpdateInfo } from '@/api/vadmin/auth/user'
import { useValidator } from '@/hooks/web/useValidator'
import { useAuthStoreWithOut } from '@/store/modules/auth'
import { ElMessage } from 'element-plus'
const { required } = useValidator()
const authStore = useAuthStoreWithOut()
const rules = {
name: [required()],
gender: [required()]
}
const schema = reactive<FormSchema[]>([
{
field: 'name',
label: '用户名称',
component: 'Input',
colProps: {
span: 24
},
componentProps: {
style: {
width: '50%'
}
}
},
{
field: 'nickname',
label: '用户昵称',
component: 'Input',
colProps: {
span: 24
},
componentProps: {
style: {
width: '50%'
}
}
},
{
field: 'gender',
label: '性别',
colProps: {
span: 24
},
component: 'Radio',
componentProps: {
options: [
{
label: '男',
value: '0'
},
{
label: '女',
value: '1'
}
]
}
},
{
field: 'save',
colProps: {
span: 24
}
}
])
const { register, elFormRef, methods } = useForm()
const { setValues } = methods
setValues(authStore.getUser)
const loading = ref(false)
//
const save = async () => {
const formRef = unref(elFormRef)
await formRef?.validate(async (isValid) => {
if (isValid) {
loading.value = true
const { getFormData } = methods
const formData = await getFormData()
try {
const res = await postCurrentUserUpdateInfo(formData)
if (res) {
authStore.updateUser(res.data)
ElMessage.success('保存成功')
}
} finally {
loading.value = false
}
}
})
}
</script>
<template>
<Form
:schema="schema"
:rules="rules"
hide-required-asterisk
class="dark:(border-1 border-[var(--el-border-color)] border-solid)"
@register="register"
>
<template #save>
<div class="w-[50%]">
<ElButton :loading="loading" type="primary" class="w-[100%]" @click="save"> 保存 </ElButton>
</div>
</template>
</Form>
</template>
<style lang="less" scoped></style>

View File

@ -0,0 +1,114 @@
<script setup lang="ts">
import { reactive, unref, ref } from 'vue'
import { Form } from '@/components/Form'
import { ElButton } from 'element-plus'
import { useForm } from '@/hooks/web/useForm'
import { postCurrentUserResetPassword } from '@/api/vadmin/auth/user'
import { useValidator } from '@/hooks/web/useValidator'
import { useAuthStoreWithOut } from '@/store/modules/auth'
import { ElMessage } from 'element-plus'
const { required } = useValidator()
const authStore = useAuthStoreWithOut()
const rules = {
password: [
required(),
{ min: 8, max: 16, message: '长度需为8-16个字符,请重新输入。', trigger: 'blur' }
],
password_two: [
required(),
{ min: 8, max: 16, message: '长度需为8-16个字符,请重新输入。', trigger: 'blur' }
]
}
const schema = reactive<FormSchema[]>([
{
field: 'title',
colProps: {
span: 24
}
},
{
field: 'password',
label: '新密码',
component: 'InputPassword',
colProps: {
span: 24
},
componentProps: {
style: {
width: '50%'
},
placeholder: '请输入新密码'
}
},
{
field: 'password_two',
label: '再次输入新密码',
component: 'InputPassword',
colProps: {
span: 24
},
componentProps: {
style: {
width: '50%'
},
placeholder: '请再次输入新密码'
}
},
{
field: 'save',
colProps: {
span: 24
}
}
])
const { register, elFormRef, methods } = useForm()
const { setValues } = methods
setValues(authStore.getUser)
const loading = ref(false)
//
const save = async () => {
const formRef = unref(elFormRef)
await formRef?.validate(async (isValid) => {
if (isValid) {
loading.value = true
const { getFormData } = methods
const formData = await getFormData()
try {
const res = await postCurrentUserResetPassword(formData)
if (res) {
formRef.resetFields()
ElMessage.success('保存成功')
}
} finally {
loading.value = false
}
}
})
}
</script>
<template>
<Form
:schema="schema"
:rules="rules"
hide-required-asterisk
class="dark:(border-1 border-[var(--el-border-color)] border-solid)"
@register="register"
>
<template #save>
<div class="w-[50%]">
<ElButton :loading="loading" type="primary" class="w-[100%]" @click="save"> 保存 </ElButton>
</div>
</template>
</Form>
</template>
<style lang="less" scoped></style>

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, unref, ref, watch } from 'vue' import { reactive, unref, ref, watch } from 'vue'
import { Form } from '@/components/Form' import { Form } from '@/components/Form'
import { ElButton, ElCheckbox, ElLink } 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 { usePermissionStore } from '@/store/modules/permission'

View File

@ -24,14 +24,12 @@ let menuTypeOptions = ref<DictDetail[]>([])
const getOptions = async () => { const getOptions = async () => {
const dictStore = useDictStore() const dictStore = useDictStore()
const result = await dictStore.getDictObj(['sys_vadmin_menu_type']) const dictOptions = await dictStore.getDictObj(['sys_vadmin_menu_type'])
menuTypeOptions.value = result.sys_vadmin_menu_type menuTypeOptions.value = dictOptions.sys_vadmin_menu_type
} }
getOptions() getOptions()
console.log(menuTypeOptions)
const { register, tableObject, methods } = useTable<TableData>({ const { register, tableObject, methods } = useTable<TableData>({
getListApi: getMenuListApi, getListApi: getMenuListApi,
delListApi: delMenuListApi, delListApi: delMenuListApi,
@ -115,6 +113,7 @@ getList()
:loading="tableObject.loading" :loading="tableObject.loading"
:selection="false" :selection="false"
row-key="id" row-key="id"
default-expand-all
@register="register" @register="register"
> >
<template #title="{ row }"> <template #title="{ row }">

View File

@ -15,9 +15,21 @@ import Write from './components/Write.vue'
import { Dialog } from '@/components/Dialog' import { Dialog } from '@/components/Dialog'
import { ElButton, ElMessage, ElSwitch } from 'element-plus' import { ElButton, ElMessage, ElSwitch } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { selectDictLabel, DictDetail } from '@/utils/dict'
import { useDictStore } from '@/store/modules/dict'
const { t } = useI18n() const { t } = useI18n()
let genderOptions = ref<DictDetail[]>([])
const getOptions = async () => {
const dictStore = useDictStore()
const dictOptions = await dictStore.getDictObj(['sys_vadmin_gender'])
genderOptions.value = dictOptions.sys_vadmin_gender
}
getOptions()
const { register, tableObject, methods } = useTable({ const { register, tableObject, methods } = useTable({
getListApi: getUserListApi, getListApi: getUserListApi,
delListApi: delUserListApi, delListApi: delUserListApi,
@ -124,6 +136,10 @@ getList()
<template #is_active="{ row }"> <template #is_active="{ row }">
<ElSwitch :value="row.is_active" disabled /> <ElSwitch :value="row.is_active" disabled />
</template> </template>
<template #gender="{ row }">
{{ selectDictLabel(genderOptions, row.gender) }}
</template>
</Table> </Table>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="700px"> <Dialog v-model="dialogVisible" :title="dialogTitle" width="700px">

View File

@ -65,8 +65,7 @@ ALLOW_HEADERS = ["*"]
""" """
if DEBUG: if DEBUG:
# 测试库 # 测试库
SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://root:Ktianc123@rm-bp181adf0phw2o0r05o.mysql.rds.aliyuncs.com:3306/kinit" SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://root:123456@127.0.0.1:3306/kinit"
# SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://root:123456@127.0.0.1:3306/kinit"
SQLALCHEMY_DATABASE_TYPE = "mysql" SQLALCHEMY_DATABASE_TYPE = "mysql"
else: else:
# 正式库 # 正式库

View File

@ -57,6 +57,18 @@ class UserDal(DalBase):
await self.db.flush() await self.db.flush()
return True return True
async def update_current_info(self, user: models.VadminUser, data: schemas.UserUpdate):
"""
更新当前用户信息
"""
user.name = data.name
user.nickname = data.nickname
user.gender = data.gender
self.db.add(user)
await self.db.flush()
await self.db.refresh(user)
return self.out_dict(user)
class RoleDal(DalBase): class RoleDal(DalBase):

View File

@ -52,10 +52,7 @@ class UserOut(UserSimpleOut):
class UserUpdate(BaseModel): class UserUpdate(BaseModel):
name: str name: str
nickname: Optional[str] = None nickname: Optional[str] = None
is_active: Optional[bool] = True
is_cancel: Optional[bool] = False
gender: Optional[str] = "0" gender: Optional[str] = "0"
role_ids: Optional[List[int]] = []
class ResetPwd(BaseModel): class ResetPwd(BaseModel):

View File

@ -53,6 +53,7 @@ async def login_for_access_token(request: Request, data: dict = Depends(authenti
"name": user.name, "name": user.name,
"nickname": user.nickname, "nickname": user.nickname,
"avatar": user.avatar, "avatar": user.avatar,
"gender": user.gender,
"roles": [{"name": i.name, "value": i.role_key} for i in user.roles] "roles": [{"name": i.name, "value": i.role_key} for i in user.roles]
} }
} }

View File

@ -37,7 +37,7 @@ async def delete_users(ids: list = Depends(id_list), auth: Auth = Depends(login_
return SuccessResponse("删除成功") return SuccessResponse("删除成功")
@app.put("/users/{data_id}/", summary="更新用户基本信息") @app.put("/users/{data_id}/", summary="更新用户信息")
async def put_user(data_id: int, data: schemas.User, auth: Auth = Depends(login_auth)): async def put_user(data_id: int, data: schemas.User, auth: Auth = Depends(login_auth)):
return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data)) return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data))
@ -55,6 +55,16 @@ async def user_current_reset_password(data: schemas.ResetPwd, auth: Auth = Depen
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="更新当前用户基本信息")
async def post_user_current_update_info( data: schemas.UserUpdate, auth: Auth = Depends(login_auth)):
return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data))
@app.get("/user/current/info/", summary="获取当前用户基本信息")
async def get_user_current_info(auth: Auth = Depends(login_auth)):
return SuccessResponse(schemas.UserSimpleOut.from_orm(auth.user).dict())
########################################################### ###########################################################
# 角色管理 # 角色管理
########################################################### ###########################################################