chore: update

This commit is contained in:
xiaoxian521 2024-04-12 16:12:12 +08:00
parent 1d4b5854c6
commit d66e1aef8e
17 changed files with 208 additions and 124 deletions

View File

@ -10,7 +10,9 @@ export default defineFakeRoute([
return {
success: true,
data: {
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "小铭",
// 一个用户可能有多个角色
roles: ["admin"],
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
@ -22,8 +24,9 @@ export default defineFakeRoute([
return {
success: true,
data: {
avatar: "https://avatars.githubusercontent.com/u/52823142",
username: "common",
// 一个用户可能有多个角色
nickname: "小林",
roles: ["common"],
accessToken: "eyJhbGciOiJIUzUxMiJ9.common",
refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh",

View File

@ -12,7 +12,7 @@ export default defineFakeRoute([
data: {
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "Coder",
nickname: "小铭",
email: "pureadmin@163.com",
phone: "15888886789",
description: "一个热爱开源的前端工程师"

View File

@ -9,9 +9,9 @@ export default defineFakeRoute([
response: ({ body }) => {
let list = [
{
username: "admin",
nickname: "admin",
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "小铭",
phone: "15888886789",
email: faker.internet.email(),
sex: 0,
@ -27,9 +27,9 @@ export default defineFakeRoute([
createTime: 1605456000000
},
{
username: "common",
nickname: "common",
avatar: "https://avatars.githubusercontent.com/u/52823142",
username: "common",
nickname: "小林",
phone: "18288882345",
email: faker.internet.email(),
sex: 1,
@ -397,6 +397,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -420,6 +421,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -443,6 +445,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -466,6 +469,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -489,6 +493,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -512,6 +517,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -535,6 +541,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -558,6 +565,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -581,6 +589,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -604,6 +613,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -627,6 +637,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -651,6 +662,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -674,6 +686,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -697,6 +710,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -720,6 +734,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -743,6 +758,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -766,6 +782,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -790,6 +807,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -813,6 +831,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -836,6 +855,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -859,6 +879,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -882,6 +903,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -906,6 +928,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -929,6 +952,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -952,6 +976,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -975,6 +1000,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -998,6 +1024,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -1022,6 +1049,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -1045,6 +1073,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -1068,6 +1097,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: false,
showParent: false
},
@ -1091,6 +1121,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: false,
showParent: false
}
@ -1472,6 +1503,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -1495,6 +1527,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -1518,6 +1551,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -1541,6 +1575,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@ -1564,6 +1599,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
}

View File

@ -0,0 +1,7 @@
import reCropperPreview from "./src/index.vue";
import { withInstall } from "@pureadmin/utils";
/** 图片裁剪预览组件 */
export const ReCropperPreview = withInstall(reCropperPreview);
export default ReCropperPreview;

View File

@ -3,6 +3,10 @@ import { ref } from "vue";
import ReCropper from "@/components/ReCropper";
import { formatBytes } from "@pureadmin/utils";
defineOptions({
name: "ReCropperPreview"
});
const props = defineProps({
imgSrc: String
});

View File

@ -11,6 +11,10 @@ import { isFunction } from "@pureadmin/utils";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
defineOptions({
name: "ReDialog"
});
const fullscreen = ref(false);
const footerButtons = computed(() => {

View File

@ -2,6 +2,10 @@
import flippers from "./filpper";
import { ref, unref, nextTick, onUnmounted } from "vue";
defineOptions({
name: "ReFlop"
});
const timer = ref(null);
const flipObjs = ref([]);

View File

@ -2,6 +2,10 @@
import { h, onMounted, ref, useSlots } from "vue";
import { type TippyOptions, useTippy } from "vue-tippy";
defineOptions({
name: "ReText"
});
const props = defineProps({
//
lineClamp: {

View File

@ -187,7 +187,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
}
.logout {
max-width: 120px;
width: 120px;
::v-deep(.el-dropdown-menu__item) {
display: inline-flex;

View File

@ -160,7 +160,7 @@ nextTick(() => {
}
.logout {
max-width: 120px;
width: 120px;
::v-deep(.el-dropdown-menu__item) {
display: inline-flex;

View File

@ -193,7 +193,7 @@ watch(
}
.logout {
max-width: 120px;
width: 120px;
::v-deep(.el-dropdown-menu__item) {
display: inline-flex;

View File

@ -2,16 +2,16 @@ import { storeToRefs } from "pinia";
import { getConfig } from "@/config";
import { useRouter } from "vue-router";
import { emitter } from "@/utils/mitt";
import userAvatar from "@/assets/user.jpg";
import Avatar from "@/assets/user.jpg";
import { getTopMenu } from "@/router/utils";
import { useFullscreen } from "@vueuse/core";
import { useGlobal } from "@pureadmin/utils";
import type { routeMetaType } from "../types";
import { transformI18n } from "@/plugins/i18n";
import { router, remainingPaths } from "@/router";
import { computed, type CSSProperties } from "vue";
import { useAppStoreHook } from "@/store/modules/app";
import { useUserStoreHook } from "@/store/modules/user";
import { useGlobal, isAllEmpty } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { usePermissionStoreHook } from "@/store/modules/permission";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
@ -37,9 +37,18 @@ export function useNav() {
};
});
/** 用户名 */
/** 头像(如果头像为空则使用 src/assets/user.jpg */
const userAvatar = computed(() => {
return isAllEmpty(useUserStoreHook()?.avatar)
? Avatar
: useUserStoreHook()?.avatar;
});
/** 昵称(如果昵称为空则显示用户名) */
const username = computed(() => {
return useUserStoreHook()?.username;
return isAllEmpty(useUserStoreHook()?.nickname)
? useUserStoreHook()?.username
: useUserStoreHook()?.nickname;
});
/** 设置国际化选中后的样式 */

View File

@ -38,7 +38,9 @@ export type setType = {
};
export type userType = {
avatar?: string;
username?: string;
nickname?: string;
roles?: Array<string>;
verifyCode?: string;
currentPage?: number;

View File

@ -12,8 +12,12 @@ import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth";
export const useUserStore = defineStore({
id: "pure-user",
state: (): userType => ({
// 头像
avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "",
// 用户名
username: storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "",
// 昵称
nickname: storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? "",
// 页面级别权限
roles: storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [],
// 前端生成的验证码(按实际需求替换)
@ -26,10 +30,18 @@ export const useUserStore = defineStore({
loginDay: 7
}),
actions: {
/** 存储头像 */
SET_AVATAR(avatar: string) {
this.avatar = avatar;
},
/** 存储用户名 */
SET_USERNAME(username: string) {
this.username = username;
},
/** 存储昵称 */
SET_NICKNAME(nickname: string) {
this.nickname = nickname;
},
/** 存储角色 */
SET_ROLES(roles: Array<string>) {
this.roles = roles;

View File

@ -9,8 +9,12 @@ export interface DataInfo<T> {
expires: T;
/** 用于调用刷新accessToken的接口时所需的token */
refreshToken: string;
/** 头像 */
avatar?: string;
/** 用户名 */
username?: string;
/** 昵称 */
nickname?: string;
/** 当前登陆用户的角色 */
roles?: Array<string>;
}
@ -37,7 +41,7 @@ export function getToken(): DataInfo<number> {
* @description `token``token`
* `accessToken`访使`token``refreshToken``accessToken``token``refreshToken`30`accessToken`2`expires``accessToken`
* `accessToken``expires`key值为authorized-token的cookie里
* `username``roles``refreshToken``expires`key值为`user-info`localStorage里`multipleTabsKey`
* `avatar``username``nickname``roles``refreshToken``expires`key值为`user-info`localStorage里`multipleTabsKey`
*/
export function setToken(data: DataInfo<Date>) {
let expires = 0;
@ -62,26 +66,44 @@ export function setToken(data: DataInfo<Date>) {
: {}
);
function setUserKey(username: string, roles: Array<string>) {
function setUserKey({ avatar, username, nickname, roles }) {
useUserStoreHook().SET_AVATAR(avatar);
useUserStoreHook().SET_USERNAME(username);
useUserStoreHook().SET_NICKNAME(nickname);
useUserStoreHook().SET_ROLES(roles);
storageLocal().setItem(userKey, {
refreshToken,
expires,
avatar,
username,
nickname,
roles
});
}
if (data.username && data.roles) {
const { username, roles } = data;
setUserKey(username, roles);
setUserKey({
avatar: data?.avatar ?? "",
username,
nickname: data?.nickname ?? "",
roles
});
} else {
const avatar =
storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "";
const username =
storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "";
const nickname =
storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? "";
const roles =
storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [];
setUserKey(username, roles);
setUserKey({
avatar,
username,
nickname,
roles
});
}
}

View File

@ -1,22 +1,21 @@
<script setup lang="ts">
// [TO DO] dialogcropper src
import { reactive, ref } from "vue";
import { useNav } from "@/layout/hooks/useNav";
import uploadLine from "@iconify-icons/ri/upload-line";
import CroppingUpload from "@/views/system/user/upload.vue";
import type { FormInstance, FormRules } from "element-plus";
import { formUpload } from "@/api/mock";
import { message } from "@/utils/message";
import { type UserInfo, getMine } from "@/api/user";
import type { FormInstance, FormRules } from "element-plus";
import ReCropperPreview from "@/components/ReCropperPreview";
import { createFormData, deviceDetection } from "@pureadmin/utils";
import { getMine, UserInfo } from "@/api/user";
const { userAvatar, getLogo, username } = useNav();
import uploadLine from "@iconify-icons/ri/upload-line";
const imgSrc = ref("");
const cropperInfo = ref();
const cropRef = ref();
const upload = ref();
const uploadRef = ref();
const isShow = ref(false);
const userInfoFormRef = ref<FormInstance>();
//
const userInfoFormData = reactive({
const userInfos = reactive({
avatar: "",
nickname: "",
email: "",
@ -24,86 +23,11 @@ const userInfoFormData = reactive({
description: ""
});
getMine().then(res => {
Object.assign(userInfoFormData, res.data);
});
const rules = reactive<FormRules<UserInfo>>({
nickname: [
{ required: true, message: "昵称必填", trigger: "blur" },
{ min: 3, max: 5, message: "长度最小3最大16", trigger: "blur" }
]
nickname: [{ required: true, message: "昵称必填", trigger: "blur" }]
});
const imgSrc = ref("");
const onChange = uploadFile => {
const reader = new FileReader();
reader.onload = e => {
imgSrc.value = e.target.result as string;
isShow.value = true;
};
// reader.onloadend = () => {
// cropRef.value.init();
// };
reader.readAsDataURL(uploadFile.raw);
/* imgSrc.value = uploadFile.row;
isShow.value = true; */
};
let cropperInfo: any;
const onCropper = info => {
cropperInfo = info;
};
const handleClose = () => {
cropRef.value.hidePopover();
upload.value.clearFiles();
isShow.value = false;
};
// url
const handleSubmitImage = () => {
const formData = createFormData({
files: new File([cropperInfo], "avatar") // file
});
formUpload(formData)
.then(({ success, data }) => {
if (success) {
message("提交成功", { type: "success" });
handleClose();
} else {
message("提交失败");
}
})
.catch(error => {
message(`提交异常 ${error}`, { type: "error" });
});
};
//
const handleCascaderChange = value => {
console.log(value);
};
//
const onSubmit = async (formEl: FormInstance) => {
await formEl.validate((valid, fields) => {
if (valid) {
console.log(userInfoFormData);
console.log("验证成功!");
} else {
console.log("error submit!", fields);
}
});
};
function createFilter(queryString) {
return item => {
return item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0;
};
}
function querySearchEmail(queryString, callback) {
function queryEmail(queryString, callback) {
const emailList = [
{ value: "@qq.com" },
{ value: "@126.com" },
@ -115,10 +39,64 @@ function querySearchEmail(queryString, callback) {
queryList.push({ value: queryString.split("@")[0] + item.value })
);
results = queryString
? queryList.filter(createFilter(queryString))
? queryList.filter(
item =>
item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
: queryList;
callback(results);
}
const onChange = uploadFile => {
const reader = new FileReader();
reader.onload = e => {
imgSrc.value = e.target.result as string;
isShow.value = true;
};
reader.readAsDataURL(uploadFile.raw);
};
const handleClose = () => {
cropRef.value.hidePopover();
uploadRef.value.clearFiles();
isShow.value = false;
};
const onCropper = info => (cropperInfo.value = info);
const handleSubmitImage = () => {
const formData = createFormData({
files: new File([cropperInfo.value], "avatar")
});
formUpload(formData)
.then(({ success, data }) => {
if (success) {
message("更新头像成功", { type: "success" });
handleClose();
} else {
message("更新头像失败");
}
})
.catch(error => {
message(`提交异常 ${error}`, { type: "error" });
});
};
//
const onSubmit = async (formEl: FormInstance) => {
await formEl.validate((valid, fields) => {
if (valid) {
console.log(userInfos);
message("更新信息成功", { type: "success" });
} else {
console.log("error submit!", fields);
}
});
};
getMine().then(res => {
Object.assign(userInfos, res.data);
});
</script>
<template>
@ -133,18 +111,18 @@ function querySearchEmail(queryString, callback) {
ref="userInfoFormRef"
label-position="top"
:rules="rules"
:model="userInfoFormData"
:model="userInfos"
>
<el-form-item label="头像">
<el-avatar :size="80" :src="userAvatar" />
<el-avatar :size="80" :src="userInfos.avatar" />
<el-upload
ref="upload"
ref="uploadRef"
accept="image/*"
action="#"
:limit="1"
:auto-upload="false"
:show-file-list="false"
:on-change="onChange"
accept="image/*"
>
<el-button plain class="ml-4">
<IconifyIconOffline :icon="uploadLine" />
@ -153,15 +131,12 @@ function querySearchEmail(queryString, callback) {
</el-upload>
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input
v-model="userInfoFormData.nickname"
placeholder="请输入昵称"
/>
<el-input v-model="userInfos.nickname" placeholder="请输入昵称" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-autocomplete
v-model="userInfoFormData.email"
:fetch-suggestions="querySearchEmail"
v-model="userInfos.email"
:fetch-suggestions="queryEmail"
:trigger-on-focus="false"
placeholder="请输入邮箱"
clearable
@ -170,14 +145,14 @@ function querySearchEmail(queryString, callback) {
</el-form-item>
<el-form-item label="联系电话">
<el-input
v-model="userInfoFormData.phone"
v-model="userInfos.phone"
placeholder="请输入联系电话"
clearable
/>
</el-form-item>
<el-form-item label="简介">
<el-input
v-model="userInfoFormData.description"
v-model="userInfos.description"
placeholder="请输入简介"
type="textarea"
:autosize="{ minRows: 6, maxRows: 8 }"
@ -191,15 +166,18 @@ function querySearchEmail(queryString, callback) {
</el-form>
<el-dialog
v-model="isShow"
width="40%"
title="编辑头像"
:before-close="handleClose"
destroy-on-close
:closeOnClickModal="false"
:before-close="handleClose"
:fullscreen="deviceDetection()"
>
<CroppingUpload ref="cropRef" :imgSrc="imgSrc" @cropper="onCropper" />
<ReCropperPreview ref="cropRef" :imgSrc="imgSrc" @cropper="onCropper" />
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmitImage">
<el-button bg text @click="handleClose">取消</el-button>
<el-button bg text type="primary" @click="handleSubmitImage">
确定
</el-button>
</div>

View File

@ -5,11 +5,11 @@ import editForm from "../form/index.vue";
import { zxcvbn } from "@zxcvbn-ts/core";
import { handleTree } from "@/utils/tree";
import { message } from "@/utils/message";
import croppingUpload from "../upload.vue";
import userAvatar from "@/assets/user.jpg";
import { usePublicHooks } from "../../hooks";
import { addDialog } from "@/components/ReDialog";
import type { PaginationProps } from "@pureadmin/table";
import ReCropperPreview from "@/components/ReCropperPreview";
import type { FormItemProps, RoleFormItemProps } from "../utils/types";
import {
getKeyList,
@ -365,11 +365,10 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
addDialog({
title: "裁剪、上传头像",
width: "40%",
draggable: true,
closeOnClickModal: false,
fullscreen: deviceDetection(),
contentRenderer: () =>
h(croppingUpload, {
h(ReCropperPreview, {
ref: cropRef,
imgSrc: row.avatar || userAvatar,
onCropper: info => (avatarInfo.value = info)