Skip to content

Commit 83b9a1c

Browse files
committed
chore: merge latest codes
2 parents 7665ad2 + 86da3ce commit 83b9a1c

File tree

23 files changed

+1714
-1320
lines changed

23 files changed

+1714
-1320
lines changed

.vscode/settings.json

+12-1
Original file line numberDiff line numberDiff line change
@@ -223,5 +223,16 @@
223223
"commentTranslate.multiLineMerge": true,
224224
"vue.server.hybridMode": true,
225225
"typescript.tsdk": "node_modules/typescript/lib",
226-
"oxc.enable": false
226+
"oxc.enable": false,
227+
"cSpell.words": [
228+
"archiver",
229+
"axios",
230+
"dotenv",
231+
"isequal",
232+
"jspm",
233+
"napi",
234+
"nolebase",
235+
"rollup",
236+
"vitest"
237+
]
227238
}

apps/simple-admin-core/src/adapter/component/index.ts

+53-24
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,60 @@ import {
2121
} from '#/components/form';
2222
import { globalShareState, IconPicker } from '@vben/common-ui';
2323
import { $t } from '@vben/locales';
24+
import { notification } from 'ant-design-vue';
2425
import {
25-
AutoComplete,
26-
Button,
27-
Checkbox,
28-
CheckboxGroup,
29-
DatePicker,
30-
Divider,
31-
Input,
32-
InputNumber,
33-
InputPassword,
34-
Mentions,
35-
notification,
36-
Radio,
37-
RadioGroup,
38-
RangePicker,
39-
Rate,
40-
Select,
41-
Space,
42-
Switch,
43-
Textarea,
44-
TimePicker,
45-
TreeSelect,
46-
Upload,
47-
} from 'ant-design-vue';
48-
import { defineComponent, getCurrentInstance, h, ref } from 'vue';
26+
defineAsyncComponent,
27+
defineComponent,
28+
getCurrentInstance,
29+
h,
30+
ref,
31+
} from 'vue';
32+
33+
const AutoComplete = defineAsyncComponent(
34+
() => import('ant-design-vue/es/auto-complete'),
35+
);
36+
const Button = defineAsyncComponent(() => import('ant-design-vue/es/button'));
37+
const Checkbox = defineAsyncComponent(
38+
() => import('ant-design-vue/es/checkbox'),
39+
);
40+
const CheckboxGroup = defineAsyncComponent(() =>
41+
import('ant-design-vue/es/checkbox').then((res) => res.CheckboxGroup),
42+
);
43+
const DatePicker = defineAsyncComponent(
44+
() => import('ant-design-vue/es/date-picker'),
45+
);
46+
const Divider = defineAsyncComponent(() => import('ant-design-vue/es/divider'));
47+
const Input = defineAsyncComponent(() => import('ant-design-vue/es/input'));
48+
const InputNumber = defineAsyncComponent(
49+
() => import('ant-design-vue/es/input-number'),
50+
);
51+
const InputPassword = defineAsyncComponent(() =>
52+
import('ant-design-vue/es/input').then((res) => res.InputPassword),
53+
);
54+
const Mentions = defineAsyncComponent(
55+
() => import('ant-design-vue/es/mentions'),
56+
);
57+
const Radio = defineAsyncComponent(() => import('ant-design-vue/es/radio'));
58+
const RadioGroup = defineAsyncComponent(() =>
59+
import('ant-design-vue/es/radio').then((res) => res.RadioGroup),
60+
);
61+
const RangePicker = defineAsyncComponent(() =>
62+
import('ant-design-vue/es/date-picker').then((res) => res.RangePicker),
63+
);
64+
const Rate = defineAsyncComponent(() => import('ant-design-vue/es/rate'));
65+
const Select = defineAsyncComponent(() => import('ant-design-vue/es/select'));
66+
const Space = defineAsyncComponent(() => import('ant-design-vue/es/space'));
67+
const Switch = defineAsyncComponent(() => import('ant-design-vue/es/switch'));
68+
const Textarea = defineAsyncComponent(() =>
69+
import('ant-design-vue/es/input').then((res) => res.Textarea),
70+
);
71+
const TimePicker = defineAsyncComponent(
72+
() => import('ant-design-vue/es/time-picker'),
73+
);
74+
const TreeSelect = defineAsyncComponent(
75+
() => import('ant-design-vue/es/tree-select'),
76+
);
77+
const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload'));
4978

