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

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

View File

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

View File

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

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
globals: {
DialogOption: "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**
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) 源码 ;
> 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",
"private": true,
"version": "2.6.7",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "vite serve --mode development",
@ -63,7 +63,8 @@
"vue": "^3.3.8",
"vue-i18n": "9.2.2",
"vue-router": "^4.2.5",
"xlsx": "^0.18.5"
"xlsx": "^0.18.5",
"dayjs": "^1.11.10"
},
"devDependencies": {
"@commitlint/cli": "^17.8.1",

View File

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

View File

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

View File

@ -61,13 +61,6 @@ onMounted(() => {
</template>
</template>
</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-scrollbar>
</template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { useTagsViewStore, TagView } from "@/store/modules/tagsView";
import { useTagsViewStore } from "@/store/modules/tagsView";
const tagAndTagSpacing = ref(4);
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">
import { getCurrentInstance, ComponentInternalInstance } from "vue";
import { storeToRefs } from "pinia";
import path from "path-browserify";
import { useRoute, useRouter } from "vue-router";
import { useRoute, useRouter, RouteRecordRaw } from "vue-router";
import { resolve } from "path-browserify";
import { translateRouteTitle } from "@/utils/i18n";
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 { useAppStore } from "@/store/modules/app";
import ScrollPane from "./ScrollPane.vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { proxy } = getCurrentInstance()!;
const router = useRouter();
const route = useRoute();
@ -27,11 +82,19 @@ const { visitedViews } = storeToRefs(tagsViewStore);
const settingsStore = useSettingsStore();
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 left = ref(0);
const top = ref(0);
const affixTags = ref<TagView[]>([]);
watch(
route,
@ -40,8 +103,7 @@ watch(
moveToCurrentTag();
},
{
//
immediate: true,
immediate: true, //
}
);
@ -54,27 +116,31 @@ watch(tagMenuVisible, (value) => {
}
});
function filterAffixTags(routes: any[], basePath = "/") {
let tags: TagView[] = [];
function filterAffixTags(routes: RouteRecordRaw[], basePath = "/") {
const processRoute = (route: RouteRecordRaw) => {
const fullPath = resolve(basePath, route.path);
routes.forEach((route) => {
if (route.meta && route.meta.affix) {
const tagPath = path.resolve(basePath, route.path);
tags.push({
fullPath: tagPath,
path: tagPath,
name: route.name,
meta: { ...route.meta },
});
const tag: TagView = {
path: route.path,
fullPath,
name: String(route.name),
title: route.meta?.title || "no-name",
affix: route.meta?.affix,
keepAlive: route.meta?.keepAlive,
};
if (tag.affix) {
tags.push(tag);
}
if (route.children) {
const childTags = filterAffixTags(route.children, route.path);
if (childTags.length >= 1) {
tags = tags.concat(childTags);
}
route.children.forEach(processRoute);
}
});
};
let tags: TagView[] = [];
routes.forEach(processRoute);
return tags;
}
@ -90,19 +156,35 @@ function initTags() {
}
function addTags() {
if (route.name) {
tagsViewStore.addView(route);
if (route.meta.title) {
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() {
// 使 nextTick() tagsView scrollPaneRef
nextTick(() => {
for (const r of tagsViewStore.visitedViews) {
if (r.path === route.path) {
scrollPaneRef.value.moveToTarget(r);
for (const tag of visitedViews.value) {
if (tag.path === route.path) {
scrollPaneRef.value.moveToTarget(tag);
// when query is different then update
if (r.fullPath !== route.fullPath) {
tagsViewStore.updateVisitedView(route);
route.query = { ...route.query, ...tag.query };
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) {
return tag.path === route.path;
return tag.fullPath === route.fullPath;
}
function isAffix(tag: TagView) {
return tag.meta && tag.meta.affix;
return tag?.affix;
}
function isFirstView() {
try {
return (
(selectedTag.value as TagView).fullPath === "/dashboard" ||
(selectedTag.value as TagView).fullPath ===
tagsViewStore.visitedViews[1].fullPath
selectedTag.value.fullPath === "/dashboard" ||
selectedTag.value.fullPath === tagsViewStore.visitedViews[1].fullPath
);
} catch (err) {
return false;
@ -132,7 +213,7 @@ function isFirstView() {
function isLastView() {
try {
return (
(selectedTag.value as TagView).fullPath ===
selectedTag.value.fullPath ===
tagsViewStore.visitedViews[tagsViewStore.visitedViews.length - 1].fullPath
);
} catch (err) {
@ -144,20 +225,18 @@ function refreshSelectedTag(view: TagView) {
tagsViewStore.delCachedView(view);
const { fullPath } = view;
nextTick(() => {
router.replace({ path: "/redirect" + fullPath }).catch((err) => {
console.warn(err);
});
router.replace({ path: "/redirect" + fullPath });
});
}
function toLastView(visitedViews: TagView[], view?: any) {
function toLastView(visitedViews: TagView[], view?: TagView) {
const latestView = visitedViews.slice(-1)[0];
if (latestView && latestView.fullPath) {
router.push(latestView.fullPath);
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view.name === "Dashboard") {
if (view?.name === "Dashboard") {
// to reload home page
router.replace({ path: "/redirect" + view.fullPath });
} else {
@ -289,63 +368,6 @@ onMounted(() => {
});
</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>
.tags-container {
width: 100%;

View File

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

View File

@ -1,68 +1,16 @@
/**
*
*/
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 = {
const defaultSettings: AppSettings = {
title: "vue3-element-admin",
version: "v2.6.7",
showSettings: true,
tagsView: true,
fixedHeader: false,
sidebarLogo: true,
layout: "left", // 默认左侧模式
theme: "light", // 暗黑模式-dark 明亮模式-light
layout: "left",
theme: "light",
size: "default",
language: "zh-cn",
themeColor: "#409EFF",
watermark: { enabled: false, content: "有来技术" },
};
export default defaultSettings;

View File

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

View File

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

View File

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

View File

@ -7,9 +7,7 @@ declare global {
const EffectScope: typeof import("vue")["EffectScope"];
const ElForm: typeof import("element-plus/es")["ElForm"];
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 ElTree: typeof import("element-plus/es")["ElTree"];
const asyncComputed: typeof import("@vueuse/core")["asyncComputed"];
const autoResetRef: typeof import("@vueuse/core")["autoResetRef"];
const computed: typeof import("vue")["computed"];
@ -41,6 +39,7 @@ declare global {
const h: typeof import("vue")["h"];
const ignorableWatch: typeof import("@vueuse/core")["ignorableWatch"];
const inject: typeof import("vue")["inject"];
const injectLocal: typeof import("@vueuse/core")["injectLocal"];
const isDefined: typeof import("@vueuse/core")["isDefined"];
const isProxy: typeof import("vue")["isProxy"];
const isReactive: typeof import("vue")["isReactive"];
@ -68,6 +67,7 @@ declare global {
const onUpdated: typeof import("vue")["onUpdated"];
const pausableWatch: typeof import("@vueuse/core")["pausableWatch"];
const provide: typeof import("vue")["provide"];
const provideLocal: typeof import("@vueuse/core")["provideLocal"];
const reactify: typeof import("@vueuse/core")["reactify"];
const reactifyObject: typeof import("@vueuse/core")["reactifyObject"];
const reactive: typeof import("vue")["reactive"];
@ -287,10 +287,14 @@ declare global {
Component,
ComponentPublicInstance,
ComputedRef,
ExtractDefaultPropTypes,
ExtractPropTypes,
ExtractPublicPropTypes,
InjectionKey,
PropType,
Ref,
VNode,
WritableComputedRef,
} from "vue";
}
// for vue template auto import
@ -305,7 +309,6 @@ declare module "vue" {
readonly ElMessageBox: UnwrapRef<
typeof import("element-plus/es")["ElMessageBox"]
>;
readonly ElTree: UnwrapRef<typeof import("element-plus/es")["ElTree"]>;
readonly asyncComputed: UnwrapRef<
typeof import("@vueuse/core")["asyncComputed"]
>;
@ -385,6 +388,9 @@ declare module "vue" {
typeof import("@vueuse/core")["ignorableWatch"]
>;
readonly inject: UnwrapRef<typeof import("vue")["inject"]>;
readonly injectLocal: UnwrapRef<
typeof import("@vueuse/core")["injectLocal"]
>;
readonly isDefined: UnwrapRef<typeof import("@vueuse/core")["isDefined"]>;
readonly isProxy: UnwrapRef<typeof import("vue")["isProxy"]>;
readonly isReactive: UnwrapRef<typeof import("vue")["isReactive"]>;
@ -434,6 +440,9 @@ declare module "vue" {
typeof import("@vueuse/core")["pausableWatch"]
>;
readonly provide: UnwrapRef<typeof import("vue")["provide"]>;
readonly provideLocal: UnwrapRef<
typeof import("@vueuse/core")["provideLocal"]
>;
readonly reactify: UnwrapRef<typeof import("@vueuse/core")["reactify"]>;
readonly reactifyObject: UnwrapRef<
typeof import("@vueuse/core")["reactifyObject"]
@ -951,7 +960,6 @@ declare module "@vue/runtime-core" {
readonly ElMessageBox: UnwrapRef<
typeof import("element-plus/es")["ElMessageBox"]
>;
readonly ElTree: UnwrapRef<typeof import("element-plus/es")["ElTree"]>;
readonly asyncComputed: UnwrapRef<
typeof import("@vueuse/core")["asyncComputed"]
>;
@ -1031,6 +1039,9 @@ declare module "@vue/runtime-core" {
typeof import("@vueuse/core")["ignorableWatch"]
>;
readonly inject: UnwrapRef<typeof import("vue")["inject"]>;
readonly injectLocal: UnwrapRef<
typeof import("@vueuse/core")["injectLocal"]
>;
readonly isDefined: UnwrapRef<typeof import("@vueuse/core")["isDefined"]>;
readonly isProxy: UnwrapRef<typeof import("vue")["isProxy"]>;
readonly isReactive: UnwrapRef<typeof import("vue")["isReactive"]>;
@ -1080,6 +1091,9 @@ declare module "@vue/runtime-core" {
typeof import("@vueuse/core")["pausableWatch"]
>;
readonly provide: UnwrapRef<typeof import("vue")["provide"]>;
readonly provideLocal: UnwrapRef<
typeof import("@vueuse/core")["provideLocal"]
>;
readonly reactify: UnwrapRef<typeof import("@vueuse/core")["reactify"]>;
readonly reactifyObject: UnwrapRef<
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"];
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"];
@ -35,38 +32,35 @@ declare module "@vue/runtime-core" {
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"];
ElStatistic: typeof import("element-plus/es")["ElStatistic"];
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"];
ElWatermark: typeof import("element-plus/es")["ElWatermark"];
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"];
IEpCheck: typeof import("~icons/ep/check")["default"];
IEpClose: typeof import("~icons/ep/close")["default"];
IEpCollection: typeof import("~icons/ep/collection")["default"];
IEpDelete: typeof import("~icons/ep/delete")["default"];
@ -75,12 +69,11 @@ declare module "@vue/runtime-core" {
IEpPicture: typeof import("~icons/ep/picture")["default"];
IEpPlus: typeof import("~icons/ep/plus")["default"];
IEpPosition: typeof import("~icons/ep/position")["default"];
IEpQuestionFilled: typeof import("~icons/ep/question-filled")["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"];
@ -105,7 +98,6 @@ declare module "@vue/runtime-core" {
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"];

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"
>
<div class="text-center relative">
<h2>{{ defaultSettings.title }}</h2>
<el-tag class="ml-2 absolute top-0 right-0">{{
defaultSettings.version
}}</el-tag>
<h2>{{ title }}</h2>
<el-tag class="ml-2 absolute top-0 right-0">{{ version }}</el-tag>
</div>
<el-form
ref="loginFormRef"
@ -147,12 +145,13 @@ import { useAppStore } from "@/store/modules/app";
import { LocationQuery, LocationQueryValue, useRoute } from "vue-router";
import { getCaptchaApi } from "@/api/auth";
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 handleThemeChange = (isDark: any) => {
useToggle(isDark);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,7 +27,7 @@
"include": [
"src/**/*.ts",
"src/**/*.vue",
"src/types/**/*.d.ts",
"types/**/*.d.ts",
"mock/**/*.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;
}
// 环境变量 TypeScript的智能提示
interface ImportMetaEnv {
VITE_APP_TITLE: string;
/** 应用端口 */
VITE_APP_PORT: string;
/** API 基础路径 */
VITE_APP_BASE_API: string;
VITE_APP_API_URL: string;
}
interface ImportMeta {
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 { UserConfig, ConfigEnv, loadEnv, defineConfig } from "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 { viteMockServe } from "vite-plugin-mock";
import vueJsx from "@vitejs/plugin-vue-jsx";
import UnoCSS from "unocss/vite";
import path from "path";
import { resolve } from "path";
const pathSrc = path.resolve(__dirname, "src");
// 参考Vite官方 https://cn.vitejs.dev/config
const pathSrc = resolve(__dirname, "src");
// https://cn.vitejs.dev/config
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd());
return {
@ -53,17 +51,13 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
* http://localhost:3000/dev-api/users (F12可见请求路径) => http://localhost:8989/users (实际请求后端 API 路径)
*
* env.VITE_APP_BASE_API: /dev-api
* env.VITE_APP_TARGET_URL: http://localhost:8989
* env.VITE_APP_TARGET_BASE_API: ""
* env.VITE_APP_API_URL: http://localhost:8989
*/
[env.VITE_APP_BASE_API]: {
changeOrigin: true,
target: env.VITE_APP_TARGET_URL,
target: env.VITE_APP_API_URL,
rewrite: (path) =>
path.replace(
new RegExp("^" + env.VITE_APP_BASE_API),
env.VITE_APP_TARGET_BASE_API
),
path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
},
},
},
@ -86,8 +80,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
},
vueTemplate: true,
// 配置文件生成位置(false:关闭自动生成)
dts: false,
// dts: "src/types/auto-imports.d.ts",
//dts: false,
dts: "src/types/auto-imports.d.ts",
}),
Components({
@ -100,8 +94,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
// 指定自定义组件位置(默认:src/components)
dirs: ["src/components", "src/**/components"],
// 配置文件位置 (false:关闭自动生成)
dts: false,
// dts: "src/types/components.d.ts",
// dts: false,
dts: "src/types/components.d.ts",
}),
Icons({
@ -109,7 +103,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
}),
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(pathSrc, "assets/icons")],
iconDirs: [resolve(pathSrc, "assets/icons")],
// 指定symbolId格式
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/image/style/css",
"element-plus/es/components/statistic/style/css",
"element-plus/es/components/watermark/style/css",
"@vueuse/core",
"sortablejs",
"path-to-regexp",