feat: use simpler nitro instead of nestjs to implement mock service

This commit is contained in:
vben
2024-07-20 08:31:05 +08:00
parent 9ec91ac16d
commit 9987451647
122 changed files with 2556 additions and 2967 deletions

View File

@@ -4,7 +4,7 @@ import type { Preferences } from './types';
import { markRaw, reactive, readonly, watch } from 'vue';
import { StorageManager, merge } from '@vben-core/toolkit';
import { StorageManager, isMacOs, merge } from '@vben-core/toolkit';
import {
breakpointsTailwind,
@@ -46,7 +46,7 @@ class PreferenceManager {
this.savePreferences = useDebounceFn(
(preference: Preferences) => this._savePreferences(preference),
200,
100,
);
}
@@ -81,6 +81,11 @@ class PreferenceManager {
}
}
private initPlatform() {
const dom = document.documentElement;
dom.dataset.platform = isMacOs() ? 'macOs' : 'window';
}
/**
* 从缓存中加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。
*/
@@ -187,6 +192,8 @@ class PreferenceManager {
this.updatePreferences(mergedPreference);
this.setupWatcher();
this.initPlatform();
// 标记为已初始化
this.isInitialized = true;
}

View File

@@ -3,7 +3,7 @@ import type { RouteRecordNormalized, Router } from 'vue-router';
import { toRaw } from 'vue';
import { startProgress, stopProgress } from '@vben-core/toolkit';
import { openWindow, startProgress, stopProgress } from '@vben-core/toolkit';
import { acceptHMRUpdate, defineStore } from 'pinia';
@@ -226,6 +226,18 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
await this.closeTab(this.tabs[index], router);
},
/**
* @zh_CN 新窗口打开标签页
* @param tab
*/
async openTabInNewWindow(tab: TabDefinition) {
const { hash, origin } = location;
const path = tab.fullPath;
const fullPath = path.startsWith('/') ? path : `/${path}`;
const url = `${origin}${hash ? '/#' : ''}${fullPath}`;
openWindow(url, { target: '_blank' });
},
/**
* @zh_CN 固定标签页
* @param tab
@@ -257,6 +269,23 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
this.renderRouteView = true;
stopProgress();
},
/**
* @zh_CN 重置标签页标题
*/
async resetTabTitle(tab: TabDefinition) {
if (!tab?.meta?.newTabTitle) {
return;
}
const findTab = this.tabs.find(
(item) => getTabPath(item) === getTabPath(tab),
);
if (findTab) {
findTab.meta.newTabTitle = undefined;
await this.updateCacheTab();
}
},
/**
* 设置固定标签页
* @param tabs
@@ -267,6 +296,21 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
this.addTab(routeToTab(tab));
}
},
/**
* @zh_CN 设置标签页标题
* @param tab
* @param title
*/
async setTabTitle(tab: TabDefinition, title: string) {
const findTab = this.tabs.find(
(item) => getTabPath(item) === getTabPath(tab),
);
if (findTab) {
findTab.meta.newTabTitle = title;
await this.updateCacheTab();
}
},
/**
* @zh_CN 设置标签页顺序
* @param oldIndex
@@ -278,6 +322,15 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
this.tabs.splice(newIndex, 0, currentTab);
this.dragEndIndex = this.dragEndIndex + 1;
},
/**
* @zh_CN 切换固定标签页
* @param tab
*/
async toggleTabPin(tab: TabDefinition) {
const affixTab = tab?.meta?.affixTab ?? false;
await (affixTab ? this.unpinTab(tab) : this.pinTab(tab));
},
/**
* @zh_CN 取消固定标签页
* @param tab

View File

@@ -37,11 +37,14 @@
},
"dependencies": {
"@vben-core/constants": "workspace:*",
"@vben-core/preferences": "workspace:*",
"@vben-core/stores": "workspace:*",
"@vben-core/toolkit": "workspace:*",
"@vueuse/core": "^10.11.0",
"radix-vue": "^1.9.1",
"sortablejs": "^1.15.2",
"vue": "^3.4.32"
"vue": "^3.4.32",
"vue-router": "^4.4.0"
},
"devDependencies": {
"@types/sortablejs": "^1.15.8"

View File

@@ -1,6 +1,9 @@
export * from './use-content-height';
export * from './use-content-maximize';
export * from './use-namespace';
export * from './use-refresh';
export * from './use-sortable';
export * from './use-tabs';
export {
useEmitAsProps,
useForwardExpose,

View File

@@ -0,0 +1,24 @@
import { updatePreferences, usePreferences } from '@vben-core/preferences';
/**
* 主体区域最大化
*/
export function useContentMaximize() {
const { contentIsMaximize } = usePreferences();
function toggleMaximize() {
const isMaximize = contentIsMaximize.value;
updatePreferences({
header: {
hidden: !isMaximize,
},
sidebar: {
hidden: !isMaximize,
},
});
}
return {
contentIsMaximize,
toggleMaximize,
};
}

