feat 用户基础消息修改, 一些报错修改

This commit is contained in:
xxm
2022-10-31 14:13:23 +08:00
parent de43ea4cd3
commit 9ca8e18f0e
17 changed files with 215 additions and 141 deletions

View File

@@ -5,7 +5,7 @@ VITE_USE_MOCK=true
VITE_PUBLIC_PATH=/
# 跨域代理,您可以配置多个 ,请注意,没有换行符
VITE_PROXY=[["/api","http://localhost:9999"],["/upload","http://localhost:3300/upload"]]
VITE_PROXY=[["/api","http://localhost:9999"],["/upload","http://localhost:9999/file/upload"]]
# 控制台不输出console
VITE_DROP_CONSOLE=false

View File

@@ -1,5 +1,8 @@
import { defHttp } from '/@/utils/http/axios'
import { Result } from '/#/axios'
import { Result, UploadFileParams } from '/#/axios'
import { UploadApiResult } from '/@/api/sys/model/uploadModel'
import { getAppEnvConfig } from '/@/utils/env'
const { VITE_GLOB_API_URL } = getAppEnvConfig()
/**
* 获取文件预览地址
@@ -29,3 +32,34 @@ export const getFileDownloadUrl = (id) => {
params: { id },
})
}
/**
* 上传文件
* @param params
* @param onUploadProgress
*/
export function uploadFile(params: UploadFileParams, onUploadProgress: (progressEvent: ProgressEvent) => void) {
return defHttp.uploadFile<UploadApiResult>(
{
url: VITE_GLOB_API_URL + '/file/upload',
onUploadProgress,
},
params,
)
}
/**
* 上传文件信息
*/
export interface UpdateFileInfo {
// 文件id
id: string
// 文件名称
fileName: string
// 文件后缀
fileSuffix: string
// 文件类型
fileType: string
// 文件大小
fileSize: number
}

View File

@@ -1,11 +1,7 @@
import { defHttp } from '/@/utils/http/axios'
import { getMenuListResultModel, MenuAndResource } from './model/menuModel'
import { MenuAndResource } from './model/menuModel'
import { Result } from '/#/axios'
enum Api {
GetMenuList = '/getMenuList',
}
/**
* 获取菜单和权限码
*/

View File

