Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
#app,
|
||||
body,
|
||||
html {
|
||||
@apply size-full overscroll-none;
|
||||
@apply size-full;
|
||||
|
||||
/* scrollbar-gutter: stable; */
|
||||
}
|
||||
|
@@ -3,19 +3,5 @@ import { defineBuildConfig } from 'unbuild';
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: [
|
||||
{
|
||||
builder: 'mkdist',
|
||||
input: './src',
|
||||
loaders: ['vue'],
|
||||
pattern: ['**/*.vue'],
|
||||
},
|
||||
{
|
||||
builder: 'mkdist',
|
||||
format: 'esm',
|
||||
input: './src',
|
||||
loaders: ['js'],
|
||||
pattern: ['**/*.ts'],
|
||||
},
|
||||
],
|
||||
entries: ['src/index'],
|
||||
});
|
||||
|
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g fill="none" fill-rule="evenodd" transform="translate(0 1)">
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="hsl(var(--background-deep))"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g fill-rule="nonzero" stroke="hsl(var(--heavy))">
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
fill="hsl(var(--accent))"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
@@ -1,10 +1,14 @@
|
||||
export { default as EmptyIcon } from './components/empty.vue';
|
||||
export * from './create-icon';
|
||||
|
||||
export * from './lucide';
|
||||
|
||||
export type { IconifyIcon as IconifyIconStructure } from '@iconify/vue';
|
||||
export { addCollection, addIcon, Icon as IconifyIcon } from '@iconify/vue';
|
||||
export {
|
||||
addCollection,
|
||||
addIcon,
|
||||
Icon as IconifyIcon,
|
||||
listIcons,
|
||||
} from '@iconify/vue';
|
||||
|
||||
/**
|
||||
* 从@iconify/vue/dist/offline'导出的组件为离线ICON 不支持在线
|
||||
|
@@ -27,6 +27,7 @@ export {
|
||||
FoldHorizontal,
|
||||
Fullscreen,
|
||||
Github,
|
||||
Grip,
|
||||
Info,
|
||||
InspectionPanel,
|
||||
Languages,
|
||||
|
@@ -84,6 +84,7 @@
|
||||
"@types/lodash.get": "catalog:",
|
||||
"@vue/shared": "catalog:",
|
||||
"clsx": "catalog:",
|
||||
"dayjs": "^1.11.13",
|
||||
"defu": "catalog:",
|
||||
"lodash.clonedeep": "catalog:",
|
||||
"lodash.get": "catalog:",
|
||||
|
18
packages/@core/base/shared/src/utils/date.ts
Normal file
18
packages/@core/base/shared/src/utils/date.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export function formatDate(time: number | string, format = 'YYYY-MM-DD') {
|
||||
try {
|
||||
const date = dayjs(time);
|
||||
if (!date.isValid()) {
|
||||
throw new Error('Invalid date');
|
||||
}
|
||||
return date.format(format);
|
||||
} catch (error) {
|
||||
console.error(`Error formatting date: ${error}`);
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatDateTime(time: number | string) {
|
||||
return formatDate(time, 'YYYY-MM-DD HH:mm:ss');
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
export * from './cn';
|
||||
export * from './date';
|
||||
export * from './diff';
|
||||
export * from './dom';
|
||||
export * from './inference';
|
||||
|
4
packages/@core/base/typings/src/basic.d.ts
vendored
4
packages/@core/base/typings/src/basic.d.ts
vendored
@@ -34,4 +34,6 @@ interface BasicUserInfo {
|
||||
username: string;
|
||||
}
|
||||
|
||||
export type { BasicOption, BasicUserInfo, SelectOption, TabOption };
|
||||
type ClassType = Array<object | string> | object | string;
|
||||
|
||||
export type { BasicOption, BasicUserInfo, ClassType, SelectOption, TabOption };
|
||||
|
@@ -40,6 +40,7 @@
|
||||
"@vben-core/composables": "workspace:*",
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@vben-core/shared": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vee-validate/zod": "catalog:",
|
||||
"@vueuse/core": "catalog:",
|
||||
"vee-validate": "catalog:",
|
||||
|
@@ -3,7 +3,12 @@ import { computed, toRaw, unref, watch } from 'vue';
|
||||
|
||||
import { useSimpleLocale } from '@vben-core/composables';
|
||||
import { VbenExpandableArrow } from '@vben-core/shadcn-ui';
|
||||
import { cn, isFunction, triggerWindowResize } from '@vben-core/shared/utils';
|
||||
import {
|
||||
cn,
|
||||
formatDate,
|
||||
isFunction,
|
||||
triggerWindowResize,
|
||||
} from '@vben-core/shared/utils';
|
||||
|
||||
import { COMPONENT_MAP } from '../config';
|
||||
import { injectFormProps } from '../use-form-context';
|
||||
@@ -52,20 +57,64 @@ async function handleSubmit(e: Event) {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
await unref(rootProps).handleSubmit?.(toRaw(form.values));
|
||||
|
||||
const values = handleRangeTimeValue(toRaw(form.values));
|
||||
await unref(rootProps).handleSubmit?.(values);
|
||||
}
|
||||
|
||||
async function handleReset(e: Event) {
|
||||
e?.preventDefault();
|
||||
e?.stopPropagation();
|
||||
const props = unref(rootProps);
|
||||
|
||||
const values = toRaw(form.values);
|
||||
// 清理时间字段
|
||||
props.fieldMappingTime &&
|
||||
props.fieldMappingTime.forEach(([_, [startTimeKey, endTimeKey]]) => {
|
||||
delete values[startTimeKey];
|
||||
delete values[endTimeKey];
|
||||
});
|
||||
|
||||
if (isFunction(props.handleReset)) {
|
||||
await props.handleReset?.(form.values);
|
||||
await props.handleReset?.(values);
|
||||
} else {
|
||||
form.resetForm();
|
||||
}
|
||||
}
|
||||
|
||||
function handleRangeTimeValue(values: Record<string, any>) {
|
||||
const fieldMappingTime = unref(rootProps).fieldMappingTime;
|
||||
|
||||
if (!fieldMappingTime || !Array.isArray(fieldMappingTime)) {
|
||||
return values;
|
||||
}
|
||||
|
||||
fieldMappingTime.forEach(
|
||||
([field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD']) => {
|
||||
if (!values[field]) {
|
||||
delete values[field];
|
||||
return;
|
||||
}
|
||||
|
||||
const [startTime, endTime] = values[field];
|
||||
const [startTimeFormat, endTimeFormat] = Array.isArray(format)
|
||||
? format
|
||||
: [format, format];
|
||||
|
||||
values[startTimeKey] = startTime
|
||||
? formatDate(startTime, startTimeFormat)
|
||||
: undefined;
|
||||
values[endTimeKey] = endTime
|
||||
? formatDate(endTime, endTimeFormat)
|
||||
: undefined;
|
||||
|
||||
delete values[field];
|
||||
},
|
||||
);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => collapsed.value,
|
||||
() => {
|
||||
|
@@ -303,4 +303,13 @@ export class FormApi {
|
||||
}
|
||||
return validateResult;
|
||||
}
|
||||
|
||||
async validateAndSubmitForm() {
|
||||
const form = await this.getForm();
|
||||
const { valid } = await form.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
return await this.submitForm();
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import type { VbenButtonProps } from '@vben-core/shadcn-ui';
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
import type { FieldOptions, FormContext, GenericObject } from 'vee-validate';
|
||||
import type { ZodTypeAny } from 'zod';
|
||||
|
||||
@@ -205,6 +206,12 @@ export type HandleResetFn = (
|
||||
values: Record<string, any>,
|
||||
) => Promise<void> | void;
|
||||
|
||||
export type FieldMappingTime = [
|
||||
string,
|
||||
[string, string],
|
||||
([string, string] | string)?,
|
||||
][];
|
||||
|
||||
export interface FormSchema<
|
||||
T extends BaseFormComponentType = BaseFormComponentType,
|
||||
> extends FormCommonConfig {
|
||||
@@ -303,7 +310,11 @@ export interface VbenFormProps<
|
||||
/**
|
||||
* 表单操作区域class
|
||||
*/
|
||||
actionWrapperClass?: any;
|
||||
actionWrapperClass?: ClassType;
|
||||
/**
|
||||
* 表单字段映射成时间格式
|
||||
*/
|
||||
fieldMappingTime?: FieldMappingTime;
|
||||
/**
|
||||
* 表单重置回调
|
||||
*/
|
||||
|
@@ -41,6 +41,7 @@
|
||||
"@vben-core/icons": "workspace:*",
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@vben-core/shared": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vueuse/core": "catalog:",
|
||||
"vue": "catalog:"
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
|
||||
import type { DrawerApi } from './drawer-api';
|
||||
|
||||
import type { Component, Ref } from 'vue';
|
||||
@@ -7,7 +9,7 @@ export interface DrawerProps {
|
||||
* 取消按钮文字
|
||||
*/
|
||||
cancelText?: string;
|
||||
class?: string;
|
||||
class?: ClassType;
|
||||
/**
|
||||
* 是否显示右上角的关闭按钮
|
||||
* @default true
|
||||
@@ -42,6 +44,20 @@ export interface DrawerProps {
|
||||
* @default true
|
||||
*/
|
||||
footer?: boolean;
|
||||
/**
|
||||
* 弹窗底部样式
|
||||
*/
|
||||
footerClass?: ClassType;
|
||||
/**
|
||||
* 是否显示顶栏
|
||||
* @default true
|
||||
*/
|
||||
header?: boolean;
|
||||
/**
|
||||
* 弹窗头部样式
|
||||
*/
|
||||
headerClass?: ClassType;
|
||||
|
||||
/**
|
||||
* 弹窗是否显示
|
||||
* @default false
|
||||
|
@@ -56,6 +56,9 @@ const {
|
||||
contentClass,
|
||||
description,
|
||||
footer: showFooter,
|
||||
footerClass,
|
||||
header: showHeader,
|
||||
headerClass,
|
||||
loading: showLoading,
|
||||
modal,
|
||||
openAutoFocus,
|
||||
@@ -129,10 +132,15 @@ function handleFocusOutside(e: Event) {
|
||||
@pointer-down-outside="pointerDownOutside"
|
||||
>
|
||||
<SheetHeader
|
||||
v-if="showHeader"
|
||||
:class="
|
||||
cn('!flex flex-row items-center justify-between border-b px-6 py-5', {
|
||||
'px-4 py-3': closable,
|
||||
})
|
||||
cn(
|
||||
'!flex flex-row items-center justify-between border-b px-6 py-5',
|
||||
headerClass,
|
||||
{
|
||||
'px-4 py-3': closable,
|
||||
},
|
||||
)
|
||||
"
|
||||
>
|
||||
<div>
|
||||
@@ -171,6 +179,12 @@ function handleFocusOutside(e: Event) {
|
||||
</div>
|
||||
</SheetHeader>
|
||||
|
||||
<template v-else>
|
||||
<VisuallyHidden>
|
||||
<SheetTitle />
|
||||
<SheetDescription />
|
||||
</VisuallyHidden>
|
||||
</template>
|
||||
<div
|
||||
ref="wrapperRef"
|
||||
:class="
|
||||
@@ -186,7 +200,12 @@ function handleFocusOutside(e: Event) {
|
||||
|
||||
<SheetFooter
|
||||
v-if="showFooter"
|
||||
class="w-full flex-row items-center justify-end border-t p-2 px-3"
|
||||
:class="
|
||||
cn(
|
||||
'w-full flex-row items-center justify-end border-t p-2 px-3',
|
||||
footerClass,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot name="prepend-footer"></slot>
|
||||
<slot name="footer">
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
import type {
|
||||
AvatarFallbackProps,
|
||||
AvatarImageProps,
|
||||
@@ -11,9 +12,9 @@ import { Avatar, AvatarFallback, AvatarImage } from '../../ui';
|
||||
|
||||
interface Props extends AvatarRootProps, AvatarFallbackProps, AvatarImageProps {
|
||||
alt?: string;
|
||||
class?: any;
|
||||
class?: ClassType;
|
||||
dot?: boolean;
|
||||
dotClass?: any;
|
||||
dotClass?: ClassType;
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
|
@@ -14,6 +14,7 @@ interface Props extends VbenButtonProps {
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
tooltip?: string;
|
||||
tooltipDelayDuration?: number;
|
||||
tooltipSide?: 'bottom' | 'left' | 'right' | 'top';
|
||||
variant?: ButtonVariants;
|
||||
}
|
||||
@@ -21,6 +22,7 @@ interface Props extends VbenButtonProps {
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
disabled: false,
|
||||
onClick: () => {},
|
||||
tooltipDelayDuration: 200,
|
||||
tooltipSide: 'bottom',
|
||||
variant: 'icon',
|
||||
});
|
||||
@@ -42,7 +44,11 @@ const showTooltip = computed(() => !!slots.tooltip || !!props.tooltip);
|
||||
<slot></slot>
|
||||
</VbenButton>
|
||||
|
||||
<VbenTooltip v-else :side="tooltipSide">
|
||||
<VbenTooltip
|
||||
v-else
|
||||
:delay-duration="tooltipDelayDuration"
|
||||
:side="tooltipSide"
|
||||
>
|
||||
<template #trigger>
|
||||
<VbenButton
|
||||
:class="cn('rounded-full', props.class)"
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
import type {
|
||||
ContextMenuContentProps,
|
||||
ContextMenuRootEmits,
|
||||
@@ -22,11 +23,11 @@ import {
|
||||
|
||||
const props = defineProps<
|
||||
{
|
||||
class?: any;
|
||||
contentClass?: any;
|
||||
class?: ClassType;
|
||||
contentClass?: ClassType;
|
||||
contentProps?: ContextMenuContentProps;
|
||||
handlerData?: Record<string, any>;
|
||||
itemClass?: any;
|
||||
itemClass?: ClassType;
|
||||
menus: (data: any) => IContextMenuItem[];
|
||||
} & ContextMenuRootProps
|
||||
>();
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
import type {
|
||||
HoverCardContentProps,
|
||||
HoverCardRootEmits,
|
||||
@@ -12,8 +13,8 @@ import { useForwardPropsEmits } from 'radix-vue';
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '../../ui';
|
||||
|
||||
interface Props extends HoverCardRootProps {
|
||||
class?: any;
|
||||
contentClass?: any;
|
||||
class?: ClassType;
|
||||
contentClass?: ClassType;
|
||||
contentProps?: HoverCardContentProps;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
import type {
|
||||
PopoverContentProps,
|
||||
PopoverRootEmits,
|
||||
@@ -16,8 +17,8 @@ import {
|
||||
} from '../../ui';
|
||||
|
||||
interface Props extends PopoverRootProps {
|
||||
class?: any;
|
||||
contentClass?: any;
|
||||
class?: ClassType;
|
||||
contentClass?: ClassType;
|
||||
contentProps?: PopoverContentProps;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { cn } from '@vben-core/shared/utils';
|
||||
@@ -6,9 +8,9 @@ import { cn } from '@vben-core/shared/utils';
|
||||
import { ScrollArea, ScrollBar } from '../../ui';
|
||||
|
||||
interface Props {
|
||||
class?: any;
|
||||
class?: ClassType;
|
||||
horizontal?: boolean;
|
||||
scrollBarClass?: any;
|
||||
scrollBarClass?: ClassType;
|
||||
shadow?: boolean;
|
||||
shadowBorder?: boolean;
|
||||
shadowBottom?: boolean;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
import type { TooltipContentProps } from 'radix-vue';
|
||||
|
||||
import type { StyleValue } from 'vue';
|
||||
@@ -11,7 +12,7 @@ import {
|
||||
} from '../../ui';
|
||||
|
||||
interface Props {
|
||||
contentClass?: any;
|
||||
contentClass?: ClassType;
|
||||
contentStyle?: StyleValue;
|
||||
delayDuration?: number;
|
||||
side?: TooltipContentProps['side'];
|
||||
|
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { cn } from '@vben-core/shared/utils';
|
||||
@@ -18,8 +20,8 @@ import DialogOverlay from './DialogOverlay.vue';
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
{
|
||||
class?: any;
|
||||
closeClass?: any;
|
||||
class?: ClassType;
|
||||
closeClass?: ClassType;
|
||||
modal?: boolean;
|
||||
open?: boolean;
|
||||
showClose?: boolean;
|
||||
|
Reference in New Issue
Block a user