View File

@@ -0,0 +1,16 @@
import { useRouter } from 'vue-router';
import { useCoreTabbarStore } from '@vben-core/stores';
export function useRefresh() {
const router = useRouter();
const coreTabbarStore = useCoreTabbarStore();
function refresh() {
coreTabbarStore.refresh(router);
}
return {
refresh,
};
}

View File

@@ -0,0 +1,111 @@
import { type RouteLocationNormalized, useRoute, useRouter } from 'vue-router';
import { useCoreTabbarStore } from '@vben-core/stores';
export function useTabs() {
const router = useRouter();
const route = useRoute();
const coreTabbarStore = useCoreTabbarStore();
async function closeLeftTabs(tab?: RouteLocationNormalized) {
await coreTabbarStore.closeLeftTabs(tab || route);
}
async function closeAllTabs() {
await coreTabbarStore.closeAllTabs(router);
}
async function closeRightTabs(tab?: RouteLocationNormalized) {
await coreTabbarStore.closeRightTabs(tab || route);
}
async function closeOtherTabs(tab?: RouteLocationNormalized) {
await coreTabbarStore.closeOtherTabs(tab || route);
}
async function closeCurrentTab(tab?: RouteLocationNormalized) {
await coreTabbarStore.closeTab(tab || route, router);
}
async function pinTab(tab?: RouteLocationNormalized) {
await coreTabbarStore.pinTab(tab || route);
}
async function unpinTab(tab?: RouteLocationNormalized) {
await coreTabbarStore.unpinTab(tab || route);
}
async function toggleTabPin(tab?: RouteLocationNormalized) {
await coreTabbarStore.toggleTabPin(tab || route);
}
async function refreshTab() {
await coreTabbarStore.refresh(router);
}
async function openTabInNewWindow(tab?: RouteLocationNormalized) {
coreTabbarStore.openTabInNewWindow(tab || route);
}
async function closeTabByKey(key: string) {
await coreTabbarStore.closeTabByKey(key, router);
}
async function setTabTitle(title: string) {
await coreTabbarStore.setTabTitle(route, title);
}
async function resetTabTitle() {
await coreTabbarStore.resetTabTitle(route);
}
/**
* 获取操作是否禁用
* @param tab
*/
function getTabDisableState(tab: RouteLocationNormalized = route) {
const tabs = coreTabbarStore.getTabs;
const affixTabs = coreTabbarStore.affixTabs;
const index = tabs.findIndex((item) => item.path === tab.path);
const disabled = tabs.length <= 1;
const { meta } = tab;
const affixTab = meta?.affixTab ?? false;
const isCurrentTab = route.path === tab.path;
// 当前处于最左侧或者减去固定标签页的数量等于0
const disabledCloseLeft =
index === 0 || index - affixTabs.length <= 0 || !isCurrentTab;
const disabledCloseRight = !isCurrentTab || index === tabs.length - 1;
const disabledCloseOther =
disabled || !isCurrentTab || tabs.length - affixTabs.length <= 1;
return {
disabledCloseAll: disabled,
disabledCloseCurrent: !!affixTab || disabled,
disabledCloseLeft,
disabledCloseOther,
disabledCloseRight,
disabledRefresh: !isCurrentTab,
};
}
return {
closeAllTabs,
closeCurrentTab,
closeLeftTabs,
closeOtherTabs,
closeRightTabs,
closeTabByKey,
getTabDisableState,
openTabInNewWindow,
pinTab,
refreshTab,
resetTabTitle,
setTabTitle,
toggleTabPin,
unpinTab,
};
}

