refactor: ♻️ 项目重构(临时提交)
This commit is contained in:
parent
bdcf96f713
commit
14e64324f7
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
@ -83,7 +83,6 @@ module.exports = {
|
|||
],
|
||||
// https://eslint.org/docs/latest/use/configure/language-options#specifying-globals
|
||||
globals: {
|
||||
DialogOption: "readonly",
|
||||
OptionType: "readonly",
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
"recommendations": ["Vue.volar", "lokalise.i18n-ally","esbenp.prettier-vscode"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
||||
import { translateRouteTitle } from "@/utils/i18n";
|
||||
|
||||
defineProps({
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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%;
|
||||
|
|
|
|||
|
|
@ -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,6 +80,7 @@ function toggleSideBar() {
|
|||
|
||||
<template>
|
||||
<div :class="classObj" class="app-wrapper">
|
||||
<el-watermark content="vue3-element-admin">
|
||||
<!-- 手机设备侧边栏打开遮罩层 -->
|
||||
<div
|
||||
v-if="classObj.mobile && classObj.openSidebar"
|
||||
|
|
@ -101,6 +104,7 @@ function toggleSideBar() {
|
|||
</div>
|
||||
</template>
|
||||
<Main v-else />
|
||||
</el-watermark>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
// 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");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
title,
|
||||
version,
|
||||
showSettings,
|
||||
tagsView,
|
||||
fixedHeader,
|
||||
|
|
@ -63,5 +57,6 @@ export const useSettingsStore = defineStore("setting", () => {
|
|||
themeColor,
|
||||
changeSetting,
|
||||
theme,
|
||||
watermark,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"];
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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[]>();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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: "" }); // 当前选中的字典类型
|
||||
|
||||
|
|
|
|||
|
|
@ -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[]>([]);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.vue",
|
||||
"src/types/**/*.d.ts",
|
||||
"types/**/*.d.ts",
|
||||
"mock/**/*.ts",
|
||||
"vite.config.ts"
|
||||
],
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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"];
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
@ -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 {};
|
||||
|
|
@ -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[];
|
||||
}
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue