diff --git a/docs/src/commercial/community.md b/docs/src/commercial/community.md
index 35fc4054..b139f9f4 100644
--- a/docs/src/commercial/community.md
+++ b/docs/src/commercial/community.md
@@ -14,8 +14,8 @@
:::
-作者主要通过微信群提供帮助,如果你有问题,可以通过以下方式加入微信群:
+作者主要通过微信群提供帮助,如果你有问题,可以通过以下方式加入微信群。
通过微信联系作者,注明加群来意:
-
+
diff --git a/internal/vite-config/src/options.ts b/internal/vite-config/src/options.ts
index b3d44624..d35441b5 100644
--- a/internal/vite-config/src/options.ts
+++ b/internal/vite-config/src/options.ts
@@ -25,6 +25,9 @@ const getDefaultPwaOptions = (name: string): Partial => ({
},
});
+/**
+ * importmap CDN 暂时不开启,因为有些包不支持,且网络不稳定
+ */
const defaultImportmapOptions: ImportmapPluginOptions = {
// 通过 Importmap CDN 方式引入,
// 目前只有esm.sh源兼容性好一点,jspm.io对于 esm 入口要求高
diff --git a/internal/vite-config/src/plugins/importmap.ts b/internal/vite-config/src/plugins/importmap.ts
index 600993d9..c6154c9e 100644
--- a/internal/vite-config/src/plugins/importmap.ts
+++ b/internal/vite-config/src/plugins/importmap.ts
@@ -80,7 +80,7 @@ async function viteImportMapPlugin(
const firstLayerKeys = Object.keys(scopes);
const inputMapScopes: string[] = [];
firstLayerKeys.forEach((key) => {
- inputMapScopes.push(...Object.keys(scopes[key]));
+ inputMapScopes.push(...Object.keys(scopes[key] || {}));
});
const inputMapImports = Object.keys(imports);
@@ -160,7 +160,10 @@ async function viteImportMapPlugin(
options.defaultProvider || DEFAULT_PROVIDER,
);
- const resultHtml = await injectShimsToHtml(html, esModuleShimsSrc);
+ const resultHtml = await injectShimsToHtml(
+ html,
+ esModuleShimsSrc || '',
+ );
html = await minify(resultHtml || html, {
collapseWhitespace: true,
minifyCSS: true,
diff --git a/internal/vite-config/src/plugins/inject-metadata.ts b/internal/vite-config/src/plugins/inject-metadata.ts
index 80d1db5a..2731412f 100644
--- a/internal/vite-config/src/plugins/inject-metadata.ts
+++ b/internal/vite-config/src/plugins/inject-metadata.ts
@@ -16,8 +16,8 @@ function resolvePackageVersion(
async function resolveMonorepoDependencies() {
const { packages } = await getPackages();
- const resultDevDependencies: Record = {};
- const resultDependencies: Record = {};
+ const resultDevDependencies: Record = {};
+ const resultDependencies: Record = {};
const pkgsMeta: Record = {};
for (const { packageJson } of packages) {
diff --git a/internal/vite-config/src/utils/env.ts b/internal/vite-config/src/utils/env.ts
index ea1cbf67..f9cdd4f5 100644
--- a/internal/vite-config/src/utils/env.ts
+++ b/internal/vite-config/src/utils/env.ts
@@ -6,6 +6,14 @@ import { fs } from '@vben/node-utils';
import dotenv from 'dotenv';
+const getBoolean = (value: string | undefined) => value === 'true';
+
+const getString = (value: string | undefined, fallback: string) =>
+ value ?? fallback;
+
+const getNumber = (value: string | undefined, fallback: number) =>
+ Number(value) || fallback;
+
/**
* 获取当前环境下生效的配置文件名
*/
@@ -63,6 +71,7 @@ async function loadAndConvertEnv(
} & Partial
> {
const envConfig = await loadEnv(match, confFiles);
+
const {
VITE_APP_TITLE,
VITE_BASE,
@@ -74,22 +83,22 @@ async function loadAndConvertEnv(
VITE_PWA,
VITE_VISUALIZER,
} = envConfig;
- const compress = VITE_COMPRESS || '';
- const compressTypes = compress
+
+ const compressTypes = (VITE_COMPRESS ?? '')
.split(',')
.filter((item) => item === 'brotli' || item === 'gzip');
return {
- appTitle: VITE_APP_TITLE ?? 'Vben Admin',
- base: VITE_BASE || '/',
- compress: !!compress,
- compressTypes: compressTypes as ('brotli' | 'gzip')[],
- devtools: VITE_DEVTOOLS === 'true',
- injectAppLoading: VITE_INJECT_APP_LOADING === 'true',
- nitroMock: VITE_NITRO_MOCK === 'true',
- port: Number(VITE_PORT) || 5173,
- pwa: VITE_PWA === 'true',
- visualizer: VITE_VISUALIZER === 'true',
+ appTitle: getString(VITE_APP_TITLE, 'Vben Admin'),
+ base: getString(VITE_BASE, '/'),
+ compress: compressTypes.length > 0,
+ compressTypes,
+ devtools: getBoolean(VITE_DEVTOOLS),
+ injectAppLoading: getBoolean(VITE_INJECT_APP_LOADING),
+ nitroMock: getBoolean(VITE_NITRO_MOCK),
+ port: getNumber(VITE_PORT, 5173),
+ pwa: getBoolean(VITE_PWA),
+ visualizer: getBoolean(VITE_VISUALIZER),
};
}
diff --git a/packages/@core/base/design/src/design-tokens/dark/index.css b/packages/@core/base/design/src/design-tokens/dark/index.css
index 1dea6978..45e255f7 100644
--- a/packages/@core/base/design/src/design-tokens/dark/index.css
+++ b/packages/@core/base/design/src/design-tokens/dark/index.css
@@ -19,8 +19,13 @@
--popover-foreground: 210 40% 98%;
/* Muted backgrounds such as , and */
- --muted: 220deg 6.82% 17.25%;
- --muted-foreground: 215 20.2% 65.1%;
+
+ /* --muted: 220deg 6.82% 17.25%; */
+
+ /* --muted-foreground: 215 20.2% 65.1%; */
+
+ --muted: 240 3.7% 15.9%;
+ --muted-foreground: 240 5% 64.9%;
/* 主题颜色 */
diff --git a/packages/@core/base/design/src/design-tokens/default/index.css b/packages/@core/base/design/src/design-tokens/default/index.css
index bf334175..991ea890 100644
--- a/packages/@core/base/design/src/design-tokens/default/index.css
+++ b/packages/@core/base/design/src/design-tokens/default/index.css
@@ -19,8 +19,12 @@
--popover-foreground: 222.2 84% 4.9%;
/* Muted backgrounds such as , and */
- --muted: 210 40% 96.1%;
- --muted-foreground: 215.4 16.3% 46.9%;
+
+ /* --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%; */
+
+ --muted: 240 4.8% 95.9%;
+ --muted-foreground: 240 3.8% 46.1%;
/* 主题颜色 */
diff --git a/packages/@core/base/shared/src/utils/diff.test.ts b/packages/@core/base/shared/src/utils/diff.test.ts
index e2a8121d..c858a3bf 100644
--- a/packages/@core/base/shared/src/utils/diff.test.ts
+++ b/packages/@core/base/shared/src/utils/diff.test.ts
@@ -3,58 +3,51 @@ import { describe, expect, it } from 'vitest';
import { diff } from './diff';
describe('diff function', () => {
- it('should correctly find differences in flat objects', () => {
- const oldObj = { a: 1, b: 2, c: 3 };
- const newObj = { a: 1, b: 3, c: 3 };
- expect(diff(oldObj, newObj)).toEqual({ b: 3 });
+ it('should return an empty object when comparing identical objects', () => {
+ const obj1 = { a: 1, b: { c: 2 } };
+ const obj2 = { a: 1, b: { c: 2 } };
+ expect(diff(obj1, obj2)).toEqual(undefined);
});
- it('should correctly handle nested objects', () => {
- const oldObj = { a: { b: 1, c: 2 }, d: 3 };
- const newObj = { a: { b: 1, c: 3 }, d: 3 };
- expect(diff(oldObj, newObj)).toEqual({ a: { b: 1, c: 3 } });
+ it('should detect simple changes in primitive values', () => {
+ const obj1 = { a: 1, b: 2 };
+ const obj2 = { a: 1, b: 3 };
+ expect(diff(obj1, obj2)).toEqual({ b: 3 });
});
- it('should correctly handle arrays`', () => {
- const oldObj = { a: [1, 2, 3] };
- const newObj = { a: [1, 2, 4] };
- expect(diff(oldObj, newObj)).toEqual({ a: [1, 2, 4] });
+ it('should detect nested object changes', () => {
+ const obj1 = { a: 1, b: { c: 2, d: 4 } };
+ const obj2 = { a: 1, b: { c: 3, d: 4 } };
+ expect(diff(obj1, obj2)).toEqual({ b: { c: 3 } });
});
- it('should correctly handle nested arrays', () => {
- const oldObj = {
- a: [
- [1, 2],
- [3, 4],
- ],
- };
- const newObj = {
- a: [
- [1, 2],
- [3, 5],
- ],
- };
- expect(diff(oldObj, newObj)).toEqual({
- a: [
- [1, 2],
- [3, 5],
- ],
- });
+ it('should handle array changes', () => {
+ const obj1 = { a: [1, 2, 3], b: 2 };
+ const obj2 = { a: [1, 2, 4], b: 2 };
+ expect(diff(obj1, obj2)).toEqual({ a: [1, 2, 4] });
});
- it('should return null if objects are identical', () => {
- const oldObj = { a: 1, b: 2, c: 3 };
- const newObj = { a: 1, b: 2, c: 3 };
- expect(diff(oldObj, newObj)).toBeNull();
+ it('should handle added keys', () => {
+ const obj1 = { a: 1 };
+ const obj2 = { a: 1, b: 2 };
+ expect(diff(obj1, obj2)).toEqual({ b: 2 });
});
- it('should return differences between two objects excluding ignored fields', () => {
- const oldObj = { a: 1, b: 2, c: 3, d: 6 };
- const newObj = { a: 2, b: 2, c: 4, d: 5 };
- const ignoreFields: (keyof typeof newObj)[] = ['a', 'd'];
+ it('should handle removed keys', () => {
+ const obj1 = { a: 1, b: 2 };
+ const obj2 = { a: 1 };
+ expect(diff(obj1, obj2)).toEqual(undefined);
+ });
- const result = diff(oldObj, newObj, ignoreFields);
+ it('should handle boolean value changes', () => {
+ const obj1 = { a: true, b: false };
+ const obj2 = { a: true, b: true };
+ expect(diff(obj1, obj2)).toEqual({ b: true });
+ });
- expect(result).toEqual({ c: 4 });
+ it('should handle null and undefined values', () => {
+ const obj1 = { a: null, b: undefined };
+ const obj2: any = { a: 1, b: undefined };
+ expect(diff(obj1, obj2)).toEqual({ a: 1 });
});
});
diff --git a/packages/@core/base/shared/src/utils/diff.ts b/packages/@core/base/shared/src/utils/diff.ts
index feb1f7df..449214d7 100644
--- a/packages/@core/base/shared/src/utils/diff.ts
+++ b/packages/@core/base/shared/src/utils/diff.ts
@@ -1,4 +1,4 @@
-type Diff = T;
+// type Diff = T;
// 比较两个数组是否相等
@@ -19,40 +19,78 @@ function arraysEqual(a: T[], b: T[]): boolean {
}
// 深度对比两个值
-function deepEqual(oldVal: T, newVal: T): boolean {
- if (
- typeof oldVal === 'object' &&
- oldVal !== null &&
- typeof newVal === 'object' &&
- newVal !== null
- ) {
- return Array.isArray(oldVal) && Array.isArray(newVal)
- ? arraysEqual(oldVal, newVal)
- : diff(oldVal as any, newVal as any) === null;
- } else {
- return oldVal === newVal;
- }
-}
+// function deepEqual(oldVal: T, newVal: T): boolean {
+// if (
+// typeof oldVal === 'object' &&
+// oldVal !== null &&
+// typeof newVal === 'object' &&
+// newVal !== null
+// ) {
+// return Array.isArray(oldVal) && Array.isArray(newVal)
+// ? arraysEqual(oldVal, newVal)
+// : diff(oldVal as any, newVal as any) === null;
+// } else {
+// return oldVal === newVal;
+// }
+// }
-// 主要的 diff 函数
-function diff(
- oldObj: T,
- newObj: T,
- ignoreFields: (keyof T)[] = [],
-): { [K in keyof T]?: Diff } | null {
- const difference: { [K in keyof T]?: Diff } = {};
+// // diff 函数
+// function diff(
+// oldObj: T,
+// newObj: T,
+// ignoreFields: (keyof T)[] = [],
+// ): { [K in keyof T]?: Diff } | null {
+// const difference: { [K in keyof T]?: Diff } = {};
- for (const key in oldObj) {
- if (ignoreFields.includes(key)) continue;
- const oldValue = oldObj[key];
- const newValue = newObj[key];
+// for (const key in oldObj) {
+// if (ignoreFields.includes(key)) continue;
+// const oldValue = oldObj[key];
+// const newValue = newObj[key];
- if (!deepEqual(oldValue, newValue)) {
- difference[key] = newValue;
+// if (!deepEqual(oldValue, newValue)) {
+// difference[key] = newValue;
+// }
+// }
+
+// return Object.keys(difference).length === 0 ? null : difference;
+// }
+
+type DiffResult = Partial<{
+ [K in keyof T]: T[K] extends object ? DiffResult : T[K];
+}>;
+
+function diff>(obj1: T, obj2: T): DiffResult {
+ function findDifferences(o1: any, o2: any): any {
+ if (Array.isArray(o1) && Array.isArray(o2)) {
+ if (!arraysEqual(o1, o2)) {
+ return o2;
+ }
+ return undefined;
}
+
+ if (
+ typeof o1 === 'object' &&
+ typeof o2 === 'object' &&
+ o1 !== null &&
+ o2 !== null
+ ) {
+ const diffResult: any = {};
+
+ const keys = new Set([...Object.keys(o1), ...Object.keys(o2)]);
+ keys.forEach((key) => {
+ const valueDiff = findDifferences(o1[key], o2[key]);
+ if (valueDiff !== undefined) {
+ diffResult[key] = valueDiff;
+ }
+ });
+
+ return Object.keys(diffResult).length > 0 ? diffResult : undefined;
+ }
+
+ return o1 === o2 ? undefined : o2;
}
- return Object.keys(difference).length === 0 ? null : difference;
+ return findDifferences(obj1, obj2);
}
export { arraysEqual, diff };
diff --git a/packages/effects/common-ui/src/components/page/__tests__/page.test.ts b/packages/effects/common-ui/src/components/page/__tests__/page.test.ts
index 0efb72d2..4169514a 100644
--- a/packages/effects/common-ui/src/components/page/__tests__/page.test.ts
+++ b/packages/effects/common-ui/src/components/page/__tests__/page.test.ts
@@ -58,6 +58,20 @@ describe('page.vue', () => {
expect(contentDiv.classes()).toContain('custom-class');
});
+ it('does not render title slot if title prop is provided', () => {
+ const wrapper = mount(Page, {
+ props: {
+ title: 'Test Title',
+ },
+ slots: {
+ title: 'Title Slot Content
',
+ },
+ });
+
+ expect(wrapper.text()).toContain('Title Slot Content');
+ expect(wrapper.html()).not.toContain('Test Title');
+ });
+
it('does not render description slot if description prop is provided', () => {
const wrapper = mount(Page, {
props: {
@@ -68,7 +82,7 @@ describe('page.vue', () => {
},
});
- expect(wrapper.text()).toContain('Test Description');
- expect(wrapper.html()).not.toContain('Description Slot Content');
+ expect(wrapper.text()).toContain('Description Slot Content');
+ expect(wrapper.html()).not.toContain('Test Description');
});
});
diff --git a/packages/effects/common-ui/src/components/page/page.vue b/packages/effects/common-ui/src/components/page/page.vue
index cdbefa23..d8564543 100644
--- a/packages/effects/common-ui/src/components/page/page.vue
+++ b/packages/effects/common-ui/src/components/page/page.vue
@@ -24,11 +24,20 @@ const props = withDefaults(defineProps(), {
v-if="description || $slots.description || title"
class="bg-card px-6 py-4"
>
-
- {{ title }}
-
- {{ description }}
-
+
+
+ {{ title }}
+
+
+
+
+
+ {{ description }}
+
+