View File

@@ -13,6 +13,7 @@
"workspace": "Workspace"
},
"vben": {
"title": "Project",
"about": "About",
"document": "Document"
}

View File

@@ -13,6 +13,7 @@
"workspace": "工作台"
},
"vben": {
"title": "项目",
"about": "关于",
"document": "文档"
}
@@ -123,7 +124,7 @@
"sendCode": "获取验证码",
"sendText": "{0}秒后重新获取",
"thirdPartyLogin": "其他登录方式",
"loginAgainTitle": "重新登录",
"loginAgainTitle": "重新登录",
"loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。",
"layout": {
"center": "居中",

View File

@@ -83,19 +83,20 @@
@apply m-0 appearance-none;
}
/* 考虑只在mac下打开 */
/* 只有非mac下才进行调整mac下使用默认滚动条 */
html:not([data-platform='macOs']) {
*::-webkit-scrollbar {
@apply h-[1px] w-[10px];
}
/* *::-webkit-scrollbar {
@apply h-[1px] w-[10px];
*::-webkit-scrollbar-thumb {
@apply bg-border rounded-sm border-none;
}
*::-webkit-scrollbar-track {
@apply rounded-sm border-none bg-transparent shadow-none;
}
}
*::-webkit-scrollbar-thumb {
@apply bg-border rounded-sm border-none;
}
*::-webkit-scrollbar-track {
@apply rounded-sm border-none bg-transparent shadow-none;
} */
}
@layer components {

View File

@@ -54,7 +54,7 @@ function handleScroll(event: Event) {
v-if="shadow"
:class="{
'opacity-100': !isAtTop && !isAtBottom,
'border-border border-t': shadowBorder && !isAtTop && !isAtBottom,
'border-border border-b': shadowBorder && !isAtTop && !isAtBottom,
}"
class="scrollbar-bottom-shadow pointer-events-none absolute bottom-0 z-10 h-12 w-full opacity-0 transition-opacity duration-300 ease-in-out will-change-[opacity]"
></div>

View File

@@ -71,7 +71,7 @@ function scrollIntoView() {
<template>
<div :style="style" class="tabs-chrome size-full flex-1 overflow-hidden pt-1">
<VbenScrollbar class="h-full" horizontal>
<VbenScrollbar class="tabs-chrome__scrollbar h-full" horizontal>
<!-- footer -> 4px -->
<div
ref="contentRef"
@@ -237,6 +237,7 @@ function scrollIntoView() {
}
}
&__scrollbar,
&__label {
mask-image: linear-gradient(
90deg,

View File

@@ -72,7 +72,7 @@ function scrollIntoView() {
<template>
<div class="h-full flex-1 overflow-hidden">
<VbenScrollbar class="h-full" horizontal>
<VbenScrollbar class="tabs-scrollbar h-full" horizontal>
<div
:class="contentClass"
class="relative !flex h-full w-max items-center"
@@ -147,3 +147,14 @@ function scrollIntoView() {
</VbenScrollbar>
</div>
</template>
<style scoped>
.tabs-scrollbar {
mask-image: linear-gradient(
90deg,
#000 0%,
#000 calc(100% - 16px),
transparent
);
}
</style>