refactor: ♻️ 项目重构(临时提交)

This commit is contained in:
郝先瑞 2023-11-16 22:46:28 +08:00
parent bdcf96f713
commit 14e64324f7
34 changed files with 2129 additions and 399 deletions

View File

@ -3,14 +3,12 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV='development' NODE_ENV='development'
VITE_APP_TITLE = 'vue3-element-admin'
VITE_APP_PORT = 3000 VITE_APP_PORT = 3000
# API请求前缀 # API请求前缀
VITE_APP_BASE_API = '/dev-api' VITE_APP_BASE_API = '/dev-api'
# proxy代理配置 # proxy代理配置
VITE_APP_TARGET_URL = 'http://vapi.youlai.tech' # 线上接口 VITE_APP_API_URL = 'http://vapi.youlai.tech' # 线上接口
# VITE_APP_TARGET_URL = 'http://localhost:8989' # 本地接口本地启动后端https://gitee.com/youlaiorg/youlai-boot # VITE_APP_API_URL = 'http://localhost:8989' # 本地接口本地启动后端https://gitee.com/youlaiorg/youlai-boot
# VITE_APP_TARGET_URL = 'http://localhost:3000' # 本地Mock # VITE_APP_API_URL = 'http://localhost:3000' # 本地Mock
VITE_APP_TARGET_BASE_API = ''

View File

@ -1,11 +1,9 @@
## 生产环境 ## 生产环境
VITE_APP_TITLE = 'vue3-element-admin'
VITE_APP_PORT = 3000 VITE_APP_PORT = 3000
# API请求前缀 # API请求前缀
VITE_APP_BASE_API = '/prod-api' VITE_APP_BASE_API = '/prod-api'
# proxy代理配置 # proxy代理配置
VITE_APP_TARGET_URL = "http://vapi.youlai.tech" VITE_APP_API_URL = "http://vapi.youlai.tech"
VITE_APP_TARGET_BASE_API = ''

View File

@ -1,8 +0,0 @@
## 模拟环境
NODE_ENV='staging'
VITE_APP_TITLE = 'vue3-element-admin'
VITE_APP_PORT = 3000
VITE_APP_TARGET_URL = 'http://localhost:3000';
VITE_APP_BASE_API = '/prod-api'

View File

@ -83,7 +83,6 @@ module.exports = {
], ],
// https://eslint.org/docs/latest/use/configure/language-options#specifying-globals // https://eslint.org/docs/latest/use/configure/language-options#specifying-globals
globals: { globals: {
DialogOption: "readonly",
OptionType: "readonly", OptionType: "readonly",
}, },
}; };

View File

@ -1,3 +1,3 @@
{ {
"recommendations": ["Vue.volar"] "recommendations": ["Vue.volar", "lokalise.i18n-ally","esbenp.prettier-vscode"]
} }

View File