5079
const withDefaultPlaceholder = <T extends Component>(
5180
component: T,

apps/simple-admin-core/src/bootstrap.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { $t, setupI18n } from '#/locales';
22
import { registerAccessDirective } from '@vben/access';
3-
import { initTippy, registerLoadingDirective } from '@vben/common-ui';
4-
import { MotionPlugin } from '@vben/plugins/motion';
3+
import { registerLoadingDirective } from '@vben/common-ui/es/loading';
54
import { preferences } from '@vben/preferences';
65
import { initStores } from '@vben/stores';
76
import { useTitle } from '@vueuse/core';
@@ -45,12 +44,14 @@ async function bootstrap(namespace: string) {
4544
registerAccessDirective(app);
4645

4746
// 初始化 tippy
47+
const { initTippy } = await import('@vben/common-ui/es/tippy');
4848
initTippy(app);
4949

5050
// 配置路由及路由守卫
5151
app.use(router);
5252

5353
// 配置Motion插件
54+
const { MotionPlugin } = await import('@vben/plugins/motion');
5455
app.use(MotionPlugin);
5556

5657
// 动态更新标题

apps/simple-admin-core/src/router/routes/core.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import type { RouteRecordRaw } from 'vue-router';
22

3-
import { AuthPageLayout, BasicLayout } from '#/layouts';
43
import { $t } from '#/locales';
5-
import Login from '#/views/_core/authentication/login.vue';
6-
import InitializationPage from '#/views/sys/initialize/index.vue';
74
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
85

6+
const BasicLayout = () => import('#/layouts/basic.vue');
7+
const AuthPageLayout = () => import('#/layouts/auth.vue');
98
/** 全局404页面 */
109
const fallbackNotFoundRoute: RouteRecordRaw = {
1110
component: () => import('#/views/_core/fallback/not-found.vue'),
@@ -43,7 +42,7 @@ const coreRoutes: RouteRecordRaw[] = [
4342
},
4443
name: 'Initialization Page',
4544
path: '/init',
46-
component: InitializationPage,
45+
component: () => import('#/views/sys/initialize/index.vue'),
4746
},
4847
{
4948
meta: {
@@ -66,7 +65,7 @@ const coreRoutes: RouteRecordRaw[] = [
6665
{
6766
name: 'Login',
6867
path: 'login',
69-
component: Login,
68+
component: () => import('#/views/_core/authentication/login.vue'),
7069
meta: {
7170
title: $t('page.auth.login'),
7271
},

packages/@core/ui-kit/menu-ui/src/components/menu.vue

-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import type {
1111
1212
import { useNamespace } from '@vben-core/composables';
1313
import { Ellipsis } from '@vben-core/icons';
14-
import { isHttpUrl } from '@vben-core/shared/utils';
1514
import { useResizeObserver } from '@vueuse/core';
1615
import {
1716
computed,
@@ -245,9 +244,6 @@ function handleMenuItemClick(data: MenuItemClicked) {
245244
if (!path || !parentPaths) {
246245
return;
247246
}
248-
if (!isHttpUrl(path)) {
249-
activePath.value = path;
250-
}
251247
252248
emit('select', path, parentPaths);
253249
}

packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts

+78-37
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { Recordable } from '@vben-core/typings';
2-
import type { Component } from 'vue';
2+
import type { Component, VNode } from 'vue';
33

4-
import type { AlertProps, BeforeCloseScope } from './alert';
4+
import type { AlertProps, BeforeCloseScope, PromptProps } from './alert';
55

66
import { useSimpleLocale } from '@vben-core/composables';
77
import { Input } from '@vben-core/shadcn-ui';
88
import { isFunction, isString } from '@vben-core/shared/utils';
9-
import { h, ref, render } from 'vue';
9+
import { h, nextTick, ref, render } from 'vue';
1010

1111
import Alert from './alert.vue';
1212

@@ -128,40 +128,58 @@ export function vbenConfirm(
128128
}
129129

130130
export async function vbenPrompt<T = any>(
131-
options: {
132-
beforeClose?: (scope: {
133-
isConfirm: boolean;
134-
value: T | undefined;
135-
}) => boolean | Promise<boolean | undefined> | undefined;
136-
component?: Component;
137-
componentProps?: Recordable<any>;
138-
defaultValue?: T;
139-
modelPropName?: string;
140-
} & Omit<AlertProps, 'beforeClose'>,
131+
options: PromptProps<T>,
141132
): Promise<T | undefined> {
142133
const {
143134
component: _component,
144135
componentProps: _componentProps,
136+
componentSlots,
145137
content,
146138
defaultValue,
147139
modelPropName: _modelPropName,
148140
...delegated
149141
} = options;
150-
const contents: Component[] = [];
142+
151143
const modelValue = ref<T | undefined>(defaultValue);
144+
const inputComponentRef = ref<null | VNode>(null);
145+
const staticContents: Component[] = [];
146+
152147
if (isString(content)) {
153-
contents.push(h('span', content));
154-
} else {
155-
contents.push(content);
148+
staticContents.push(h('span', content));
149+
} else if (content) {
150+
staticContents.push(content as Component);
156151
}
157-
const componentProps = _componentProps || {};
152+
158153
const modelPropName = _modelPropName || 'modelValue';
159-
componentProps[modelPropName] = modelValue.value;
160-
componentProps[`onUpdate:${modelPropName}`] = (val: any) => {
161-
modelValue.value = val;
154+
const componentProps = { ..._componentProps };
155+
156+
// 每次渲染时都会重新计算的内容函数
157+
const contentRenderer = () => {
158+
const currentProps = { ...componentProps };
159+
160+
// 设置当前值
161+
currentProps[modelPropName] = modelValue.value;
162+
163+
// 设置更新处理函数
164+
currentProps[`onUpdate:${modelPropName}`] = (val: T) => {
165+
modelValue.value = val;
166+
};
167+
168+
// 创建输入组件
169+
inputComponentRef.value = h(
170+
_component || Input,
171+
currentProps,
172+
componentSlots,
173+
);
174+
175+
// 返回包含静态内容和输入组件的数组
176+
return h(
177+
'div',
178+
{ class: 'flex flex-col gap-2' },
179+
{ default: () => [...staticContents, inputComponentRef.value] },
180+
);
162181
};
163-
const componentRef = h(_component || Input, componentProps);
164-
contents.push(componentRef);
182+
165183
const props: AlertProps & Recordable<any> = {
166184
...delegated,
167185
async beforeClose(scope: BeforeCloseScope) {
@@ -172,23 +190,46 @@ export async function vbenPrompt<T = any>(
172190
});
173191
}
174192
},
175-
content: h(
176-
'div',
177-
{ class: 'flex flex-col gap-2' },
178-
{ default: () => contents },
179-
),
180-
onOpened() {
181-
// 组件挂载完成后,自动聚焦到输入组件
182-
if (
183-
componentRef.component?.exposed &&
184-
isFunction(componentRef.component.exposed.focus)
185-
) {
186-
componentRef.component.exposed.focus();
187-
} else if (componentRef.el && isFunction(componentRef.el.focus)) {
188-
componentRef.el.focus();
193+
// 使用函数形式,每次渲染都会重新计算内容
194+
content: contentRenderer,
195+
contentMasking: true,
196+
async onOpened() {
197+
await nextTick();
198+
const componentRef: null | VNode = inputComponentRef.value;
199+
if (componentRef) {
200+
if (
201+
componentRef.component?.exposed &&
202+
isFunction(componentRef.component.exposed.focus)
203+
) {
204+
componentRef.component.exposed.focus();
205+
} else {
206+
if (componentRef.el) {
207+
if (
208+
isFunction(componentRef.el.focus) &&
209+
['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'].includes(
210+
componentRef.el.tagName,
211+
)
212+
) {
213+
componentRef.el.focus();
214+
} else if (isFunction(componentRef.el.querySelector)) {
215+
const focusableElement = componentRef.el.querySelector(
216+
'input, select, textarea, button',
217+
);
218+
if (focusableElement && isFunction(focusableElement.focus)) {
219+
focusableElement.focus();
220+
}
221+
} else if (
222+
componentRef.el.nextElementSibling &&
223+
isFunction(componentRef.el.nextElementSibling.focus)
224+
) {
225+
componentRef.el.nextElementSibling.focus();
226+
}
227+
}
228+
}
189229
}
190230
},
191231
};
232+
192233
await vbenConfirm(props);
193234
return modelValue.value;
194235
}

packages/@core/ui-kit/popup-ui/src/alert/alert.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { Component } from 'vue';
1+
import type { Recordable } from '@vben-core/typings';
2+
import type { Component, VNode, VNodeArrayChildren } from 'vue';
23

34
export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning';
45

@@ -13,6 +14,11 @@ export type AlertProps = {
1314
) => boolean | Promise<boolean | undefined> | undefined;
1415
/** 边框 */
1516
bordered?: boolean;
17+
/**
18+
* 按钮对齐方式
19+
* @default 'end'
20+
*/
21+
buttonAlign?: 'center' | 'end' | 'start';
1622
/** 取消按钮的标题 */
1723
cancelText?: string;
1824
/** 是否居中显示 */
@@ -25,10 +31,35 @@ export type AlertProps = {
2531
content: Component | string;
2632
/** 弹窗内容的额外样式 */
2733
contentClass?: string;
34+
/** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/
35+
contentMasking?: boolean;
2836
/** 弹窗的图标(在标题的前面) */
2937
icon?: Component | IconType;
3038
/** 是否显示取消按钮 */
3139
showCancel?: boolean;
3240
/** 弹窗标题 */
3341
title?: string;
3442
};
43+
44+
/** Prompt属性 */
45+
export type PromptProps<T = any> = {
46+
/** 关闭前的回调,如果返回false,则终止关闭 */
47+
beforeClose?: (scope: {
48+
isConfirm: boolean;
49+
value: T | undefined;
50+
}) => boolean | Promise<boolean | undefined> | undefined;
51+
/** 用于接受用户输入的组件 */
52+
component?: Component;
53+
/** 输入组件的属性 */
54+
componentProps?: Recordable<any>;
55+
/** 输入组件的插槽 */
56+
componentSlots?:
57+
| (() => any)
58+
| Recordable<unknown>
59+
| VNode
60+
| VNodeArrayChildren;
61+
/** 默认值 */
62+
defaultValue?: T;
63+
/** 输入组件的值属性名 */
64+
modelPropName?: string;
65+
} & Omit<AlertProps, 'beforeClose'>;

0 commit comments

Comments
 (0)