@@ -16,9 +16,16 @@ export interface LoginParams {
captcha: string
}
export interface RoleInfo {
roleName: string
value: string
/**
* 用户信息(用户详情)
*/
export interface UserDetails {
// 用户id
id: number
// 名称
name: string
// 账号
username: string
}
/**
@@ -34,3 +41,19 @@ export interface GetUserInfoModel {
// 头像
avatar: string
}
/**
* 用户基础消息
*/
export interface UserBaseInfo {
// 用户id
id: number
// 名称
name: string
// 性别
sex: number
// 头像
avatar: string
// 生日
birthday: string
}

View File

@@ -1,13 +1,6 @@
import { defHttp } from '/@/utils/http/axios'
import { LoginParams, GetUserInfoModel } from './model/userModel'
import { ErrorMessageMode, Result } from '/#/axios'
enum Api {
Logout = '/logout',
GetPermCode = '/getPermCode',
TestRetry = '/testRetry',
}
import { LoginParams, GetUserInfoModel, UserBaseInfo, UserDetails } from './model/userModel'
import { Result } from '/#/axios'
/**
* 登录接口 返回token
@@ -18,6 +11,7 @@ export function loginApi(params: LoginParams) {
params,
})
}
/**
* 登录后获取用户信息
*/
@@ -26,14 +20,31 @@ export function getUserInfo() {
}
/**
* 获取用户菜单和资源权限
* 获取用户安全信息
*/
export function getPermissions(clientCode: string) {
return defHttp.get<Result<GetUserInfoModel>>({ url: '/role/menu/getPermissions', params: { clientCode } })
export function getUserSecurityInfo() {
return defHttp.get<Result<UserDetails>>({
url: `/user/getUserSecurityInfo`,
})
}
export function getPermCode() {
return defHttp.get<string[]>({ url: Api.GetPermCode })
/**
* 获取用户基础信息
*/
export function getUserBaseInfo() {
return defHttp.get<Result<UserBaseInfo>>({
url: `/user/getUserBaseInfo`,
})
}
/**
* 更新用户基础信息
*/
export function updateBaseInfo(data) {
return defHttp.post({
url: '/user/updateBaseInfo',
data: data,
})
}
/**
@@ -42,19 +53,3 @@ export function getPermCode() {
export function doLogout() {
return defHttp.post({ url: '/token/logout' })
}
/**
* 测试重试
*/
export function testRetry() {
return defHttp.get(
{ url: Api.TestRetry },
{
retryRequest: {
isOpenRetry: true,
count: 5,
waitTime: 1000,
},
},
)
}

View File

@@ -65,10 +65,15 @@
},
)
function handleUploadSuccess({ source }) {
/**
* 响应
* @param source 文件bold内容
* @param data 上传成功后的文件信息 见 UpdateFileInfo
*/
function handleUploadSuccess({ source, data: { data } }) {
sourceValue.value = source
emit('change', source)
createMessage.success(t('component.cropper.uploadSuccess'))
emit('change', data, source)
createMessage.success('上传成功')
}
expose({ openModal: openModal.bind(null, true), closeModal })

View File

@@ -1,5 +1,5 @@
<template>
<BasicModal :footer="null" :title="t('layout.header.lockScreen')" v-bind="$attrs" :class="prefixCls" @register="register">
<BasicModal :footer="null" title="锁定屏幕" v-bind="$attrs" :class="prefixCls" @register="register">
<div :class="`${prefixCls}__entry`">
<div :class="`${prefixCls}__header`">
<img :src="avatar" :class="`${prefixCls}__header-img`" />
@@ -11,9 +11,7 @@
<BasicForm @register="registerForm" />
<div :class="`${prefixCls}__footer`">
<a-button type="primary" block class="mt-2" @click="handleLock">
{{ t('layout.header.lockScreenBtn') }}
</a-button>
<a-button type="primary" block class="mt-2" @click="handleLock"> 锁定 </a-button>
</div>
</div>
</BasicModal>
@@ -46,7 +44,7 @@
schemas: [
{
field: 'password',
label: t('layout.header.lockScreenPassword'),
label: '锁屏密码',
colProps: {
span: 24,
},

View File

@@ -86,7 +86,7 @@ export function createPermissionGuard(router: Router) {
// get userinfo while last fetch time is empty
if (userStore.getLastUpdateTime === 0) {
try {
await userStore.getUserInfoAction()
await userStore.refreshUserInfoAction()
} catch (err) {
next()
return

View File

@@ -14,10 +14,8 @@ import { router } from '/@/router'
import { usePermissionStore } from '/@/store/modules/permission'
import { RouteRecordRaw } from 'vue-router'
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'
import { isArray } from '/@/utils/is'
import { h } from 'vue'
import { getFilePreviewUrlPrefix } from '/@/api/common/FileUpload'
import headerImg from "/@/assets/images/header.jpg";
interface UserState {
userInfo: Nullable<UserInfo>
@@ -68,6 +66,10 @@ export const useUserStore = defineStore({
this.roleList = roleList
setAuthCache(ROLES_KEY, roleList)
},
/**
* 保存用户信息, 不要直接使用
* 使用 UserStoreUtil 中的包装方法来处理
*/
setUserInfo(info: UserInfo | null) {
this.userInfo = info
this.lastUpdateTime = new Date().getTime()
@@ -102,7 +104,7 @@ export const useUserStore = defineStore({
async afterLoginAction(goHome?: boolean) {
if (!this.getToken) return null
// 获取用户信息
await this.getUserInfoAction()
await this.refreshUserInfoAction()
const sessionTimeout = this.sessionTimeout
// 超时
if (sessionTimeout) {
@@ -122,18 +124,17 @@ export const useUserStore = defineStore({
goHome && (await router.replace(PageEnum.BASE_HOME))
}
},
// 获取并存储用户信息
async getUserInfoAction() {
// 刷新登陆后用户信息
async refreshUserInfoAction() {
if (!this.getToken) return null
const { data: userInfo } = await getUserInfo()
// 设置头像
const { data: urlPrefix } = await getFilePreviewUrlPrefix()
userInfo.avatar = userInfo.avatar ? urlPrefix + userInfo.avatar : ''
this.setUserInfo(userInfo)
return userInfo
},
/**
* @description: logout
* 退出
*/
async logout(goLogin = false) {
if (this.getToken) {

View File

@@ -89,7 +89,6 @@ const transform: AxiosTransform = {
// 请求之前处理config
beforeRequestHook: (config, options) => {
const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options
if (joinPrefix) {
config.url = `${urlPrefix}${config.url}`
}

View File

@@ -5,11 +5,8 @@
</div>
</template>
<script lang="ts" setup>
import { testRetry } from '/@/api/sys/user'
// @ts-ignore
const handleClick = async () => {
await testRetry()
}
const handleClick = async () => {}
</script>
<style lang="less">

View File

@@ -68,7 +68,7 @@
userStore.setToken(token)
// 重新获取用户信息和菜单
userStore.getUserInfoAction()
userStore.refreshUserInfoAction()
permissionStore.changePermissionCode()
}

View File

@@ -44,7 +44,7 @@
userStore.setToken(token)
// 重新获取用户信息和菜单
userStore.getUserInfoAction()
userStore.refreshUserInfoAction()
refreshMenu()
}

View File

@@ -1,91 +1,131 @@
<template>
<CollapseContainer title="基本设置" :canExpan="false">
<a-row :gutter="24">
<a-col :span="14">
<a-form
class="small-from-item"
ref="formRef"
:validate-trigger="['blur', 'change']"
:model="form"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="名称" name="name">
<a-input v-model:value="form.name" :disabled="!edit" placeholder="请输入名称" />
</a-form-item>
</a-form>
<!-- <BasicForm @register="register" />-->
</a-col>
<a-col :span="10">
<div class="change-avatar">
<div class="mb-2">头像</div>
<CropperAvatar
:uploadApi="uploadApi"
:value="avatar"
btnText="更换头像"
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
@change="updateAvatar"
width="150"
/>
</div>
</a-col>
</a-row>
<a-button type="primary" @click="handleSubmit"> 更新基本信息 </a-button>
<a-spin :spinning="confirmLoading">
<a-row :gutter="24">
<a-col :span="14">
<a-form
ref="formRef"
:validate-trigger="['blur', 'change']"
:model="form"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="名称" name="name">
<a-input v-model:value="form.name" :disabled="!edit" placeholder="请输入名称" />
</a-form-item>
<a-form-item label="性别" name="sex">
<a-select style="width: 200px" v-model:value="form.sex" :options="sexList" :disabled="!edit" placeholder="请选择性别" />
</a-form-item>
<a-form-item label="生日" name="birthday">
<a-date-picker placeholder="请选择日期" valueFormat="YYYY-MM-DD" :disabled="!edit" v-model:value="form.birthday" />
</a-form-item>
</a-form>
</a-col>
<a-col :span="10">
<div class="change-avatar">
<div class="mb-2">头像</div>
<CropperAvatar
v-if="edit"
:uploadApi="uploadFile"
:value="avatar"
btnText="上传头像"
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
@change="updateAvatar"
width="150"
/>
<img :src="avatar" style="width: 150px" v-else alt="avatar" />
</div>
</a-col>
</a-row>
<a-button v-if="edit" type="primary" @click="handleOk">更新基础信息</a-button>
<a-button v-else @click="edit = true">编辑基础信息</a-button>
</a-spin>
</CollapseContainer>
</template>
<script lang="ts" setup>
import { Button, Row, Col } from 'ant-design-vue'
import { computed, defineComponent, onMounted, reactive } from 'vue'
import { BasicForm, useForm } from '/@/components/Form/index'
import headerImg from '/@/assets/images/header.jpg'
import { onMounted, reactive } from 'vue'
import { CollapseContainer } from '/@/components/Container'
import { CropperAvatar } from '/@/components/Cropper'
import { useMessage } from '/@/hooks/web/useMessage'
import headerImg from '/@/assets/images/header.jpg'
import { accountInfoApi } from '/@/api/demo/account'
import { baseSetschemas } from './data'
import { useUserStore } from '/@/store/modules/user'
import { uploadApi } from '/@/api/sys/upload'
import { $ref } from 'vue/macros'
import { UserInfo } from '/#/store'
import { Rule } from 'ant-design-vue/lib/form'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { getUserBaseInfo, updateBaseInfo } from '/@/api/sys/user'
import { getFilePreviewUrlPrefix, UpdateFileInfo, uploadFile } from '/@/api/common/FileUpload'
import { UserBaseInfo } from '/@/api/sys/model/userModel'
import { useDict } from '/@/hooks/bootx/useDict'
import { LabeledValue } from 'ant-design-vue/lib/select'
const { createMessage } = useMessage()
const { createMessage, createConfirm } = useMessage()
const userStore = useUserStore()
const { dictDropDownNumber } = useDict()
let sexList = $ref<LabeledValue[]>()
// 表单项标题文字
const labelCol = { sm: { span: 3 } }
// 表单项内容
const wrapperCol = { sm: { span: 12 } }
let formRef = $ref<FormInstance>()
let confirmLoading = $ref(false)
let edit = $ref(false)
let avatar = $ref(headerImg)
let urlPrefix = $ref('')
// 用户信息
let form = $ref({} as UserInfo)
let form = $ref<UserBaseInfo>({
avatar: '',
birthday: '',
id: 0,
name: '',
sex: 0,
})
const rules = reactive({
code: [{ required: true, message: '请输入表单编码' }],
name: [{ required: true, message: '请输入表单名称' }],
} as Record<string, Rule[]>)
// onMounted(async () => {
// const data = await accountInfoApi()
// setFieldsValue(data)
// })
// 头像
const avatar = computed(() => {
const { avatar } = userStore.getUserInfo
return avatar || headerImg
onMounted(async () => {
// 初始化用户信息
await init()
})
// 更新头像
function updateAvatar(src: string) {
const userinfo = userStore.getUserInfo
userinfo.avatar = src
userStore.setUserInfo(userinfo)
// 基础数据
async function init() {
confirmLoading = true
// 初始化菜单
sexList = dictDropDownNumber('Sex')
const { data: userInfo } = await getUserBaseInfo()
// 设置头像
const result = await getFilePreviewUrlPrefix()
urlPrefix = result.data
avatar = userInfo.avatar ? urlPrefix + userInfo.avatar : ''
form = userInfo
confirmLoading = false
}
// 上传头像回调
function updateAvatar(file: UpdateFileInfo) {
console.log(file)
form.avatar = file.id
}
// 更新用户信息
async function handleOk() {
await formRef.validate()
createConfirm({
iconType: 'warning',
title: '警告',
content: '是否更新用户基础信息',
onOk: async () => {
confirmLoading = true
await updateBaseInfo(form)
createMessage.success('更新用户信息成功')
await userStore.refreshUserInfoAction()
confirmLoading = false
},
})
}
function handleSubmit() {}
</script>
<style lang="less" scoped>

View File

@@ -1,6 +1,5 @@
<template>
< forceRender :visible="visible" :maskClosable="true" width="60%" placement="right" :closable="true" @close="visible = false" >
<basic-drawer showFooter v-bind="$attrs" title="字典列表" width="60%" :visible="visible" @close="visible = false">
<basic-drawer forceRender showFooter v-bind="$attrs" title="字典列表" width="60%" :visible="visible" @close="visible = false">
<vxe-toolbar ref="xToolbar" custom :refresh="{ query: queryPage }">
<template #buttons>
<a-space>

View File

@@ -9,16 +9,6 @@
:closable="true"
@close="visible = false"
>
<basic-drawer
forceRender
v-bind="$attrs"
title="任务执行日志"
width="60%"
:maskClosable="false"
:visible="visible"
@close="visible = false"
>
<b-query :query-params="model.queryParam" :fields="fields" @query="queryPage" @reset="resetQueryParams" />
<vxe-toolbar ref="xToolbar" custom :refresh="{ query: queryPage }" />
<vxe-table row-id="id" ref="xTable" :data="pagination.records" :loading="loading">
@@ -49,17 +39,15 @@
</template>
<script lang="ts" setup>
import { nextTick, onMounted, ref } from 'vue'
import { nextTick } from 'vue'
import { $ref } from 'vue/macros'
import { del, page } from './QuartzJobLog.api'
import useTablePage from '/@/hooks/bootx/useTablePage'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { FormEditType } from '/@/enums/formTypeEnum'
import { useMessage } from '/@/hooks/web/useMessage'
import { QueryField } from '/@/components/Bootx/Query/Query'
import { QuartzJob } from './QuartzJob.api'
import BasicDrawer from "/@/components/Drawer/src/BasicDrawer.vue";
// 使用hooks
const { handleTableChange, pageQueryResHandel, resetQueryParams, pagination, pages, model, loading } = useTablePage(queryPage)

3
types/store.d.ts vendored
View File

@@ -1,6 +1,5 @@
import { ErrorTypeEnum } from '/@/enums/exceptionEnum'
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'
import { RoleInfo } from '/@/api/sys/model/userModel'
// Lock screen information
export interface LockInfo {
@@ -40,7 +39,7 @@ export interface UserInfo {
name: string
// 账号
username: string
// 头像图片地址
// 头像图片id
avatar: string
}