@ -124,7 +124,7 @@ server {
- **接口切换 Mock** - **接口切换 Mock**
v2.5.0 版本支持 Mock , 修改 `.env.development``VITE_APP_TARGET_URL` 值为 `http://localhost:3000` 即可 。 v2.5.0 版本支持 Mock , 修改 `.env.development``VITE_APP_API_URL` 值为 `http://localhost:3000` 即可 。
- **其他问题** - **其他问题**
@ -142,7 +142,7 @@ server {
> 1. 获取基于 `Java 、SpringBoot` 开发的后端 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot.git) 源码 ; > 1. 获取基于 `Java 、SpringBoot` 开发的后端 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot.git) 源码 ;
> 2. 根据后端工程说明文档 [README.md](https://gitee.com/youlaiorg/youlai-boot#%E9%A1%B9%E7%9B%AE%E8%BF%90%E8%A1%8C) 完成本地启动; > 2. 根据后端工程说明文档 [README.md](https://gitee.com/youlaiorg/youlai-boot#%E9%A1%B9%E7%9B%AE%E8%BF%90%E8%A1%8C) 完成本地启动;
> 3. 替换 [.env.development](.env.development) 的代理目标地址 `VITE_APP_TARGET_URL` 的值 `vapi.youlai.tech` 为本地的 `localhost:8989` > 3. 替换 [.env.development](.env.development) 的代理目标地址 `VITE_APP_API_URL` 的值 `vapi.youlai.tech` 为本地的 `localhost:8989`

View File

@ -1,7 +1,7 @@
{ {
"name": "vue3-element-admin", "name": "vue3-element-admin",
"private": true,
"version": "2.6.7", "version": "2.6.7",
"private": true,
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"dev": "vite serve --mode development", "dev": "vite serve --mode development",
@ -63,7 +63,8 @@
"vue": "^3.3.8", "vue": "^3.3.8",
"vue-i18n": "9.2.2", "vue-i18n": "9.2.2",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"xlsx": "^0.18.5" "xlsx": "^0.18.5",
"dayjs": "^1.11.10"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.8.1", "@commitlint/cli": "^17.8.1",

View File

@ -5,7 +5,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import SvgIcon from "@/components/SvgIcon/index.vue";
import { translateRouteTitle } from "@/utils/i18n"; import { translateRouteTitle } from "@/utils/i18n";
defineProps({ defineProps({

View File

@ -2,6 +2,7 @@
import path from "path-browserify"; import path from "path-browserify";
import { isExternal } from "@/utils/index"; import { isExternal } from "@/utils/index";
import AppLink from "./Link.vue"; import AppLink from "./Link.vue";
import { RouteRecordRaw } from "vue-router";
import Item from "./Item.vue"; import Item from "./Item.vue";
@ -38,15 +39,19 @@ const onlyOneChild = ref(); // 临时变量,唯一子路由
* @param children 子路由数组 * @param children 子路由数组
* @param parent 当前路由 * @param parent 当前路由
*/ */
function hasOneShowingChild(children = [], parent: any) { function hasOneShowingChild(
children: RouteRecordRaw[] = [],
parent: RouteRecordRaw
) {
// //
const showingChildren = children.filter((item: any) => { const showingChildren = children.filter((route: RouteRecordRaw) => {
if (item.meta?.hidden) { if (route.meta?.hidden) {
// //
return false; return false;
} else { } else {
route.meta!.hidden = false;
// onlyOneChild // onlyOneChild
onlyOneChild.value = item; onlyOneChild.value = route;
return true; return true;
} }
}); });
@ -87,7 +92,7 @@ function resolvePath(routePath: string) {
<!-- 无子路由 || 目录只有一个子路由并配置始终显示为否(alwaysShow=false) --> <!-- 无子路由 || 目录只有一个子路由并配置始终显示为否(alwaysShow=false) -->
<template <template
v-if=" v-if="
hasOneShowingChild(item.children, item) && hasOneShowingChild(item.children, item as RouteRecordRaw) &&
(!onlyOneChild.children || onlyOneChild.noShowingChildren) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
!item.meta?.alwaysShow !item.meta?.alwaysShow
" "

View File

@ -61,13 +61,6 @@ onMounted(() => {
</template> </template>
</template> </template>
</el-menu-item> </el-menu-item>
<!-- <sidebar-item
v-for="route in topMenu"
:key="route.path"
:item="route"
:base-path="route.path"
:is-collapse="false"
/> -->
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useTagsViewStore, TagView } from "@/store/modules/tagsView"; import { useTagsViewStore } from "@/store/modules/tagsView";
const tagAndTagSpacing = ref(4); const tagAndTagSpacing = ref(4);
const { proxy } = getCurrentInstance() as any; const { proxy } = getCurrentInstance() as any;

View File

@ -1,21 +1,76 @@
<template>
<div class="tags-container">
<scroll-pane ref="scrollPaneRef" @scroll="handleScroll">
<router-link
ref="tagRef"
v-for="tag in visitedViews"
:key="tag.path"
:class="'tags-item ' + (isActive(tag) ? 'active' : '')"
:data-path="tag.path"
:to="{ path: tag.path, query: tag.query }"
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openTagMenu(tag, $event)"
>
{{ translateRouteTitle(tag.title) }}
<span
v-if="!isAffix(tag)"
class="tags-item-close"
@click.prevent.stop="closeSelectedTag(tag)"
>
<i-ep-close size="10px" />
</span>
</router-link>
</scroll-pane>
<!-- tag标签操作菜单 -->
<ul
v-show="tagMenuVisible"
class="tag-menu"
:style="{ left: left + 'px', top: top + 'px' }"
>
<li @click="refreshSelectedTag(selectedTag)">
<svg-icon icon-class="refresh" />
刷新
</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
<svg-icon icon-class="close" />
关闭
</li>
<li @click="closeOtherTags">
<svg-icon icon-class="close_other" />
关闭其它
</li>
<li v-if="!isFirstView()" @click="closeLeftTags">
<svg-icon icon-class="close_left" />
关闭左侧
</li>
<li v-if="!isLastView()" @click="closeRightTags">
<svg-icon icon-class="close_right" />
关闭右侧
</li>
<li @click="closeAllTags(selectedTag)">
<svg-icon icon-class="close_all" />
关闭所有
</li>
</ul>
</div>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { getCurrentInstance, ComponentInternalInstance } from "vue";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { useRoute, useRouter, RouteRecordRaw } from "vue-router";
import path from "path-browserify"; import { resolve } from "path-browserify";
import { useRoute, useRouter } from "vue-router";
import { translateRouteTitle } from "@/utils/i18n"; import { translateRouteTitle } from "@/utils/i18n";
import { usePermissionStore } from "@/store/modules/permission"; import { usePermissionStore } from "@/store/modules/permission";
import { useTagsViewStore, TagView } from "@/store/modules/tagsView"; import { useTagsViewStore } from "@/store/modules/tagsView";
import { useSettingsStore } from "@/store/modules/settings"; import { useSettingsStore } from "@/store/modules/settings";
import { useAppStore } from "@/store/modules/app"; import { useAppStore } from "@/store/modules/app";
import ScrollPane from "./ScrollPane.vue"; import ScrollPane from "./ScrollPane.vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance()!;
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@ -27,11 +82,19 @@ const { visitedViews } = storeToRefs(tagsViewStore);
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
const layout = computed(() => settingsStore.layout); const layout = computed(() => settingsStore.layout);
const selectedTag = ref({}); const selectedTag = ref<TagView>({
path: "",
fullPath: "",
name: "",
title: "",
affix: false,
keepAlive: false,
});
const affixTags = ref<TagView[]>([]);
const scrollPaneRef = ref(); const scrollPaneRef = ref();
const left = ref(0); const left = ref(0);
const top = ref(0); const top = ref(0);
const affixTags = ref<TagView[]>([]);
watch( watch(
route, route,
@ -40,8 +103,7 @@ watch(
moveToCurrentTag(); moveToCurrentTag();
}, },
{ {
// immediate: true, //
immediate: true,
} }
); );
@ -54,27 +116,31 @@ watch(tagMenuVisible, (value) => {
} }
}); });
function filterAffixTags(routes: any[], basePath = "/") { function filterAffixTags(routes: RouteRecordRaw[], basePath = "/") {
let tags: TagView[] = []; const processRoute = (route: RouteRecordRaw) => {
const fullPath = resolve(basePath, route.path);
routes.forEach((route) => { const tag: TagView = {
if (route.meta && route.meta.affix) { path: route.path,
const tagPath = path.resolve(basePath, route.path); fullPath,
tags.push({ name: String(route.name),
fullPath: tagPath, title: route.meta?.title || "no-name",
path: tagPath, affix: route.meta?.affix,
name: route.name, keepAlive: route.meta?.keepAlive,
meta: { ...route.meta }, };
});
if (tag.affix) {
tags.push(tag);
} }
if (route.children) { if (route.children) {
const childTags = filterAffixTags(route.children, route.path); route.children.forEach(processRoute);
if (childTags.length >= 1) {
tags = tags.concat(childTags);
}
} }
}); };
let tags: TagView[] = [];
routes.forEach(processRoute);
return tags; return tags;
} }
@ -90,19 +156,35 @@ function initTags() {
} }
function addTags() { function addTags() {
if (route.name) { if (route.meta.title) {
tagsViewStore.addView(route); tagsViewStore.addView({
name: route.name as string,
title: route.meta.title,
path: route.path,
fullPath: route.fullPath,
affix: route.meta?.affix,
keepAlive: route.meta?.keepAlive,
});
} }
} }
function moveToCurrentTag() { function moveToCurrentTag() {
// 使 nextTick() tagsView scrollPaneRef
nextTick(() => { nextTick(() => {
for (const r of tagsViewStore.visitedViews) { for (const tag of visitedViews.value) {
if (r.path === route.path) { if (tag.path === route.path) {
scrollPaneRef.value.moveToTarget(r); scrollPaneRef.value.moveToTarget(tag);
// when query is different then update // when query is different then update
if (r.fullPath !== route.fullPath) { route.query = { ...route.query, ...tag.query };
tagsViewStore.updateVisitedView(route); if (tag.fullPath !== route.fullPath) {
tagsViewStore.updateVisitedView({
name: route.name as string,
title: route.meta.title,
path: route.path,
fullPath: route.fullPath,
affix: route.meta?.affix,
keepAlive: route.meta?.keepAlive,
});
} }
} }
} }
@ -110,19 +192,18 @@ function moveToCurrentTag() {
} }
function isActive(tag: TagView) { function isActive(tag: TagView) {
return tag.path === route.path; return tag.fullPath === route.fullPath;
} }
function isAffix(tag: TagView) { function isAffix(tag: TagView) {
return tag.meta && tag.meta.affix; return tag?.affix;
} }
function isFirstView() { function isFirstView() {
try { try {
return ( return (
(selectedTag.value as TagView).fullPath === "/dashboard" || selectedTag.value.fullPath === "/dashboard" ||
(selectedTag.value as TagView).fullPath === selectedTag.value.fullPath === tagsViewStore.visitedViews[1].fullPath
tagsViewStore.visitedViews[1].fullPath
); );
} catch (err) { } catch (err) {
return false; return false;
@ -132,7 +213,7 @@ function isFirstView() {
function isLastView() { function isLastView() {
try { try {
return ( return (
(selectedTag.value as TagView).fullPath === selectedTag.value.fullPath ===
tagsViewStore.visitedViews[tagsViewStore.visitedViews.length - 1].fullPath tagsViewStore.visitedViews[tagsViewStore.visitedViews.length - 1].fullPath
); );
} catch (err) { } catch (err) {
@ -144,20 +225,18 @@ function refreshSelectedTag(view: TagView) {
tagsViewStore.delCachedView(view); tagsViewStore.delCachedView(view);
const { fullPath } = view; const { fullPath } = view;
nextTick(() => { nextTick(() => {
router.replace({ path: "/redirect" + fullPath }).catch((err) => { router.replace({ path: "/redirect" + fullPath });
console.warn(err);
});
}); });
} }
function toLastView(visitedViews: TagView[], view?: any) { function toLastView(visitedViews: TagView[], view?: TagView) {
const latestView = visitedViews.slice(-1)[0]; const latestView = visitedViews.slice(-1)[0];
if (latestView && latestView.fullPath) { if (latestView && latestView.fullPath) {
router.push(latestView.fullPath); router.push(latestView.fullPath);
} else { } else {
// now the default is to redirect to the home page if there is no tags-view, // now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs. // you can adjust it according to your needs.
if (view.name === "Dashboard") { if (view?.name === "Dashboard") {
// to reload home page // to reload home page
router.replace({ path: "/redirect" + view.fullPath }); router.replace({ path: "/redirect" + view.fullPath });
} else { } else {
@ -289,63 +368,6 @@ onMounted(() => {
}); });
</script> </script>
<template>
<div class="tags-container">
<scroll-pane ref="scrollPaneRef" @scroll="handleScroll">
<router-link
v-for="tag in visitedViews"
:key="tag.path"
:class="'tags-item ' + (isActive(tag) ? 'active' : '')"
:data-path="tag.path"
:to="{ path: tag.path, query: tag.query }"
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openTagMenu(tag, $event)"
>
{{ translateRouteTitle(tag.meta?.title) }}
<span
v-if="!isAffix(tag)"
class="tags-item-close"
@click.prevent.stop="closeSelectedTag(tag)"
>
<i-ep-close class="text-[10px]" />
</span>
</router-link>
</scroll-pane>
<!-- tag标签操作菜单 -->
<ul
v-show="tagMenuVisible"
class="tag-menu"
:style="{ left: left + 'px', top: top + 'px' }"
>
<li @click="refreshSelectedTag(selectedTag)">
<svg-icon icon-class="refresh" />
刷新
</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
<svg-icon icon-class="close" />
关闭
</li>
<li @click="closeOtherTags">
<svg-icon icon-class="close_other" />
关闭其它
</li>
<li v-if="!isFirstView()" @click="closeLeftTags">
<svg-icon icon-class="close_left" />
关闭左侧
</li>
<li v-if="!isLastView()" @click="closeRightTags">
<svg-icon icon-class="close_right" />
关闭右侧
</li>
<li @click="closeAllTags(selectedTag)">
<svg-icon icon-class="close_all" />
关闭所有
</li>
</ul>
</div>
</template>
<style lang="scss" scoped> <style lang="scss" scoped>
.tags-container { .tags-container {
width: 100%; width: 100%;

View File

@ -30,6 +30,8 @@ const mixLeftMenu = computed(() => {
return permissionStore.mixLeftMenu; return permissionStore.mixLeftMenu;
}); });
const layout = computed(() => settingsStore.layout); const layout = computed(() => settingsStore.layout);
const watermarkEnabled = computed(() => settingsStore.watermark.enabled);
watch( watch(
() => activeTopMenu.value, () => activeTopMenu.value,
(newVal) => { (newVal) => {
@ -78,29 +80,31 @@ function toggleSideBar() {
<template> <template>
<div :class="classObj" class="app-wrapper"> <div :class="classObj" class="app-wrapper">
<!-- 手机设备侧边栏打开遮罩层 --> <el-watermark content="vue3-element-admin">
<div <!-- 手机设备侧边栏打开遮罩层 -->
v-if="classObj.mobile && classObj.openSidebar" <div
class="drawer-bg" v-if="classObj.mobile && classObj.openSidebar"
@click="handleOutsideClick" class="drawer-bg"
></div> @click="handleOutsideClick"
></div>
<Sidebar class="sidebar-container" /> <Sidebar class="sidebar-container" />
<template v-if="layout === 'mix'"> <template v-if="layout === 'mix'">
<div class="mix-wrap"> <div class="mix-wrap">
<div class="left-wrap"> <div class="left-wrap">
<LeftMenu :menu-list="mixLeftMenu" :base-path="activeTopMenu" /> <LeftMenu :menu-list="mixLeftMenu" :base-path="activeTopMenu" />
<div class="menu-action"> <div class="menu-action">
<hamburger <hamburger
:is-active="appStore.sidebar.opened" :is-active="appStore.sidebar.opened"
@toggle-click="toggleSideBar" @toggle-click="toggleSideBar"
/> />
</div>
</div> </div>
<Main />
</div> </div>
<Main /> </template>
</div> <Main v-else />
</template> </el-watermark>
<Main v-else />
</div> </div>
</template> </template>

View File

@ -1,68 +1,16 @@
/** const defaultSettings: AppSettings = {
*
*/
interface DefaultSettings {
/**
*
*/
title: string;
/**
*
*/
version: string;
/**
*
*/
showSettings: boolean;
/**
*
*/
tagsView: boolean;
/**
*
*/
fixedHeader: boolean;
/**
* Logo
*/
sidebarLogo: boolean;
/**
* (left|top|mix)
*/
layout: string;
/**
* (dark|light)
*/
theme: string;
/**
* (default |large |small)
*/
size: string;
/**
* ( zh-cn| en)
*/
language: string;
/**
*
*/
themeColor: string;
}
const defaultSettings: DefaultSettings = {
title: "vue3-element-admin", title: "vue3-element-admin",
version: "v2.6.7", version: "v2.6.7",
showSettings: true, showSettings: true,
tagsView: true, tagsView: true,
fixedHeader: false, fixedHeader: false,
sidebarLogo: true, sidebarLogo: true,
layout: "left", // 默认左侧模式 layout: "left",
theme: "light", // 暗黑模式-dark 明亮模式-light theme: "light",
size: "default", size: "default",
language: "zh-cn", language: "zh-cn",
themeColor: "#409EFF", themeColor: "#409EFF",
watermark: { enabled: false, content: "有来技术" },
}; };
export default defaultSettings; export default defaultSettings;

View File

@ -21,8 +21,8 @@ const hasPermission = (roles: string[], route: RouteRecordRaw) => {
return true; return true;
} }
return roles.some((role) => { return roles.some((role) => {
if (route.meta?.roles !== undefined) { if (route.meta?.roles) {
return (route.meta.roles as string[]).includes(role); return route.meta.roles.includes(role);
} }
}); });
} }

View File

@ -2,59 +2,53 @@ import { defineStore } from "pinia";
import defaultSettings from "@/settings"; import defaultSettings from "@/settings";
export const useSettingsStore = defineStore("setting", () => { export const useSettingsStore = defineStore("setting", () => {
// state const title = defaultSettings.title;
const version = defaultSettings.version;
const tagsView = useStorage<boolean>("tagsView", defaultSettings.tagsView); const tagsView = useStorage<boolean>("tagsView", defaultSettings.tagsView);
const showSettings = ref<boolean>(defaultSettings.showSettings); const showSettings = ref<boolean>(defaultSettings.showSettings);
const sidebarLogo = ref<boolean>(defaultSettings.sidebarLogo); const sidebarLogo = ref<boolean>(defaultSettings.sidebarLogo);
const fixedHeader = useStorage<boolean>( const fixedHeader = useStorage<boolean>(
"fixedHeader", "fixedHeader",
defaultSettings.fixedHeader defaultSettings.fixedHeader
); );
const layout = useStorage<string>("layout", defaultSettings.layout); const layout = useStorage<string>("layout", defaultSettings.layout);
const themeColor = useStorage<string>( const themeColor = useStorage<string>(
"themeColor", "themeColor",
defaultSettings.themeColor defaultSettings.themeColor
); );
const theme = useStorage<string>("theme", defaultSettings.theme); const theme = useStorage<string>("theme", defaultSettings.theme);
// actions // Whether to enable watermark
function changeSetting(param: { key: string; value: any }) { const watermark = useStorage<any>("watermark", defaultSettings.watermark);
const { key, value } = param;
switch (key) { const settingsMap: Record<string, Ref<any>> = {
case "showSettings": showSettings,
showSettings.value = value; fixedHeader,
break; tagsView,
case "fixedHeader": sidebarLogo,
fixedHeader.value = value; layout,
break; themeColor,
case "tagsView": theme,
tagsView.value = value; watermark: watermark.value,
break; };
case "sidevarLogo":
sidebarLogo.value = value; function changeSetting({ key, value }: { key: string; value: any }) {
break; const setting = settingsMap[key];
case "layout": if (setting !== undefined) {
layout.value = value; setting.value = value;
break; if (key === "theme" && value === "dark") {
case "themeColor": document.documentElement.classList.add("dark");
themeColor.value = value; } else {
break; document.documentElement.classList.remove("dark");
case "theme": }
theme.value = value;
if (theme.value === "dark") {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
break;
} }
} }
return { return {
title,
version,
showSettings, showSettings,
tagsView, tagsView,
fixedHeader, fixedHeader,
@ -63,5 +57,6 @@ export const useSettingsStore = defineStore("setting", () => {
themeColor, themeColor,
changeSetting, changeSetting,
theme, theme,
watermark,
}; };
}); });

View File

@ -1,45 +1,49 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { RouteLocationNormalized } from "vue-router";
export interface TagView extends Partial<RouteLocationNormalized> {
title?: string;
}
// setup
export const useTagsViewStore = defineStore("tagsView", () => { export const useTagsViewStore = defineStore("tagsView", () => {
// state
const visitedViews = ref<TagView[]>([]); const visitedViews = ref<TagView[]>([]);
const cachedViews = ref<string[]>([]); const cachedViews = ref<string[]>([]);
// actions /**
* 访访
*/
function addVisitedView(view: TagView) { function addVisitedView(view: TagView) {
if (visitedViews.value.some((v) => v.path === view.path)) return; // 如果已经存在于已访问的视图列表中,则不再添加
if (view.meta && view.meta.affix) { if (visitedViews.value.some((v) => v.path === view.path)) {
visitedViews.value.unshift( return;
Object.assign({}, view, { }
title: view.meta?.title || "no-name", // 如果视图是固定的affix则在已访问的视图列表的开头添加
}) if (view.affix) {
); visitedViews.value.unshift(view);
} else { } else {
visitedViews.value.push( // 如果视图不是固定的,则在已访问的视图列表的末尾添加
Object.assign({}, view, { visitedViews.value.push(view);
title: view.meta?.title || "no-name",
})
);
} }
} }
/**
*
*/
function addCachedView(view: TagView) { function addCachedView(view: TagView) {
const viewName = view.name as string; const viewName = view.name;
if (cachedViews.value.includes(viewName)) return; // 如果缓存视图名称已经存在于缓存视图列表中,则不再添加
if (view.meta?.keepAlive) { if (cachedViews.value.includes(viewName)) {
return;
}
// 如果视图需要缓存keepAlive则将其路由名称添加到缓存视图列表中
if (view.keepAlive) {
cachedViews.value.push(viewName); cachedViews.value.push(viewName);
} }
} }
/**
* 访
*/
function delVisitedView(view: TagView) { function delVisitedView(view: TagView) {
return new Promise((resolve) => { return new Promise((resolve) => {
for (const [i, v] of visitedViews.value.entries()) { for (const [i, v] of visitedViews.value.entries()) {
// 找到与指定视图路径匹配的视图,在已访问视图列表中删除该视图
if (v.path === view.path) { if (v.path === view.path) {
visitedViews.value.splice(i, 1); visitedViews.value.splice(i, 1);
break; break;
@ -50,7 +54,7 @@ export const useTagsViewStore = defineStore("tagsView", () => {
} }
function delCachedView(view: TagView) { function delCachedView(view: TagView) {
const viewName = view.name as string; const viewName = view.name;
return new Promise((resolve) => { return new Promise((resolve) => {
const index = cachedViews.value.indexOf(viewName); const index = cachedViews.value.indexOf(viewName);
index > -1 && cachedViews.value.splice(index, 1); index > -1 && cachedViews.value.splice(index, 1);
@ -61,7 +65,7 @@ export const useTagsViewStore = defineStore("tagsView", () => {
function delOtherVisitedViews(view: TagView) { function delOtherVisitedViews(view: TagView) {
return new Promise((resolve) => { return new Promise((resolve) => {
visitedViews.value = visitedViews.value.filter((v) => { visitedViews.value = visitedViews.value.filter((v) => {
return v.meta?.affix || v.path === view.path; return v?.affix || v.path === view.path;
}); });
resolve([...visitedViews.value]); resolve([...visitedViews.value]);
}); });
@ -126,12 +130,11 @@ export const useTagsViewStore = defineStore("tagsView", () => {
return; return;
} }
visitedViews.value = visitedViews.value.filter((item, index) => { visitedViews.value = visitedViews.value.filter((item, index) => {
// affix:true 固定tag例如“首页” if (index >= currIndex || item?.affix) {
if (index >= currIndex || (item.meta && item.meta.affix)) {
return true; return true;
} }
const cacheIndex = cachedViews.value.indexOf(item.name as string); const cacheIndex = cachedViews.value.indexOf(item.name);
if (cacheIndex > -1) { if (cacheIndex > -1) {
cachedViews.value.splice(cacheIndex, 1); cachedViews.value.splice(cacheIndex, 1);
} }
@ -151,16 +154,9 @@ export const useTagsViewStore = defineStore("tagsView", () => {
return; return;
} }
visitedViews.value = visitedViews.value.filter((item, index) => { visitedViews.value = visitedViews.value.filter((item, index) => {
// affix:true 固定tag例如“首页” if (index <= currIndex || item?.affix) {
if (index <= currIndex || (item.meta && item.meta.affix)) {
return true; return true;
} }
const cacheIndex = cachedViews.value.indexOf(item.name as string);
if (cacheIndex > -1) {
cachedViews.value.splice(cacheIndex, 1);
}
return false;
}); });
resolve({ resolve({
visitedViews: [...visitedViews.value], visitedViews: [...visitedViews.value],
@ -170,7 +166,7 @@ export const useTagsViewStore = defineStore("tagsView", () => {
function delAllViews() { function delAllViews() {
return new Promise((resolve) => { return new Promise((resolve) => {
const affixTags = visitedViews.value.filter((tag) => tag.meta?.affix); const affixTags = visitedViews.value.filter((tag) => tag?.affix);
visitedViews.value = affixTags; visitedViews.value = affixTags;
cachedViews.value = []; cachedViews.value = [];
resolve({ resolve({
@ -182,7 +178,7 @@ export const useTagsViewStore = defineStore("tagsView", () => {
function delAllVisitedViews() { function delAllVisitedViews() {
return new Promise((resolve) => { return new Promise((resolve) => {
const affixTags = visitedViews.value.filter((tag) => tag.meta?.affix); const affixTags = visitedViews.value.filter((tag) => tag?.affix);
visitedViews.value = affixTags; visitedViews.value = affixTags;
resolve([...visitedViews.value]); resolve([...visitedViews.value]);
}); });

View File

@ -7,9 +7,7 @@ declare global {
const EffectScope: typeof import("vue")["EffectScope"]; const EffectScope: typeof import("vue")["EffectScope"];
const ElForm: typeof import("element-plus/es")["ElForm"]; const ElForm: typeof import("element-plus/es")["ElForm"];
const ElMessage: typeof import("element-plus/es")["ElMessage"]; const ElMessage: typeof import("element-plus/es")["ElMessage"];
const ElNotification: typeof import("element-plus/es")["ElNotification"];
const ElMessageBox: typeof import("element-plus/es")["ElMessageBox"]; const ElMessageBox: typeof import("element-plus/es")["ElMessageBox"];
const ElTree: typeof import("element-plus/es")["ElTree"];
const asyncComputed: typeof import("@vueuse/core")["asyncComputed"]; const asyncComputed: typeof import("@vueuse/core")["asyncComputed"];
const autoResetRef: typeof import("@vueuse/core")["autoResetRef"]; const autoResetRef: typeof import("@vueuse/core")["autoResetRef"];
const computed: typeof import("vue")["computed"]; const computed: typeof import("vue")["computed"];
@ -41,6 +39,7 @@ declare global {
const h: typeof import("vue")["h"]; const h: typeof import("vue")["h"];
const ignorableWatch: typeof import("@vueuse/core")["ignorableWatch"]; const ignorableWatch: typeof import("@vueuse/core")["ignorableWatch"];
const inject: typeof import("vue")["inject"]; const inject: typeof import("vue")["inject"];
const injectLocal: typeof import("@vueuse/core")["injectLocal"];
const isDefined: typeof import("@vueuse/core")["isDefined"]; const isDefined: typeof import("@vueuse/core")["isDefined"];
const isProxy: typeof import("vue")["isProxy"]; const isProxy: typeof import("vue")["isProxy"];
const isReactive: typeof import("vue")["isReactive"]; const isReactive: typeof import("vue")["isReactive"];
@ -68,6 +67,7 @@ declare global {
const onUpdated: typeof import("vue")["onUpdated"]; const onUpdated: typeof import("vue")["onUpdated"];
const pausableWatch: typeof import("@vueuse/core")["pausableWatch"]; const pausableWatch: typeof import("@vueuse/core")["pausableWatch"];
const provide: typeof import("vue")["provide"]; const provide: typeof import("vue")["provide"];
const provideLocal: typeof import("@vueuse/core")["provideLocal"];
const reactify: typeof import("@vueuse/core")["reactify"]; const reactify: typeof import("@vueuse/core")["reactify"];
const reactifyObject: typeof import("@vueuse/core")["reactifyObject"]; const reactifyObject: typeof import("@vueuse/core")["reactifyObject"];
const reactive: typeof import("vue")["reactive"]; const reactive: typeof import("vue")["reactive"];
@ -287,10 +287,14 @@ declare global {
Component, Component,
ComponentPublicInstance, ComponentPublicInstance,
ComputedRef, ComputedRef,
ExtractDefaultPropTypes,
ExtractPropTypes,
ExtractPublicPropTypes,
InjectionKey, InjectionKey,
PropType, PropType,
Ref, Ref,
VNode, VNode,
WritableComputedRef,
} from "vue"; } from "vue";
} }
// for vue template auto import // for vue template auto import
@ -305,7 +309,6 @@ declare module "vue" {
readonly ElMessageBox: UnwrapRef< readonly ElMessageBox: UnwrapRef<
typeof import("element-plus/es")["ElMessageBox"] typeof import("element-plus/es")["ElMessageBox"]
>; >;
readonly ElTree: UnwrapRef<typeof import("element-plus/es")["ElTree"]>;
readonly asyncComputed: UnwrapRef< readonly asyncComputed: UnwrapRef<
typeof import("@vueuse/core")["asyncComputed"] typeof import("@vueuse/core")["asyncComputed"]
>; >;
@ -385,6 +388,9 @@ declare module "vue" {
typeof import("@vueuse/core")["ignorableWatch"] typeof import("@vueuse/core")["ignorableWatch"]
>; >;
readonly inject: UnwrapRef<typeof import("vue")["inject"]>; readonly inject: UnwrapRef<typeof import("vue")["inject"]>;
readonly injectLocal: UnwrapRef<
typeof import("@vueuse/core")["injectLocal"]
>;
readonly isDefined: UnwrapRef<typeof import("@vueuse/core")["isDefined"]>; readonly isDefined: UnwrapRef<typeof import("@vueuse/core")["isDefined"]>;
readonly isProxy: UnwrapRef<typeof import("vue")["isProxy"]>; readonly isProxy: UnwrapRef<typeof import("vue")["isProxy"]>;
readonly isReactive: UnwrapRef<typeof import("vue")["isReactive"]>; readonly isReactive: UnwrapRef<typeof import("vue")["isReactive"]>;
@ -434,6 +440,9 @@ declare module "vue" {
typeof import("@vueuse/core")["pausableWatch"] typeof import("@vueuse/core")["pausableWatch"]
>; >;
readonly provide: UnwrapRef<typeof import("vue")["provide"]>; readonly provide: UnwrapRef<typeof import("vue")["provide"]>;
readonly provideLocal: UnwrapRef<
typeof import("@vueuse/core")["provideLocal"]
>;
readonly reactify: UnwrapRef<typeof import("@vueuse/core")["reactify"]>; readonly reactify: UnwrapRef<typeof import("@vueuse/core")["reactify"]>;
readonly reactifyObject: UnwrapRef< readonly reactifyObject: UnwrapRef<
typeof import("@vueuse/core")["reactifyObject"] typeof import("@vueuse/core")["reactifyObject"]
@ -951,7 +960,6 @@ declare module "@vue/runtime-core" {
readonly ElMessageBox: UnwrapRef< readonly ElMessageBox: UnwrapRef<
typeof import("element-plus/es")["ElMessageBox"] typeof import("element-plus/es")["ElMessageBox"]
>; >;
readonly ElTree: UnwrapRef<typeof import("element-plus/es")["ElTree"]>;
readonly asyncComputed: UnwrapRef< readonly asyncComputed: UnwrapRef<
typeof import("@vueuse/core")["asyncComputed"] typeof import("@vueuse/core")["asyncComputed"]
>; >;
@ -1031,6 +1039,9 @@ declare module "@vue/runtime-core" {
typeof import("@vueuse/core")["ignorableWatch"] typeof import("@vueuse/core")["ignorableWatch"]
>; >;
readonly inject: UnwrapRef<typeof import("vue")["inject"]>; readonly inject: UnwrapRef<typeof import("vue")["inject"]>;
readonly injectLocal: UnwrapRef<
typeof import("@vueuse/core")["injectLocal"]
>;
readonly isDefined: UnwrapRef<typeof import("@vueuse/core")["isDefined"]>; readonly isDefined: UnwrapRef<typeof import("@vueuse/core")["isDefined"]>;
readonly isProxy: UnwrapRef<typeof import("vue")["isProxy"]>; readonly isProxy: UnwrapRef<typeof import("vue")["isProxy"]>;
readonly isReactive: UnwrapRef<typeof import("vue")["isReactive"]>; readonly isReactive: UnwrapRef<typeof import("vue")["isReactive"]>;
@ -1080,6 +1091,9 @@ declare module "@vue/runtime-core" {
typeof import("@vueuse/core")["pausableWatch"] typeof import("@vueuse/core")["pausableWatch"]
>; >;
readonly provide: UnwrapRef<typeof import("vue")["provide"]>; readonly provide: UnwrapRef<typeof import("vue")["provide"]>;
readonly provideLocal: UnwrapRef<
typeof import("@vueuse/core")["provideLocal"]
>;
readonly reactify: UnwrapRef<typeof import("@vueuse/core")["reactify"]>; readonly reactify: UnwrapRef<typeof import("@vueuse/core")["reactify"]>;
readonly reactifyObject: UnwrapRef< readonly reactifyObject: UnwrapRef<
typeof import("@vueuse/core")["reactifyObject"] typeof import("@vueuse/core")["reactifyObject"]

View File

@ -15,13 +15,10 @@ declare module "@vue/runtime-core" {
DeptTree: typeof import("./../views/system/user/components/dept-tree.vue")["default"]; DeptTree: typeof import("./../views/system/user/components/dept-tree.vue")["default"];
Dictionary: typeof import("./../components/Dictionary/index.vue")["default"]; Dictionary: typeof import("./../components/Dictionary/index.vue")["default"];
DictItem: typeof import("./../views/system/dict/components/dict-item.vue")["default"]; DictItem: typeof import("./../views/system/dict/components/dict-item.vue")["default"];
ElAlert: typeof import("element-plus/es")["ElAlert"];
ElBreadcrumb: typeof import("element-plus/es")["ElBreadcrumb"]; ElBreadcrumb: typeof import("element-plus/es")["ElBreadcrumb"];
ElBreadcrumbItem: typeof import("element-plus/es")["ElBreadcrumbItem"]; ElBreadcrumbItem: typeof import("element-plus/es")["ElBreadcrumbItem"];
ElButton: typeof import("element-plus/es")["ElButton"]; ElButton: typeof import("element-plus/es")["ElButton"];
ElCard: typeof import("element-plus/es")["ElCard"]; ElCard: typeof import("element-plus/es")["ElCard"];
ElCheckbox: typeof import("element-plus/es")["ElCheckbox"];
ElCheckboxGroup: typeof import("element-plus/es")["ElCheckboxGroup"];
ElCol: typeof import("element-plus/es")["ElCol"]; ElCol: typeof import("element-plus/es")["ElCol"];
ElDatePicker: typeof import("element-plus/es")["ElDatePicker"]; ElDatePicker: typeof import("element-plus/es")["ElDatePicker"];
ElDialog: typeof import("element-plus/es")["ElDialog"]; ElDialog: typeof import("element-plus/es")["ElDialog"];
@ -35,38 +32,35 @@ declare module "@vue/runtime-core" {
ElImage: typeof import("element-plus/es")["ElImage"]; ElImage: typeof import("element-plus/es")["ElImage"];
ElInput: typeof import("element-plus/es")["ElInput"]; ElInput: typeof import("element-plus/es")["ElInput"];
ElInputNumber: typeof import("element-plus/es")["ElInputNumber"]; ElInputNumber: typeof import("element-plus/es")["ElInputNumber"];
ElLink: typeof import("element-plus/es")["ElLink"];
ElMenu: typeof import("element-plus/es")["ElMenu"]; ElMenu: typeof import("element-plus/es")["ElMenu"];
ElMenuItem: typeof import("element-plus/es")["ElMenuItem"]; ElMenuItem: typeof import("element-plus/es")["ElMenuItem"];
ElOption: typeof import("element-plus/es")["ElOption"]; ElOption: typeof import("element-plus/es")["ElOption"];
ElPagination: typeof import("element-plus/es")["ElPagination"]; ElPagination: typeof import("element-plus/es")["ElPagination"];
ElPopover: typeof import("element-plus/es")["ElPopover"]; ElPopover: typeof import("element-plus/es")["ElPopover"];
ElRadio: typeof import("element-plus/es")["ElRadio"]; ElRadio: typeof import("element-plus/es")["ElRadio"];
ElRadioButton: typeof import("element-plus/es")["ElRadioButton"];
ElRadioGroup: typeof import("element-plus/es")["ElRadioGroup"]; ElRadioGroup: typeof import("element-plus/es")["ElRadioGroup"];
ElRate: typeof import("element-plus/es")["ElRate"];
ElRow: typeof import("element-plus/es")["ElRow"]; ElRow: typeof import("element-plus/es")["ElRow"];
ElScrollbar: typeof import("element-plus/es")["ElScrollbar"]; ElScrollbar: typeof import("element-plus/es")["ElScrollbar"];
ElSelect: typeof import("element-plus/es")["ElSelect"]; ElSelect: typeof import("element-plus/es")["ElSelect"];
ElStatistic: typeof import("element-plus/es")["ElStatistic"];
ElSubMenu: typeof import("element-plus/es")["ElSubMenu"]; ElSubMenu: typeof import("element-plus/es")["ElSubMenu"];
ElSwitch: typeof import("element-plus/es")["ElSwitch"]; ElSwitch: typeof import("element-plus/es")["ElSwitch"];
ElTable: typeof import("element-plus/es")["ElTable"]; ElTable: typeof import("element-plus/es")["ElTable"];
ElTableColumn: typeof import("element-plus/es")["ElTableColumn"]; ElTableColumn: typeof import("element-plus/es")["ElTableColumn"];
ElTabPane: typeof import("element-plus/es")["ElTabPane"];
ElTabs: typeof import("element-plus/es")["ElTabs"];
ElTag: typeof import("element-plus/es")["ElTag"]; ElTag: typeof import("element-plus/es")["ElTag"];
ElTooltip: typeof import("element-plus/es")["ElTooltip"]; ElTooltip: typeof import("element-plus/es")["ElTooltip"];
ElTree: typeof import("element-plus/es")["ElTree"]; ElTree: typeof import("element-plus/es")["ElTree"];
ElTreeSelect: typeof import("element-plus/es")["ElTreeSelect"]; ElTreeSelect: typeof import("element-plus/es")["ElTreeSelect"];
ElUpload: typeof import("element-plus/es")["ElUpload"]; ElUpload: typeof import("element-plus/es")["ElUpload"];
ElWatermark: typeof import("element-plus/es")["ElWatermark"];
FixedThead: typeof import("./../views/demo/table/dynamic-table/components/FixedThead.vue")["default"]; FixedThead: typeof import("./../views/demo/table/dynamic-table/components/FixedThead.vue")["default"];
FunnelChart: typeof import("./../views/dashboard/components/FunnelChart.vue")["default"]; FunnelChart: typeof import("./../views/dashboard/components/FunnelChart.vue")["default"];
GithubCorner: typeof import("./../components/GithubCorner/index.vue")["default"]; GithubCorner: typeof import("./../components/GithubCorner/index.vue")["default"];
Hamburger: typeof import("./../components/Hamburger/index.vue")["default"]; Hamburger: typeof import("./../components/Hamburger/index.vue")["default"];
IconSelect: typeof import("./../components/IconSelect/index.vue")["default"]; IconSelect: typeof import("./../components/IconSelect/index.vue")["default"];
IEpArrowDown: typeof import("~icons/ep/arrow-down")["default"];
IEpCaretBottom: typeof import("~icons/ep/caret-bottom")["default"]; IEpCaretBottom: typeof import("~icons/ep/caret-bottom")["default"];
IEpCaretTop: typeof import("~icons/ep/caret-top")["default"]; IEpCaretTop: typeof import("~icons/ep/caret-top")["default"];
IEpCheck: typeof import("~icons/ep/check")["default"];
IEpClose: typeof import("~icons/ep/close")["default"]; IEpClose: typeof import("~icons/ep/close")["default"];
IEpCollection: typeof import("~icons/ep/collection")["default"]; IEpCollection: typeof import("~icons/ep/collection")["default"];
IEpDelete: typeof import("~icons/ep/delete")["default"]; IEpDelete: typeof import("~icons/ep/delete")["default"];
@ -75,12 +69,11 @@ declare module "@vue/runtime-core" {
IEpPicture: typeof import("~icons/ep/picture")["default"]; IEpPicture: typeof import("~icons/ep/picture")["default"];
IEpPlus: typeof import("~icons/ep/plus")["default"]; IEpPlus: typeof import("~icons/ep/plus")["default"];
IEpPosition: typeof import("~icons/ep/position")["default"]; IEpPosition: typeof import("~icons/ep/position")["default"];
IEpQuestionFilled: typeof import("~icons/ep/question-filled")["default"];
IEpRefresh: typeof import("~icons/ep/refresh")["default"]; IEpRefresh: typeof import("~icons/ep/refresh")["default"];
IEpRefreshLeft: typeof import("~icons/ep/refresh-left")["default"]; IEpRefreshLeft: typeof import("~icons/ep/refresh-left")["default"];
IEpSearch: typeof import("~icons/ep/search")["default"]; IEpSearch: typeof import("~icons/ep/search")["default"];
IEpSetting: typeof import("~icons/ep/setting")["default"]; IEpSetting: typeof import("~icons/ep/setting")["default"];
IEpSortDown: typeof import("~icons/ep/sort-down")["default"];
IEpSortUp: typeof import("~icons/ep/sort-up")["default"];
IEpTop: typeof import("~icons/ep/top")["default"]; IEpTop: typeof import("~icons/ep/top")["default"];
IEpUploadFilled: typeof import("~icons/ep/upload-filled")["default"]; IEpUploadFilled: typeof import("~icons/ep/upload-filled")["default"];
Item: typeof import("./../layout/components/Sidebar/Item.vue")["default"]; Item: typeof import("./../layout/components/Sidebar/Item.vue")["default"];
@ -105,7 +98,6 @@ declare module "@vue/runtime-core" {
SizeSelect: typeof import("./../components/SizeSelect/index.vue")["default"]; SizeSelect: typeof import("./../components/SizeSelect/index.vue")["default"];
SvgIcon: typeof import("./../components/SvgIcon/index.vue")["default"]; SvgIcon: typeof import("./../components/SvgIcon/index.vue")["default"];
SwitchRoles: typeof import("./../views/demo/permission/components/SwitchRoles.vue")["default"]; SwitchRoles: typeof import("./../views/demo/permission/components/SwitchRoles.vue")["default"];
TagInput: typeof import("./../components/TagInput/index.vue")["default"];
TagsView: typeof import("./../layout/components/TagsView/index.vue")["default"]; TagsView: typeof import("./../layout/components/TagsView/index.vue")["default"];
TopMenu: typeof import("./../layout/components/Sidebar/TopMenu.vue")["default"]; TopMenu: typeof import("./../layout/components/Sidebar/TopMenu.vue")["default"];
UnfixedThead: typeof import("./../views/demo/table/dynamic-table/components/UnfixedThead.vue")["default"]; UnfixedThead: typeof import("./../views/demo/table/dynamic-table/components/UnfixedThead.vue")["default"];

55
src/types/global.d.ts vendored
View File

@ -1,55 +0,0 @@
declare global {
/**
*
*/
interface PageQuery {
pageNum: number;
pageSize: number;
}
/**
*
*/
interface PageResult<T> {
/**
*
*/
list: T;
/**
*
*/
total: number;
}
/**
*
*/
interface DialogOption {
/**
*
*/
title?: string;
/**
*
*/
visible: boolean;
}
/**
*
*/
interface OptionType {
/**
*
*/
value: string | number;
/**
*
*/
label: string;
/**
*
*/
children?: OptionType[];
}
}
export {};

View File

@ -18,10 +18,8 @@
class="z-1 !border-none w-100 !bg-transparent !rounded-4% <sm:w-83" class="z-1 !border-none w-100 !bg-transparent !rounded-4% <sm:w-83"
> >
<div class="text-center relative"> <div class="text-center relative">
<h2>{{ defaultSettings.title }}</h2> <h2>{{ title }}</h2>
<el-tag class="ml-2 absolute top-0 right-0">{{ <el-tag class="ml-2 absolute top-0 right-0">{{ version }}</el-tag>
defaultSettings.version
}}</el-tag>
</div> </div>
<el-form <el-form
ref="loginFormRef" ref="loginFormRef"
@ -147,12 +145,13 @@ import { useAppStore } from "@/store/modules/app";
import { LocationQuery, LocationQueryValue, useRoute } from "vue-router"; import { LocationQuery, LocationQueryValue, useRoute } from "vue-router";
import { getCaptchaApi } from "@/api/auth"; import { getCaptchaApi } from "@/api/auth";
import { LoginData } from "@/api/auth/types"; import { LoginData } from "@/api/auth/types";
import defaultSettings from "@/settings";
const settingsStore = useSettingsStore();
const { title, version } = settingsStore;
/** /**
* 明亮/暗黑主题切换 * 明亮/暗黑主题切换
*/ */
const settingsStore = useSettingsStore();
const isDark = ref<boolean>(settingsStore.theme === "dark"); const isDark = ref<boolean>(settingsStore.theme === "dark");
const handleThemeChange = (isDark: any) => { const handleThemeChange = (isDark: any) => {
useToggle(isDark); useToggle(isDark);

View File

@ -20,9 +20,10 @@ const deptFormRef = ref(ElForm);
const loading = ref(false); const loading = ref(false);
const ids = ref<number[]>([]); const ids = ref<number[]>([]);
const dialog = reactive<DialogOption>({ const dialog = {
title: "",
visible: false, visible: false,
}); };
const queryParams = reactive<DeptQuery>({}); const queryParams = reactive<DeptQuery>({});
const deptList = ref<DeptVO[]>(); const deptList = ref<DeptVO[]>();

View File

@ -53,9 +53,10 @@ const queryParams = reactive<DictQuery>({
const dictList = ref<DictPageVO[]>(); const dictList = ref<DictPageVO[]>();
const dialog = reactive<DialogOption>({ const dialog = {
title: "",
visible: false, visible: false,
}); };
const formData = reactive<DictForm>({ const formData = reactive<DictForm>({
status: 1, status: 1,

View File

@ -29,9 +29,10 @@ const queryParams = reactive<DictTypeQuery>({
const dictTypeList = ref<DictTypePageVO[]>(); const dictTypeList = ref<DictTypePageVO[]>();
const dialog = reactive<DialogOption>({ const dialog = {
title: "",
visible: false, visible: false,
}); };
const formData = reactive<DictTypeForm>({ const formData = reactive<DictTypeForm>({
status: 1, status: 1,
@ -148,9 +149,10 @@ function handleDelete(dictTypeId?: number) {
}); });
} }
const dictDataDialog = reactive<DialogOption>({ const dictDataDialog = {
title: "",
visible: false, visible: false,
}); };
const selectedDictType = reactive({ typeCode: "", typeName: "" }); // const selectedDictType = reactive({ typeCode: "", typeName: "" }); //

View File

@ -24,9 +24,10 @@ const queryFormRef = ref(ElForm);
const menuFormRef = ref(ElForm); const menuFormRef = ref(ElForm);
const loading = ref(false); const loading = ref(false);
const dialog = reactive<DialogOption>({ const dialog = {
title: "",
visible: false, visible: false,
}); };
const queryParams = reactive<MenuQuery>({}); const queryParams = reactive<MenuQuery>({});
const menuList = ref<MenuVO[]>([]); const menuList = ref<MenuVO[]>([]);

View File

@ -32,9 +32,10 @@ const queryParams = reactive<RoleQuery>({
const roleList = ref<RolePageVO[]>(); const roleList = ref<RolePageVO[]>();
const dialog = reactive<DialogOption>({ const dialog = {
title: "",
visible: false, visible: false,
}); };
const formData = reactive<RoleForm>({ const formData = reactive<RoleForm>({
sort: 1, sort: 1,

View File

@ -27,7 +27,7 @@
"include": [ "include": [
"src/**/*.ts", "src/**/*.ts",
"src/**/*.vue", "src/**/*.vue",
"src/types/**/*.d.ts", "types/**/*.d.ts",
"mock/**/*.ts", "mock/**/*.ts",
"vite.config.ts" "vite.config.ts"
], ],

1589
types/auto-imports.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

117
types/components.d.ts vendored Normal file
View File

@ -0,0 +1,117 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {};
declare module "@vue/runtime-core" {
export interface GlobalComponents {
AppMain: typeof import("./../layout/components/AppMain.vue")["default"];
BarChart: typeof import("./../views/dashboard/components/BarChart.vue")["default"];
Breadcrumb: typeof import("./../components/Breadcrumb/index.vue")["default"];
DeptTree: typeof import("./../views/system/user/components/dept-tree.vue")["default"];
Dictionary: typeof import("./../components/Dictionary/index.vue")["default"];
DictItem: typeof import("./../views/system/dict/components/dict-item.vue")["default"];
ElAlert: typeof import("element-plus/es")["ElAlert"];
ElBreadcrumb: typeof import("element-plus/es")["ElBreadcrumb"];
ElBreadcrumbItem: typeof import("element-plus/es")["ElBreadcrumbItem"];
ElButton: typeof import("element-plus/es")["ElButton"];
ElCard: typeof import("element-plus/es")["ElCard"];
ElCheckbox: typeof import("element-plus/es")["ElCheckbox"];
ElCheckboxGroup: typeof import("element-plus/es")["ElCheckboxGroup"];
ElCol: typeof import("element-plus/es")["ElCol"];
ElDatePicker: typeof import("element-plus/es")["ElDatePicker"];
ElDialog: typeof import("element-plus/es")["ElDialog"];
ElDivider: typeof import("element-plus/es")["ElDivider"];
ElDropdown: typeof import("element-plus/es")["ElDropdown"];
ElDropdownItem: typeof import("element-plus/es")["ElDropdownItem"];
ElDropdownMenu: typeof import("element-plus/es")["ElDropdownMenu"];
ElForm: typeof import("element-plus/es")["ElForm"];
ElFormItem: typeof import("element-plus/es")["ElFormItem"];
ElIcon: typeof import("element-plus/es")["ElIcon"];
ElImage: typeof import("element-plus/es")["ElImage"];
ElInput: typeof import("element-plus/es")["ElInput"];
ElInputNumber: typeof import("element-plus/es")["ElInputNumber"];
ElLink: typeof import("element-plus/es")["ElLink"];
ElMenu: typeof import("element-plus/es")["ElMenu"];
ElMenuItem: typeof import("element-plus/es")["ElMenuItem"];
ElOption: typeof import("element-plus/es")["ElOption"];
ElPagination: typeof import("element-plus/es")["ElPagination"];
ElPopover: typeof import("element-plus/es")["ElPopover"];
ElRadio: typeof import("element-plus/es")["ElRadio"];
ElRadioButton: typeof import("element-plus/es")["ElRadioButton"];
ElRadioGroup: typeof import("element-plus/es")["ElRadioGroup"];
ElRate: typeof import("element-plus/es")["ElRate"];
ElRow: typeof import("element-plus/es")["ElRow"];
ElScrollbar: typeof import("element-plus/es")["ElScrollbar"];
ElSelect: typeof import("element-plus/es")["ElSelect"];
ElSubMenu: typeof import("element-plus/es")["ElSubMenu"];
ElSwitch: typeof import("element-plus/es")["ElSwitch"];
ElTable: typeof import("element-plus/es")["ElTable"];
ElTableColumn: typeof import("element-plus/es")["ElTableColumn"];
ElTabPane: typeof import("element-plus/es")["ElTabPane"];
ElTabs: typeof import("element-plus/es")["ElTabs"];
ElTag: typeof import("element-plus/es")["ElTag"];
ElTooltip: typeof import("element-plus/es")["ElTooltip"];
ElTree: typeof import("element-plus/es")["ElTree"];
ElTreeSelect: typeof import("element-plus/es")["ElTreeSelect"];
ElUpload: typeof import("element-plus/es")["ElUpload"];
FixedThead: typeof import("./../views/demo/table/dynamic-table/components/FixedThead.vue")["default"];
FunnelChart: typeof import("./../views/dashboard/components/FunnelChart.vue")["default"];
GithubCorner: typeof import("./../components/GithubCorner/index.vue")["default"];
Hamburger: typeof import("./../components/Hamburger/index.vue")["default"];
IconSelect: typeof import("./../components/IconSelect/index.vue")["default"];
IEpArrowDown: typeof import("~icons/ep/arrow-down")["default"];
IEpCaretBottom: typeof import("~icons/ep/caret-bottom")["default"];
IEpCaretTop: typeof import("~icons/ep/caret-top")["default"];
IEpClose: typeof import("~icons/ep/close")["default"];
IEpCollection: typeof import("~icons/ep/collection")["default"];
IEpDelete: typeof import("~icons/ep/delete")["default"];
IEpDownload: typeof import("~icons/ep/download")["default"];
IEpEdit: typeof import("~icons/ep/edit")["default"];
IEpPicture: typeof import("~icons/ep/picture")["default"];
IEpPlus: typeof import("~icons/ep/plus")["default"];
IEpPosition: typeof import("~icons/ep/position")["default"];
IEpRefresh: typeof import("~icons/ep/refresh")["default"];
IEpRefreshLeft: typeof import("~icons/ep/refresh-left")["default"];
IEpSearch: typeof import("~icons/ep/search")["default"];
IEpSetting: typeof import("~icons/ep/setting")["default"];
IEpSortDown: typeof import("~icons/ep/sort-down")["default"];
IEpSortUp: typeof import("~icons/ep/sort-up")["default"];
IEpTop: typeof import("~icons/ep/top")["default"];
IEpUploadFilled: typeof import("~icons/ep/upload-filled")["default"];
Item: typeof import("./../layout/components/Sidebar/Item.vue")["default"];
LangSelect: typeof import("./../components/LangSelect/index.vue")["default"];
LeftMenu: typeof import("./../layout/components/Sidebar/LeftMenu.vue")["default"];
Link: typeof import("./../layout/components/Sidebar/Link.vue")["default"];
Logo: typeof import("./../layout/components/Sidebar/Logo.vue")["default"];
MultiUpload: typeof import("./../components/Upload/MultiUpload.vue")["default"];
NavBar: typeof import("./../layout/components/NavBar/index.vue")["default"];
NavRight: typeof import("./../layout/components/NavBar/NavRight.vue")["default"];
Pagination: typeof import("./../components/Pagination/index.vue")["default"];
PieChart: typeof import("./../views/dashboard/components/PieChart.vue")["default"];
RadarChart: typeof import("./../views/dashboard/components/RadarChart.vue")["default"];
RightPanel: typeof import("./../components/RightPanel/index.vue")["default"];
RouterLink: typeof import("vue-router")["RouterLink"];
RouterView: typeof import("vue-router")["RouterView"];
ScrollPane: typeof import("./../layout/components/TagsView/ScrollPane.vue")["default"];
Settings: typeof import("./../layout/components/Settings/index.vue")["default"];
Sidebar: typeof import("./../layout/components/Sidebar/index.vue")["default"];
SidebarItem: typeof import("./../layout/components/Sidebar/SidebarItem.vue")["default"];
SingleUpload: typeof import("./../components/Upload/SingleUpload.vue")["default"];
SizeSelect: typeof import("./../components/SizeSelect/index.vue")["default"];
SvgIcon: typeof import("./../components/SvgIcon/index.vue")["default"];
SwitchRoles: typeof import("./../views/demo/permission/components/SwitchRoles.vue")["default"];
TagInput: typeof import("./../components/TagInput/index.vue")["default"];
TagsView: typeof import("./../layout/components/TagsView/index.vue")["default"];
TopMenu: typeof import("./../layout/components/Sidebar/TopMenu.vue")["default"];
UnfixedThead: typeof import("./../views/demo/table/dynamic-table/components/UnfixedThead.vue")["default"];
WangEditor: typeof import("./../components/WangEditor/index.vue")["default"];
}
export interface ComponentCustomProperties {
vLoading: typeof import("element-plus/es")["ElLoadingDirective"];
}
}

View File

@ -7,15 +7,14 @@ declare module "*.vue" {
export default component; export default component;
} }
// 环境变量 TypeScript的智能提示
interface ImportMetaEnv { interface ImportMetaEnv {
VITE_APP_TITLE: string; /** 应用端口 */
VITE_APP_PORT: string; VITE_APP_PORT: string;
/** API 基础路径 */
VITE_APP_BASE_API: string; VITE_APP_BASE_API: string;
VITE_APP_API_URL: string;
} }
interface ImportMeta { interface ImportMeta {
readonly env: ImportMetaEnv; readonly env: ImportMetaEnv;
} }
declare module "lodash-es";

102
types/global.d.ts vendored Normal file
View File

@ -0,0 +1,102 @@
declare global {
/**
*
*/
const __APP_INFO__: {
pkg: {
name: string;
version: string;
dependencies: Recordable<string>;
devDependencies: Recordable<string>;
};
lastBuildTime: string;
};
/**
*
*/
interface PageQuery {
pageNum: number;
pageSize: number;
}
/**
*
*/
interface PageResult<T> {
/** 数据列表 */
list: T;
/** 总数 */
total: number;
}
/**
*
*/
interface TagView {
/** 页签名称 */
name: string;
/** 页签标题 */
title: string;
/** 页签路由路径 */
path: string;
/** 页签路由完整路径 */
fullPath: string;
/** 页签图标 */
icon?: string;
/** 是否固定页签 */
affix?: boolean;
/** 是否开启缓存 */
keepAlive?: boolean;
/** 路由查询参数 */
query?: any;
}
/**
*
*/
interface AppSettings {
/** 系统标题 */
title: string;
/** 系统版本 */
version: string;
/** 是否显示设置 */
showSettings: boolean;
/** 是否固定头部 */
fixedHeader: boolean;
/** 是否显示多标签导航 */
tagsView: boolean;
/** 是否显示侧边栏Logo */
sidebarLogo: boolean;
/** 导航栏布局(left|top|mix) */
layout: string;
/** 主题颜色 */
themeColor: string;
/** 主题模式(dark|light) */
theme: string;
/** 布局大小(default |large |small) */
size: string;
/** 语言( zh-cn| en) */
language: string;
/** 水印配置 */
watermark: {
/** 是否开启水印 */
enabled: boolean;
/** 水印内容 */
content: string;
};
}
/**
*
*/
interface OptionType {
/** 值 */
value: string | number;
/** 文本 */
label: string;
/** 子列表 */
children?: OptionType[];
}
}
export {};

22
types/router.d.ts vendored Normal file
View File

@ -0,0 +1,22 @@
import "vue-router";
declare module "vue-router" {
// https://router.vuejs.org/zh/guide/advanced/meta.html#typescript
// 可以通过扩展 RouteMeta 接口来输入 meta 字段
interface RouteMeta {
/** 菜单名称 */
title: string;
/** 菜单图标 */
icon?: string;
/** 菜单是否隐藏 */
hidden?: boolean;
/** 是否固定页签 */
affix?: boolean;
/** 是否缓存页面 */
keepAlive?: boolean;
/** 是否在面包屑上隐藏 */
breadcrumb?: boolean;
/** 拥有菜单权限的角色编码集合 */
roles?: string[];
}
}

View File

@ -1,5 +1,4 @@
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import { UserConfig, ConfigEnv, loadEnv, defineConfig } from "vite"; import { UserConfig, ConfigEnv, loadEnv, defineConfig } from "vite";
import AutoImport from "unplugin-auto-import/vite"; import AutoImport from "unplugin-auto-import/vite";
@ -12,14 +11,13 @@ import IconsResolver from "unplugin-icons/resolver";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import { viteMockServe } from "vite-plugin-mock"; import { viteMockServe } from "vite-plugin-mock";
import vueJsx from "@vitejs/plugin-vue-jsx"; import vueJsx from "@vitejs/plugin-vue-jsx";
import UnoCSS from "unocss/vite"; import UnoCSS from "unocss/vite";
import path from "path"; import { resolve } from "path";
const pathSrc = path.resolve(__dirname, "src"); const pathSrc = resolve(__dirname, "src");
// 参考Vite官方 https://cn.vitejs.dev/config // https://cn.vitejs.dev/config
export default defineConfig(({ mode }: ConfigEnv): UserConfig => { export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd()); const env = loadEnv(mode, process.cwd());
return { return {
@ -53,17 +51,13 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
* http://localhost:3000/dev-api/users (F12可见请求路径) => http://localhost:8989/users (实际请求后端 API 路径) * http://localhost:3000/dev-api/users (F12可见请求路径) => http://localhost:8989/users (实际请求后端 API 路径)
* *
* env.VITE_APP_BASE_API: /dev-api * env.VITE_APP_BASE_API: /dev-api
* env.VITE_APP_TARGET_URL: http://localhost:8989 * env.VITE_APP_API_URL: http://localhost:8989
* env.VITE_APP_TARGET_BASE_API: ""
*/ */
[env.VITE_APP_BASE_API]: { [env.VITE_APP_BASE_API]: {
changeOrigin: true, changeOrigin: true,
target: env.VITE_APP_TARGET_URL, target: env.VITE_APP_API_URL,
rewrite: (path) => rewrite: (path) =>
path.replace( path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
new RegExp("^" + env.VITE_APP_BASE_API),
env.VITE_APP_TARGET_BASE_API
),
}, },
}, },
}, },
@ -86,8 +80,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
}, },
vueTemplate: true, vueTemplate: true,
// 配置文件生成位置(false:关闭自动生成) // 配置文件生成位置(false:关闭自动生成)
dts: false, //dts: false,
// dts: "src/types/auto-imports.d.ts", dts: "src/types/auto-imports.d.ts",
}), }),
Components({ Components({
@ -100,8 +94,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
// 指定自定义组件位置(默认:src/components) // 指定自定义组件位置(默认:src/components)
dirs: ["src/components", "src/**/components"], dirs: ["src/components", "src/**/components"],
// 配置文件位置 (false:关闭自动生成) // 配置文件位置 (false:关闭自动生成)
dts: false, // dts: false,
// dts: "src/types/components.d.ts", dts: "src/types/components.d.ts",
}), }),
Icons({ Icons({
@ -109,7 +103,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
}), }),
createSvgIconsPlugin({ createSvgIconsPlugin({
// 指定需要缓存的图标文件夹 // 指定需要缓存的图标文件夹
iconDirs: [path.resolve(pathSrc, "assets/icons")], iconDirs: [resolve(pathSrc, "assets/icons")],
// 指定symbolId格式 // 指定symbolId格式
symbolId: "icon-[dir]-[name]", symbolId: "icon-[dir]-[name]",
}), }),
@ -173,6 +167,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
"element-plus/es/components/notification/style/css", "element-plus/es/components/notification/style/css",
"element-plus/es/components/image/style/css", "element-plus/es/components/image/style/css",
"element-plus/es/components/statistic/style/css", "element-plus/es/components/statistic/style/css",
"element-plus/es/components/watermark/style/css",
"@vueuse/core", "@vueuse/core",
"sortablejs", "sortablejs",
"path-to-regexp", "path-to-regexp",