refactor: 代码重构优化(VueUse使用)

Former-commit-id: f33b8d352dd9e9b2a706c94cdd7afa150ac12931
This commit is contained in:
haoxr
2023-03-01 00:47:39 +08:00
parent 1e6e202ce6
commit 599624e944
16 changed files with 164 additions and 194 deletions

View File

@@ -1,13 +1,13 @@
import request from '@/utils/request'; import request from '@/utils/request';
import { AxiosPromise } from 'axios'; import { AxiosPromise } from 'axios';
import { ILoginData, TokenResult, VerifyCode } from './types'; import { LoginData, LoginResult } from './types';
/** /**
* *
* @param data {LoginForm} * @param data {LoginData}
* @returns * @returns
*/ */
export function loginApi(data: ILoginData): AxiosPromise<TokenResult> { export function loginApi(data: LoginData): AxiosPromise<LoginResult> {
return request({ return request({
url: '/api/v1/auth/login', url: '/api/v1/auth/login',
method: 'post', method: 'post',
@@ -24,13 +24,3 @@ export function logoutApi() {
method: 'delete' method: 'delete'
}); });
} }
/**
* 获取图片验证码
*/
export function getCaptcha(): AxiosPromise<VerifyCode> {
return request({
url: '/captcha?t=' + new Date().getTime().toString(),
method: 'get'
});
}

View File

@@ -1,23 +1,15 @@
/** /**
* 登录数据类型 * 登录请求
*/ */
export interface ILoginData { export interface LoginData {
username: string; username: string;
password: string; password: string;
/**
* 验证码Code
*/
//verifyCode: string;
/**
* 验证码Code服务端缓存key(UUID)
*/
// verifyCodeKey: string;
} }
/** /**
* Token响应类型 * 登录详情
*/ */
export interface TokenResult { export interface LoginResult {
accessToken: string; accessToken: string;
refreshToken: string; refreshToken: string;
expires: number; expires: number;

View File

@@ -1,6 +1,6 @@
import request from '@/utils/request'; import request from '@/utils/request';
import { AxiosPromise } from 'axios'; import { AxiosPromise } from 'axios';
import { UserForm, UserInfo, UserPageResult, UserQuery } from './types'; import { UserForm, UserInfo, UserPageVO, UserQuery } from './types';
/** /**
* 登录成功后获取用户信息(昵称、头像、权限集合和角色集合) * 登录成功后获取用户信息(昵称、头像、权限集合和角色集合)
@@ -19,7 +19,7 @@ export function getUserInfo(): AxiosPromise<UserInfo> {
*/ */
export function listUserPages( export function listUserPages(
queryParams: UserQuery queryParams: UserQuery
): AxiosPromise<UserPageResult> { ): AxiosPromise<PageResult<UserPageVO[]>> {
return request({ return request({
url: '/api/v1/users/pages', url: '/api/v1/users/pages',
method: 'get', method: 'get',

View File

@@ -9,7 +9,7 @@ export interface UserInfo {
} }
/** /**
* 用户查询参数 * 用户查询对象类型
*/ */
export interface UserQuery extends PageQuery { export interface UserQuery extends PageQuery {
keywords: string; keywords: string;
@@ -18,48 +18,102 @@ export interface UserQuery extends PageQuery {
} }
/** /**
* 用户分页列表项声明 * 用户分页对象
*/ */
export interface UserType { export interface UserPageVO {
id: string; /**
username: string; * 用户头像地址
nickname: string; */
mobile: string; avatar?: string;
gender: number; /**
avatar: string; * 创建时间
email: string; */
status: number; createTime?: Date;
deptName: string; /**
roleNames: string; * 部门名称
createTime: string; */
deptName?: string;
/**
* 用户邮箱
*/
email?: string;
/**
* 性别
*/
genderLabel?: string;
/**
* 用户ID
*/
id?: number;
/**
* 手机号
*/
mobile?: string;
/**
* 用户昵称
*/
nickname?: string;
/**
* 角色名称,多个使用英文逗号(,)分割
*/
roleNames?: string;
/**
* 用户状态(1:启用;0:禁用)
*/
status?: number;
/**
* 用户名
*/
username?: string;
} }
/** /**
* 用户分页项类型声明 * 用户表单类型
*/
export type UserPageResult = PageResult<UserType[]>;
/**
* 用户表单类型声明
*/ */
export interface UserForm { export interface UserForm {
id: number | undefined; /**
deptId: number; * 用户头像
username: string; */
nickname: string; avatar?: string;
password: string; /**
mobile: string; * 部门ID
email: string; */
gender: number; deptId?: number;
status: number; /**
remark: string; * 邮箱
roleIds: number[]; */
email?: string;
/**
* 性别
*/
gender?: number;
/**
* 用户ID
*/
id?: number;
mobile?: string;
/**
* 昵称
*/
nickname?: string;
/**
* 角色ID集合
*/
roleIds?: number[];
/**
* 用户状态(1:正常;0:禁用)
*/
status?: number;
/**
* 用户名
*/
username?: string;
} }
/** /**
* 用户导入表单类型声明 * 用户导入视图对象类型
*/ */
export interface UserImportData { export interface UserImportVO {
deptId: number; deptId: number;
roleIds: number[]; roleIds: number[];
} }

View File

@@ -1,6 +1,5 @@
// 自定义国际化配置 // 自定义国际化配置
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import { localStorage } from '@/utils/localStorage';
// 本地语言包 // 本地语言包
import enLocale from './en'; import enLocale from './en';
@@ -22,7 +21,7 @@ const messages = {
*/ */
export const getLanguage = () => { export const getLanguage = () => {
// 本地缓存获取 // 本地缓存获取
let language = localStorage.get('language'); let language = localStorage.getItem('language');
if (language) { if (language) {
return language; return language;
} }

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed } from 'vue';
import { isExternal } from '@/utils/validate'; import { isExternal } from '@/utils/index';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useAppStore } from '@/store/modules/app'; import { useAppStore } from '@/store/modules/app';

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import path from 'path-browserify'; import path from 'path-browserify';
import { isExternal } from '@/utils/validate'; import { isExternal } from '@/utils/index';
import AppLink from './Link.vue'; import AppLink from './Link.vue';
import { translateRouteTitleI18n } from '@/utils/i18n'; import { translateRouteTitleI18n } from '@/utils/i18n';

View File

@@ -1,5 +1,4 @@
import router from '@/router'; import router from '@/router';
import { getToken } from '@/utils/auth';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { usePermissionStoreHook } from '@/store/modules/permission'; import { usePermissionStoreHook } from '@/store/modules/permission';
@@ -14,11 +13,10 @@ const whiteList = ['/login'];
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
NProgress.start(); NProgress.start();
const hasToken = localStorage.getItem('accessToken');
const hasToken = getToken();
if (hasToken) { if (hasToken) {
if (to.path === '/login') { if (to.path === '/login') {
// // 登录成功,跳转到首页
next({ path: '/' }); next({ path: '/' });
} else { } else {
const userStore = useUserStoreHook(); const userStore = useUserStoreHook();

View File

@@ -1,5 +1,4 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import { usePermissionStoreHook } from '@/store/modules/permission';
export const Layout = () => import('@/layout/index.vue'); export const Layout = () => import('@/layout/index.vue');

View File

@@ -1,12 +1,44 @@
// 系统设置
interface DefaultSettings { interface DefaultSettings {
/**
* 系统title
*/
title: string; title: string;
/**
* 是否显示设置
*/
showSettings: boolean; showSettings: boolean;
/**
* 是否显示多标签导航
*/
tagsView: boolean; tagsView: boolean;
/**
*是否固定头部
*/
fixedHeader: boolean; fixedHeader: boolean;
/**
* 是否显示侧边栏Logo
*/
sidebarLogo: boolean; sidebarLogo: boolean;
errorLog: string; /**
* 导航栏布局
*/
layout: string; layout: string;
/**
* 主题模式
*/
theme: string; theme: string;
/**
* 布局大小
*/
size: string;
/**
* 语言
*/
language: string;
} }
const defaultSettings: DefaultSettings = { const defaultSettings: DefaultSettings = {
@@ -15,9 +47,10 @@ const defaultSettings: DefaultSettings = {
tagsView: true, tagsView: true,
fixedHeader: false, fixedHeader: false,
sidebarLogo: true, sidebarLogo: true,
errorLog: 'production',
layout: 'left', layout: 'left',
theme: 'dark' theme: 'dark', // dark | light
size: 'default', // default |large |small
language: 'zh-cn' // zh-cn| en
}; };
export default defaultSettings; export default defaultSettings;

View File

@@ -1,38 +1,21 @@
import {
getSidebarStatus,
setSidebarStatus,
getSize,
setSize,
setLanguage
} from '@/utils/localStorage';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { getLanguage } from '@/lang/index';
import { computed, reactive, ref } from 'vue';
import { useStorage } from '@vueuse/core'; import { useStorage } from '@vueuse/core';
import defaultSettings from '@/settings';
// Element Plus 语言包 // Element Plus 语言包
import zhCn from 'element-plus/es/locale/lang/zh-cn'; import zhCn from 'element-plus/es/locale/lang/zh-cn';
import en from 'element-plus/es/locale/lang/en'; import en from 'element-plus/es/locale/lang/en';
export enum DeviceType {
mobile,
desktop
}
export enum SizeType {
default,
large,
small
}
// setup // setup
export const useAppStore = defineStore('app', () => { export const useAppStore = defineStore('app', () => {
// state // state
const device = useStorage<string>('device', 'desktop'); const device = useStorage('device', 'desktop');
const size = ref(getSize() || 'default'); const size = useStorage('size', defaultSettings.size);
const language = ref(getLanguage()); const language = useStorage('language', defaultSettings.language);
const sidebarStatus = useStorage('sidebarStatus', 'closed');
const sidebar = reactive({ const sidebar = reactive({
opened: getSidebarStatus() !== 'closed', opened: sidebarStatus.value !== 'closed',
withoutAnimation: false withoutAnimation: false
}); });
@@ -49,22 +32,22 @@ export const useAppStore = defineStore('app', () => {
sidebar.opened = !sidebar.opened; sidebar.opened = !sidebar.opened;
sidebar.withoutAnimation = withoutAnimation; sidebar.withoutAnimation = withoutAnimation;
if (sidebar.opened) { if (sidebar.opened) {
setSidebarStatus('opened'); sidebarStatus.value = 'opened';
} else { } else {
setSidebarStatus('closed'); sidebarStatus.value = 'closed';
} }
} }
function closeSideBar(withoutAnimation: boolean) { function closeSideBar(withoutAnimation: boolean) {
sidebar.opened = false; sidebar.opened = false;
sidebar.withoutAnimation = withoutAnimation; sidebar.withoutAnimation = withoutAnimation;
setSidebarStatus('closed'); sidebarStatus.value = 'closed';
} }
function openSideBar(withoutAnimation: boolean) { function openSideBar(withoutAnimation: boolean) {
sidebar.opened = true; sidebar.opened = true;
sidebar.withoutAnimation = withoutAnimation; sidebar.withoutAnimation = withoutAnimation;
setSidebarStatus('opened'); sidebarStatus.value = 'opened';
} }
function toggleDevice(val: string) { function toggleDevice(val: string) {
@@ -73,12 +56,10 @@ export const useAppStore = defineStore('app', () => {
function changeSize(val: string) { function changeSize(val: string) {
size.value = val; size.value = val;
setSize(val);
} }
function changeLanguage(val: string) { function changeLanguage(val: string) {
language.value = val; language.value = val;
setLanguage(val);
} }
return { return {

View File

@@ -1,32 +1,14 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import defaultSettings from '../../settings'; import defaultSettings from '@/settings';
import { ref } from 'vue';
import { useStorage } from '@vueuse/core'; import { useStorage } from '@vueuse/core';
/**
* 主题类型
*/
export enum ThemeType {
light,
dark
}
/**
* 布局类型
*/
export enum LayoutType {
left,
top,
mix
}
export const useSettingsStore = defineStore('setting', () => { export const useSettingsStore = defineStore('setting', () => {
// state // state
const showSettings = ref<boolean>(defaultSettings.showSettings);
const tagsView = useStorage<boolean>('tagsView', defaultSettings.tagsView); const tagsView = useStorage<boolean>('tagsView', defaultSettings.tagsView);
const showSettings = ref<boolean>(defaultSettings.showSettings);
const fixedHeader = ref<boolean>(defaultSettings.fixedHeader); const fixedHeader = ref<boolean>(defaultSettings.fixedHeader);
const sidebarLogo = ref<boolean>(defaultSettings.sidebarLogo); const sidebarLogo = ref<boolean>(defaultSettings.sidebarLogo);
const theme = useStorage('vueuse-color-scheme', defaultSettings.theme);
const layout = useStorage<string>('layout', defaultSettings.layout); const layout = useStorage<string>('layout', defaultSettings.layout);

View File

@@ -1,37 +1,35 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { getToken, setToken, removeToken } from '@/utils/auth';
import { loginApi, logoutApi } from '@/api/auth'; import { loginApi, logoutApi } from '@/api/auth';
import { getUserInfo } from '@/api/user'; import { getUserInfo } from '@/api/user';
import { resetRouter } from '@/router'; import { resetRouter } from '@/router';
import { store } from '@/store'; import { store } from '@/store';
import { ILoginData } from '@/api/auth/types';
import { ref } from 'vue'; import { LoginData } from '@/api/auth/types';
import { UserInfo } from '@/api/user/types'; import { UserInfo } from '@/api/user/types';
import { useStorage } from '@vueuse/core';
export const useUserStore = defineStore('user', () => { export const useUserStore = defineStore('user', () => {
// state // state
const token = ref<string>(getToken() || ''); const token = useStorage('accessToken', '');
const nickname = ref<string>(''); const nickname = ref<string>('');
const avatar = ref<string>(''); const avatar = ref<string>('');
const roles = ref<Array<string>>([]); // 用户角色编码集合 → 判断路由权限 const roles = ref<Array<string>>([]); // 用户角色编码集合 → 判断路由权限
const perms = ref<Array<string>>([]); // 用户权限编码集合 → 判断按钮权限 const perms = ref<Array<string>>([]); // 用户权限编码集合 → 判断按钮权限
// actions
/** /**
* 登录 * 登录
* *
* @param loginData * @param loginData
* @returns * @returns
*/ */
function login(loginData: ILoginData) { function login(loginData: LoginData) {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
loginApi(loginData) loginApi(loginData)
.then(response => { .then(response => {
const { accessToken } = response.data; const { accessToken } = response.data;
token.value = accessToken; token.value = accessToken;
setToken(accessToken);
resolve(); resolve();
}) })
.catch(error => { .catch(error => {
@@ -80,7 +78,6 @@ export const useUserStore = defineStore('user', () => {
// 重置 // 重置
function resetToken() { function resetToken() {
removeToken();
token.value = ''; token.value = '';
nickname.value = ''; nickname.value = '';
avatar.value = ''; avatar.value = '';

View File

@@ -1,53 +0,0 @@
/**
* window.localStorage 浏览器永久缓存
*/
export const localStorage = {
// 设置永久缓存
set(key: string, val: any) {
window.localStorage.setItem(key, JSON.stringify(val));
},
// 获取永久缓存
get(key: string) {
const json: any = window.localStorage.getItem(key);
return JSON.parse(json);
},
// 移除永久缓存
remove(key: string) {
window.localStorage.removeItem(key);
},
// 移除全部永久缓存
clear() {
window.localStorage.clear();
}
};
// 侧边栏状态(显示/隐藏)
const SidebarStatusKey = 'sidebarStatus';
export function getSidebarStatus() {
return localStorage.get(SidebarStatusKey);
}
export function setSidebarStatus(sidebarStatus: string) {
localStorage.set(SidebarStatusKey, sidebarStatus);
}
// 布局大小
const SizeKey = 'size';
export function getSize() {
return localStorage.get(SizeKey);
}
export function setSize(size: string) {
localStorage.set(SizeKey, size);
}
// 语言
const LanguageKey = 'language';
export function getLanguage() {
return localStorage.get(LanguageKey);
}
export function setLanguage(language: string) {
localStorage.set(LanguageKey, language);
}

View File

@@ -1,6 +1,4 @@
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios'; import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getToken } from '@/utils/auth';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
// 创建 axios 实例 // 创建 axios 实例
@@ -20,7 +18,7 @@ service.interceptors.request.use(
} }
const user = useUserStoreHook(); const user = useUserStoreHook();
if (user.token) { if (user.token) {
config.headers.Authorization = getToken(); config.headers.Authorization = localStorage.getItem('accessToken');
} }
return config; return config;
}, },

View File

@@ -46,9 +46,9 @@ import {
} from '@element-plus/icons-vue'; } from '@element-plus/icons-vue';
import { import {
UserForm, UserForm,
UserImportData, UserImportVO,
UserQuery, UserQuery,
UserType UserPageVO
} from '@/api/user/types'; } from '@/api/user/types';
const deptTreeRef = ref(ElTree); // 部门树 const deptTreeRef = ref(ElTree); // 部门树
@@ -65,7 +65,7 @@ const state = reactive({
ids: [] as number[], ids: [] as number[],
// 总条数 // 总条数
total: 0, total: 0,
userList: [] as UserType[], userList: [] as UserPageVO[],
dialog: { dialog: {
visible: false visible: false
} as DialogType, } as DialogType,
@@ -110,7 +110,7 @@ const state = reactive({
title: '用户导入', title: '用户导入',
visible: false visible: false
} as DialogType, } as DialogType,
importFormData: {} as UserImportData, importFormData: {} as UserImportVO,
excelFile: undefined as any, excelFile: undefined as any,
excelFilelist: [] as File[] excelFilelist: [] as File[]
}); });
@@ -396,7 +396,7 @@ function handleExcelChange(file: UploadFile) {
/** /**
* Excel文件上传 * Excel文件上传
*/ */
function submitImportForm() { function uploadUser() {
importFormRef.value.validate((valid: any) => { importFormRef.value.validate((valid: any) => {
if (valid) { if (valid) {
if (!state.excelFile) { if (!state.excelFile) {
@@ -805,7 +805,7 @@ onMounted(() => {
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" @click="submitImportForm"> </el-button> <el-button type="primary" @click="uploadUser"> </el-button>
<el-button @click="closeImportDialog"> </el-button> <el-button @click="closeImportDialog"> </el-button>
</div> </div>
</template> </template>