chore: code format

This commit is contained in:
xiaojunnuo
2025-06-29 14:09:09 +08:00
parent 04422a4637
commit 4fcfd089d8
644 changed files with 10845 additions and 13184 deletions
@@ -25,12 +25,12 @@ interface Props {
}
defineOptions({
name: "AccessControl"
name: "AccessControl",
});
const props = withDefaults(defineProps<Props>(), {
codes: () => [],
type: "role"
type: "role",
});
const { hasAccessByCodes, hasAccessByRoles } = useAccess();
@@ -12,7 +12,7 @@ async function generateAccessible(mode: AccessModeType, options: GenerateMenuAnd
const root = router.getRoutes().find((item: any) => item.path === "/");
// 动态添加到router实例内
accessibleRoutes.forEach((route) => {
accessibleRoutes.forEach(route => {
if (root && !route.meta?.noBasicLayout) {
// 为了兼容之前的版本用法,如果包含子路由,则将component移除,以免出现多层BasicLayout
// 如果你的项目已经跟进了本次修改,移除了所有自定义菜单首级的BasicLayout,可以将这段if代码删除
@@ -62,7 +62,7 @@ async function generateRoutes(mode: AccessModeType, options: GenerateMenuAndRout
* 调整路由树,做以下处理:
* 1. 对未添加redirect的路由添加redirect
*/
resultRoutes = mapTree(resultRoutes, (route) => {
resultRoutes = mapTree(resultRoutes, route => {
// 如果有redirect或者没有子路由,则直接返回
if (route.redirect || !route.children || route.children.length === 0) {
return route;
@@ -4,23 +4,17 @@
* @Example v-access:role="[ROLE_NAME]" or v-access:role="ROLE_NAME"
* @Example v-access:code="[ROLE_CODE]" or v-access:code="ROLE_CODE"
*/
import type { App, Directive, DirectiveBinding } from 'vue';
import type { App, Directive, DirectiveBinding } from "vue";
import { useAccess } from './use-access';
import { useAccess } from "./use-access";
function isAccessible(
el: Element,
binding: DirectiveBinding<string | string[]>,
) {
function isAccessible(el: Element, binding: DirectiveBinding<string | string[]>) {
const { accessMode, hasAccessByCodes, hasAccessByRoles } = useAccess();
const value = binding.value;
if (!value) return;
const authMethod =
accessMode.value === 'frontend' && binding.arg === 'role'
? hasAccessByRoles
: hasAccessByCodes;
const authMethod = accessMode.value === "frontend" && binding.arg === "role" ? hasAccessByRoles : hasAccessByCodes;
const values = Array.isArray(value) ? value : [value];
@@ -38,5 +32,5 @@ const authDirective: Directive = {
};
export function registerAccessDirective(app: App) {
app.directive('access', authDirective);
app.directive("access", authDirective);
}
@@ -1,4 +1,4 @@
export { default as AccessControl } from './access-control.vue';
export * from './accessible';
export * from './directive';
export * from './use-access';
export { default as AccessControl } from "./access-control.vue";
export * from "./accessible";
export * from "./directive";
export * from "./use-access";
@@ -17,7 +17,7 @@ function useAccess() {
*/
function hasAccessByRoles(roles: string[]) {
const userRoleSet = new Set(userStore.userRoles);
const intersection = roles.filter((item) => userRoleSet.has(item));
const intersection = roles.filter(item => userRoleSet.has(item));
return intersection.length > 0;
}
@@ -29,15 +29,15 @@ function useAccess() {
function hasAccessByCodes(codes: string[]) {
const userCodesSet = new Set(accessStore.accessCodes);
const intersection = codes.filter((item) => userCodesSet.has(item));
const intersection = codes.filter(item => userCodesSet.has(item));
return intersection.length > 0;
}
async function toggleAccessMode() {
updatePreferences({
app: {
accessMode: preferences.app.accessMode === "frontend" ? "backend" : "frontend"
}
accessMode: preferences.app.accessMode === "frontend" ? "backend" : "frontend",
},
});
}
@@ -45,7 +45,7 @@ function useAccess() {
accessMode,
hasAccessByCodes,
hasAccessByRoles,
toggleAccessMode
toggleAccessMode,
};
}
@@ -74,7 +74,7 @@ const props = withDefaults(defineProps<Props>(), {
afterFetch: undefined,
modelPropName: "modelValue",
api: undefined,
options: () => []
options: () => [],
});
const emit = defineEmits<{
@@ -96,13 +96,13 @@ const getOptions = computed(() => {
const refOptionsData = unref(refOptions);
function transformData(data: OptionsItem[]): OptionsItem[] {
return data.map((item) => {
return data.map(item => {
const value = get(item, valueField);
return {
...objectOmit(item, [labelField, valueField, childrenField]),
label: get(item, labelField),
value: numberToString ? `${value}` : value,
...(childrenField && item[childrenField] ? { children: transformData(item[childrenField]) } : {})
...(childrenField && item[childrenField] ? { children: transformData(item[childrenField]) } : {}),
};
});
}
@@ -122,9 +122,9 @@ const bindProps = computed(() => {
...objectOmit(attrs, [`onUpdate:${props.modelPropName}`]),
...(props.visibleEvent
? {
[props.visibleEvent]: handleFetchForVisible
[props.visibleEvent]: handleFetchForVisible,
}
: {})
: {}),
};
});
@@ -1 +1 @@
export { default as ApiComponent } from './api-component.vue';
export { default as ApiComponent } from "./api-component.vue";
@@ -1,6 +1,6 @@
import type { CaptchaPoint } from '../types';
import type { CaptchaPoint } from "../types";
import { reactive } from 'vue';
import { reactive } from "vue";
export function useCaptchaPoints() {
const points = reactive<CaptchaPoint[]>([]);
@@ -1,6 +1,6 @@
export { default as PointSelectionCaptcha } from './point-selection-captcha/index.vue';
export { default as PointSelectionCaptchaCard } from './point-selection-captcha/index.vue';
export { default as PointSelectionCaptcha } from "./point-selection-captcha/index.vue";
export { default as PointSelectionCaptchaCard } from "./point-selection-captcha/index.vue";
export { default as SliderCaptcha } from './slider-captcha/index.vue';
export { default as SliderRotateCaptcha } from './slider-rotate-captcha/index.vue';
export type * from './types';
export { default as SliderCaptcha } from "./slider-captcha/index.vue";
export { default as SliderRotateCaptcha } from "./slider-rotate-captcha/index.vue";
export type * from "./types";
@@ -1,23 +1,23 @@
<script setup lang="ts">
import type { CaptchaPoint, PointSelectionCaptchaProps } from '../types';
import type { CaptchaPoint, PointSelectionCaptchaProps } from "../types";
import { RotateCw } from '/@/vben/icons';
import { $t } from '/@/locales';
import { RotateCw } from "/@/vben/icons";
import { $t } from "/@/locales";
import { VbenButton, VbenIconButton } from '/@/vben/shadcn-ui';
import { VbenButton, VbenIconButton } from "/@/vben/shadcn-ui";
import { useCaptchaPoints } from '../hooks/useCaptchaPoints';
import CaptchaCard from './point-selection-captcha-card.vue';
import { useCaptchaPoints } from "../hooks/useCaptchaPoints";
import CaptchaCard from "./point-selection-captcha-card.vue";
const props = withDefaults(defineProps<PointSelectionCaptchaProps>(), {
height: '220px',
hintImage: '',
hintText: '',
paddingX: '12px',
paddingY: '16px',
height: "220px",
hintImage: "",
hintText: "",
paddingX: "12px",
paddingY: "16px",
showConfirm: false,
title: '',
width: '300px',
title: "",
width: "300px",
});
const emit = defineEmits<{
click: [CaptchaPoint];
@@ -27,7 +27,7 @@ const emit = defineEmits<{
const { addPoint, clearPoints, points } = useCaptchaPoints();
if (!props.hintImage && !props.hintText) {
console.warn('At least one of hint image or hint text must be provided');
console.warn("At least one of hint image or hint text must be provided");
}
const POINT_OFFSET = 11;
@@ -43,15 +43,15 @@ function getElementPosition(element: HTMLElement) {
function handleClick(e: MouseEvent) {
try {
const dom = e.currentTarget as HTMLElement;
if (!dom) throw new Error('Element not found');
if (!dom) throw new Error("Element not found");
const { x: domX, y: domY } = getElementPosition(dom);
const mouseX = e.clientX + window.scrollX;
const mouseY = e.clientY + window.scrollY;
if (typeof mouseX !== 'number' || typeof mouseY !== 'number') {
throw new TypeError('Mouse coordinates not found');
if (typeof mouseX !== "number" || typeof mouseY !== "number") {
throw new TypeError("Mouse coordinates not found");
}
const xPos = mouseX - domX;
@@ -61,7 +61,7 @@ function handleClick(e: MouseEvent) {
// 点击位置边界校验
if (xPos < 0 || yPos < 0 || xPos > rect.width || yPos > rect.height) {
console.warn('Click position is out of the valid range');
console.warn("Click position is out of the valid range");
return;
}
@@ -77,11 +77,11 @@ function handleClick(e: MouseEvent) {
addPoint(point);
emit('click', point);
emit("click", point);
e.stopPropagation();
e.preventDefault();
} catch (error) {
console.error('Error in handleClick:', error);
console.error("Error in handleClick:", error);
}
}
@@ -89,58 +89,40 @@ function clear() {
try {
clearPoints();
} catch (error) {
console.error('Error in clear:', error);
console.error("Error in clear:", error);
}
}
function handleRefresh() {
try {
clear();
emit('refresh');
emit("refresh");
} catch (error) {
console.error('Error in handleRefresh:', error);
console.error("Error in handleRefresh:", error);
}
}
function handleConfirm() {
if (!props.showConfirm) return;
try {
emit('confirm', points, clear);
emit("confirm", points, clear);
} catch (error) {
console.error('Error in handleConfirm:', error);
console.error("Error in handleConfirm:", error);
}
}
</script>
<template>
<CaptchaCard
:captcha-image="captchaImage"
:height="height"
:padding-x="paddingX"
:padding-y="paddingY"
:title="title"
:width="width"
@click="handleClick"
>
<CaptchaCard :captcha-image="captchaImage" :height="height" :padding-x="paddingX" :padding-y="paddingY" :title="title" :width="width" @click="handleClick">
<template #title>
<slot name="title">{{ $t('ui.captcha.title') }}</slot>
<slot name="title">{{ $t("ui.captcha.title") }}</slot>
</template>
<template #extra>
<VbenIconButton
:aria-label="$t('ui.captcha.refreshAriaLabel')"
class="ml-1"
@click="handleRefresh"
>
<VbenIconButton :aria-label="$t('ui.captcha.refreshAriaLabel')" class="ml-1" @click="handleRefresh">
<RotateCw class="size-5" />
</VbenIconButton>
<VbenButton
v-if="showConfirm"
:aria-label="$t('ui.captcha.confirmAriaLabel')"
class="ml-2"
size="sm"
@click="handleConfirm"
>
{{ $t('ui.captcha.confirm') }}
<VbenButton v-if="showConfirm" :aria-label="$t('ui.captcha.confirmAriaLabel')" class="ml-2" size="sm" @click="handleConfirm">
{{ $t("ui.captcha.confirm") }}
</VbenButton>
</template>
@@ -159,17 +141,9 @@ function handleConfirm() {
{{ index + 1 }}
</div>
<template #footer>
<img
v-if="hintImage"
:alt="$t('ui.captcha.alt')"
:src="hintImage"
class="border-border h-10 w-full rounded border"
/>
<div
v-else-if="hintText"
class="border-border flex-center h-10 w-full rounded border"
>
{{ `${$t('ui.captcha.clickInOrder')}` + `${hintText}` }}
<img v-if="hintImage" :alt="$t('ui.captcha.alt')" :src="hintImage" class="border-border h-10 w-full rounded border" />
<div v-else-if="hintText" class="border-border flex-center h-10 w-full rounded border">
{{ `${$t("ui.captcha.clickInOrder")}` + `${hintText}` }}
</div>
</template>
</CaptchaCard>
@@ -1,24 +1,18 @@
<script setup lang="ts">
import type { PointSelectionCaptchaCardProps } from '../types';
import type { PointSelectionCaptchaCardProps } from "../types";
import { computed } from 'vue';
import { computed } from "vue";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from '/@/vben/shadcn-ui';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "/@/vben/shadcn-ui";
const props = withDefaults(defineProps<PointSelectionCaptchaCardProps>(), {
height: '220px',
paddingX: '12px',
paddingY: '16px',
title: '',
width: '300px',
height: "220px",
paddingX: "12px",
paddingY: "16px",
title: "",
width: "300px",
});
const emit = defineEmits<{
@@ -26,7 +20,7 @@ const emit = defineEmits<{
}>();
const parseValue = (value: number | string) => {
if (typeof value === 'number') {
if (typeof value === "number") {
return value;
}
const parsed = Number.parseFloat(value);
@@ -46,7 +40,7 @@ const captchaStyles = computed(() => {
});
function handleClick(e: MouseEvent) {
emit('click', e);
emit("click", e);
}
</script>
<template>
@@ -54,7 +48,7 @@ function handleClick(e: MouseEvent) {
<CardHeader class="p-0">
<CardTitle id="captcha-title" class="flex items-center justify-between">
<template v-if="$slots.title">
<slot name="title">{{ $t('ui.captcha.title') }}</slot>
<slot name="title">{{ $t("ui.captcha.title") }}</slot>
</template>
<template v-else>
<span>{{ title }}</span>
@@ -65,14 +59,7 @@ function handleClick(e: MouseEvent) {
</CardTitle>
</CardHeader>
<CardContent class="relative mt-2 flex w-full overflow-hidden rounded p-0">
<img
v-show="captchaImage"
:alt="$t('ui.captcha.alt')"
:src="captchaImage"
:style="captchaStyles"
class="relative z-10"
@click="handleClick"
/>
<img v-show="captchaImage" :alt="$t('ui.captcha.alt')" :src="captchaImage" :style="captchaStyles" class="relative z-10" @click="handleClick" />
<div class="absolute inset-0">
<slot></slot>
</div>
@@ -1,29 +1,25 @@
<script setup lang="ts">
import type {
CaptchaVerifyPassingData,
SliderCaptchaProps,
SliderRotateVerifyPassingData,
} from '../types';
import type { CaptchaVerifyPassingData, SliderCaptchaProps, SliderRotateVerifyPassingData } from "../types";
import { reactive, unref, useTemplateRef, watch, watchEffect } from 'vue';
import { reactive, unref, useTemplateRef, watch, watchEffect } from "vue";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import { cn } from '/@/vben/shared/utils';
import { cn } from "/@/vben/shared/utils";
import { useTimeoutFn } from '@vueuse/core';
import { useTimeoutFn } from "@vueuse/core";
import SliderCaptchaAction from './slider-captcha-action.vue';
import SliderCaptchaBar from './slider-captcha-bar.vue';
import SliderCaptchaContent from './slider-captcha-content.vue';
import SliderCaptchaAction from "./slider-captcha-action.vue";
import SliderCaptchaBar from "./slider-captcha-bar.vue";
import SliderCaptchaContent from "./slider-captcha-content.vue";
const props = withDefaults(defineProps<SliderCaptchaProps>(), {
actionStyle: () => ({}),
barStyle: () => ({}),
contentStyle: () => ({}),
isSlot: false,
successText: '',
text: '',
successText: "",
text: "",
wrapperStyle: () => ({}),
});
@@ -49,21 +45,21 @@ defineExpose({
resume,
});
const wrapperRef = useTemplateRef<HTMLDivElement>('wrapperRef');
const barRef = useTemplateRef<typeof SliderCaptchaBar>('barRef');
const contentRef = useTemplateRef<typeof SliderCaptchaContent>('contentRef');
const actionRef = useTemplateRef<typeof SliderCaptchaAction>('actionRef');
const wrapperRef = useTemplateRef<HTMLDivElement>("wrapperRef");
const barRef = useTemplateRef<typeof SliderCaptchaBar>("barRef");
const contentRef = useTemplateRef<typeof SliderCaptchaContent>("contentRef");
const actionRef = useTemplateRef<typeof SliderCaptchaAction>("actionRef");
watch(
() => state.isPassing,
(isPassing) => {
isPassing => {
if (isPassing) {
const { endTime, startTime } = state;
const time = (endTime - startTime) / 1000;
emit('success', { isPassing, time: time.toFixed(1) });
emit("success", { isPassing, time: time.toFixed(1) });
modelValue.value = isPassing;
}
},
}
);
watchEffect(() => {
@@ -71,9 +67,9 @@ watchEffect(() => {
});
function getEventPageX(e: MouseEvent | TouchEvent): number {
if ('pageX' in e) {
if ("pageX" in e) {
return e.pageX;
} else if ('touches' in e && e.touches[0]) {
} else if ("touches" in e && e.touches[0]) {
return e.touches[0].pageX;
}
return 0;
@@ -84,14 +80,9 @@ function handleDragStart(e: MouseEvent | TouchEvent) {
return;
}
if (!actionRef.value) return;
emit('start', e);
emit("start", e);
state.moveDistance =
getEventPageX(e) -
Number.parseInt(
actionRef.value.getStyle().left.replace('px', '') || '0',
10,
);
state.moveDistance = getEventPageX(e) - Number.parseInt(actionRef.value.getStyle().left.replace("px", "") || "0", 10);
state.startTime = Date.now();
state.isMoving = true;
}
@@ -112,7 +103,7 @@ function handleDragMoving(e: MouseEvent | TouchEvent) {
const { actionWidth, offset, wrapperWidth } = getOffset(actionEl.getEl());
const moveX = getEventPageX(e) - moveDistance;
emit('move', {
emit("move", {
event: e,
moveDistance,
moveX,
@@ -133,7 +124,7 @@ function handleDragMoving(e: MouseEvent | TouchEvent) {
function handleDragOver(e: MouseEvent | TouchEvent) {
const { isMoving, isPassing, moveDistance } = state;
if (isMoving && !isPassing) {
emit('end', e);
emit("end", e);
const actionEl = actionRef.value;
const barEl = unref(barRef);
if (!actionEl || !barEl) return;
@@ -185,12 +176,12 @@ function resume() {
const contentEl = unref(contentRef);
if (!actionEl || !barEl || !contentEl) return;
contentEl.getEl().style.width = '100%';
contentEl.getEl().style.width = "100%";
state.toLeft = true;
useTimeoutFn(() => {
state.toLeft = false;
actionEl.setLeft('0');
barEl.setWidth('0');
actionEl.setLeft("0");
barEl.setWidth("0");
}, 300);
}
</script>
@@ -198,12 +189,7 @@ function resume() {
<template>
<div
ref="wrapperRef"
:class="
cn(
'border-border bg-background-deep relative flex h-10 w-full items-center overflow-hidden rounded-md border text-center',
props.class,
)
"
:class="cn('border-border bg-background-deep relative flex h-10 w-full items-center overflow-hidden rounded-md border text-center', props.class)"
:style="wrapperStyle"
@mouseleave="handleDragOver"
@mousemove="handleDragMoving"
@@ -211,31 +197,14 @@ function resume() {
@touchend="handleDragOver"
@touchmove="handleDragMoving"
>
<SliderCaptchaBar
ref="barRef"
:bar-style="barStyle"
:to-left="state.toLeft"
/>
<SliderCaptchaContent
ref="contentRef"
:content-style="contentStyle"
:is-passing="state.isPassing"
:success-text="successText || $t('ui.captcha.sliderSuccessText')"
:text="text || $t('ui.captcha.sliderDefaultText')"
>
<SliderCaptchaBar ref="barRef" :bar-style="barStyle" :to-left="state.toLeft" />
<SliderCaptchaContent ref="contentRef" :content-style="contentStyle" :is-passing="state.isPassing" :success-text="successText || $t('ui.captcha.sliderSuccessText')" :text="text || $t('ui.captcha.sliderDefaultText')">
<template v-if="$slots.text" #text>
<slot :is-passing="state.isPassing" name="text"></slot>
</template>
</SliderCaptchaContent>
<SliderCaptchaAction
ref="actionRef"
:action-style="actionStyle"
:is-passing="state.isPassing"
:to-left="state.toLeft"
@mousedown="handleDragStart"
@touchstart="handleDragStart"
>
<SliderCaptchaAction ref="actionRef" :action-style="actionStyle" :is-passing="state.isPassing" :to-left="state.toLeft" @mousedown="handleDragStart" @touchstart="handleDragStart">
<template v-if="$slots.actionIcon" #icon>
<slot :is-passing="state.isPassing" name="actionIcon"></slot>
</template>
@@ -1,11 +1,11 @@
<script setup lang="ts">
import type { CSSProperties } from 'vue';
import type { CSSProperties } from "vue";
import { computed, ref, useTemplateRef } from 'vue';
import { computed, ref, useTemplateRef } from "vue";
import { Check, ChevronsRight } from '/@/vben/icons';
import { Check, ChevronsRight } from "/@/vben/icons";
import { Slot } from '/@/vben/shadcn-ui';
import { Slot } from "/@/vben/shadcn-ui";
const props = defineProps<{
actionStyle: CSSProperties;
@@ -13,9 +13,9 @@ const props = defineProps<{
toLeft: boolean;
}>();
const actionRef = useTemplateRef<HTMLDivElement>('actionRef');
const actionRef = useTemplateRef<HTMLDivElement>("actionRef");
const left = ref('0');
const left = ref("0");
const style = computed(() => {
const { actionStyle } = props;
@@ -1,16 +1,16 @@
<script setup lang="ts">
import type { CSSProperties } from 'vue';
import type { CSSProperties } from "vue";
import { computed, ref, useTemplateRef } from 'vue';
import { computed, ref, useTemplateRef } from "vue";
const props = defineProps<{
barStyle: CSSProperties;
toLeft: boolean;
}>();
const barRef = useTemplateRef<HTMLDivElement>('barRef');
const barRef = useTemplateRef<HTMLDivElement>("barRef");
const width = ref('0');
const width = ref("0");
const style = computed(() => {
const { barStyle } = props;
@@ -31,10 +31,5 @@ defineExpose({
</script>
<template>
<div
ref="barRef"
:class="toLeft && 'transition-width !w-0 duration-300'"
:style="style"
class="bg-success absolute h-full"
></div>
<div ref="barRef" :class="toLeft && 'transition-width !w-0 duration-300'" :style="style" class="bg-success absolute h-full"></div>
</template>
@@ -1,9 +1,9 @@
<script setup lang="ts">
import type { CSSProperties } from 'vue';
import type { CSSProperties } from "vue";
import { computed, useTemplateRef } from 'vue';
import { computed, useTemplateRef } from "vue";
import { VbenSpineText } from '/@/vben/shadcn-ui';
import { VbenSpineText } from "/@/vben/shadcn-ui";
const props = defineProps<{
contentStyle: CSSProperties;
@@ -12,7 +12,7 @@ const props = defineProps<{
text: string;
}>();
const contentRef = useTemplateRef<HTMLDivElement>('contentRef');
const contentRef = useTemplateRef<HTMLDivElement>("contentRef");
const style = computed(() => {
const { contentStyle } = props;
@@ -1,33 +1,28 @@
<script setup lang="ts">
import type {
CaptchaVerifyPassingData,
SliderCaptchaActionType,
SliderRotateCaptchaProps,
SliderRotateVerifyPassingData,
} from '../types';
import type { CaptchaVerifyPassingData, SliderCaptchaActionType, SliderRotateCaptchaProps, SliderRotateVerifyPassingData } from "../types";
import { computed, reactive, unref, useTemplateRef, watch } from 'vue';
import { computed, reactive, unref, useTemplateRef, watch } from "vue";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import { useTimeoutFn } from '@vueuse/core';
import { useTimeoutFn } from "@vueuse/core";
import SliderCaptcha from '../slider-captcha/index.vue';
import SliderCaptcha from "../slider-captcha/index.vue";
const props = withDefaults(defineProps<SliderRotateCaptchaProps>(), {
defaultTip: '',
defaultTip: "",
diffDegree: 20,
imageSize: 260,
maxDegree: 300,
minDegree: 120,
src: '',
src: "",
});
const emit = defineEmits<{
success: [CaptchaVerifyPassingData];
}>();
const slideBarRef = useTemplateRef<SliderCaptchaActionType>('slideBarRef');
const slideBarRef = useTemplateRef<SliderCaptchaActionType>("slideBarRef");
const state = reactive({
currentRotate: 0,
@@ -45,14 +40,14 @@ const modalValue = defineModel<boolean>({ default: false });
watch(
() => state.isPassing,
(isPassing) => {
isPassing => {
if (isPassing) {
const { endTime, startTime } = state;
const time = (endTime - startTime) / 1000;
emit('success', { isPassing, time: time.toFixed(1) });
emit("success", { isPassing, time: time.toFixed(1) });
}
modalValue.value = isPassing;
},
}
);
const getImgWrapStyleRef = computed(() => {
@@ -67,7 +62,7 @@ const getImgWrapStyleRef = computed(() => {
const getFactorRef = computed(() => {
const { maxDegree, minDegree } = props;
if (minDegree > maxDegree) {
console.warn('minDegree should not be greater than maxDegree');
console.warn("minDegree should not be greater than maxDegree");
}
if (minDegree === maxDegree) {
@@ -88,18 +83,14 @@ function handleDragBarMove(data: SliderRotateVerifyPassingData) {
if (denominator === 0) {
return;
}
const currentRotate = Math.ceil(
(moveX / denominator) * 1.5 * maxDegree! * unref(getFactorRef),
);
const currentRotate = Math.ceil((moveX / denominator) * 1.5 * maxDegree! * unref(getFactorRef));
state.currentRotate = currentRotate;
setImgRotate(state.randomRotate - currentRotate);
}
function handleImgOnLoad() {
const { maxDegree, minDegree } = props;
const ranRotate = Math.floor(
minDegree! + Math.random() * (maxDegree! - minDegree!),
); // 生成随机角度
const ranRotate = Math.floor(minDegree! + Math.random() * (maxDegree! - minDegree!)); // 生成随机角度
state.randomRotate = ranRotate;
setImgRotate(ranRotate);
}
@@ -147,15 +138,11 @@ function resume() {
}
const imgCls = computed(() => {
return state.toOrigin ? ['transition-transform duration-300'] : [];
return state.toOrigin ? ["transition-transform duration-300"] : [];
});
const verifyTip = computed(() => {
return state.isPassing
? $t('ui.captcha.sliderRotateSuccessTip', [
((state.endTime - state.startTime) / 1000).toFixed(1),
])
: $t('ui.captcha.sliderRotateFailTip');
return state.isPassing ? $t("ui.captcha.sliderRotateSuccessTip", [((state.endTime - state.startTime) / 1000).toFixed(1)]) : $t("ui.captcha.sliderRotateFailTip");
});
defineExpose({
@@ -165,22 +152,9 @@ defineExpose({
<template>
<div class="relative flex flex-col items-center">
<div
:style="getImgWrapStyleRef"
class="border-border relative cursor-pointer overflow-hidden rounded-full border shadow-md"
>
<img
:class="imgCls"
:src="src"
:style="state.imgStyle"
alt="verify"
class="w-full rounded-full"
@click="resume"
@load="handleImgOnLoad"
/>
<div
class="absolute bottom-3 left-0 z-10 block h-7 w-full text-center text-xs leading-[30px] text-white"
>
<div :style="getImgWrapStyleRef" class="border-border relative cursor-pointer overflow-hidden rounded-full border shadow-md">
<img :class="imgCls" :src="src" :style="state.imgStyle" alt="verify" class="w-full rounded-full" @click="resume" @load="handleImgOnLoad" />
<div class="absolute bottom-3 left-0 z-10 block h-7 w-full text-center text-xs leading-[30px] text-white">
<div
v-if="state.showTip"
:class="{
@@ -191,20 +165,12 @@ defineExpose({
{{ verifyTip }}
</div>
<div v-if="!state.dragging" class="bg-black/30">
{{ defaultTip || $t('ui.captcha.sliderRotateDefaultTip') }}
{{ defaultTip || $t("ui.captcha.sliderRotateDefaultTip") }}
</div>
</div>
</div>
<SliderCaptcha
ref="slideBarRef"
v-model="modalValue"
class="mt-5"
is-slot
@end="handleDragEnd"
@move="handleDragBarMove"
@start="handleStart"
>
<SliderCaptcha ref="slideBarRef" v-model="modalValue" class="mt-5" is-slot @end="handleDragEnd" @move="handleDragBarMove" @start="handleStart">
<template v-for="(_, key) in $slots" :key="key" #[key]="slotProps">
<slot :name="key" v-bind="slotProps"></slot>
</template>
@@ -1,6 +1,6 @@
import type { CSSProperties } from 'vue';
import type { CSSProperties } from "vue";
import type { ClassType } from '/@/vben/types';
import type { ClassType } from "/@/vben/types";
export interface CaptchaData {
/**
@@ -54,8 +54,7 @@ export interface PointSelectionCaptchaCardProps {
width?: number | string;
}
export interface PointSelectionCaptchaProps
extends PointSelectionCaptchaCardProps {
export interface PointSelectionCaptchaProps extends PointSelectionCaptchaCardProps {
/**
* 是否展示确定按钮
* @default false
@@ -1,18 +1,14 @@
<script lang="ts" setup>
import type { ColPageProps } from './types';
import type { ColPageProps } from "./types";
import { computed, ref, useSlots } from 'vue';
import { computed, ref, useSlots } from "vue";
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from '/@/vben/shadcn-ui';
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "/@/vben/shadcn-ui";
import Page from '../page/page.vue';
import Page from "../page/page.vue";
defineOptions({
name: 'ColPage',
name: "ColPage",
inheritAttrs: false,
});
@@ -33,7 +29,7 @@ const delegatedSlots = computed(() => {
const resultSlots: string[] = [];
for (const key of Object.keys(slots)) {
if (!['default', 'left'].includes(key)) {
if (!["default", "left"].includes(key)) {
resultSlots.push(key);
}
}
@@ -58,23 +54,12 @@ defineExpose({
<template>
<Page v-bind="delegatedProps">
<!-- 继承默认的slot -->
<template
v-for="slotName in delegatedSlots"
:key="slotName"
#[slotName]="slotProps"
>
<template v-for="slotName in delegatedSlots" :key="slotName" #[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps"></slot>
</template>
<ResizablePanelGroup class="w-full" direction="horizontal">
<ResizablePanel
ref="leftPanelRef"
:collapsed-size="leftCollapsedWidth"
:collapsible="leftCollapsible"
:default-size="leftWidth"
:max-size="leftMaxWidth"
:min-size="leftMinWidth"
>
<ResizablePanel ref="leftPanelRef" :collapsed-size="leftCollapsedWidth" :collapsible="leftCollapsible" :default-size="leftWidth" :max-size="leftMaxWidth" :min-size="leftMinWidth">
<template #default="slotProps">
<slot
name="left"
@@ -86,18 +71,8 @@ defineExpose({
></slot>
</template>
</ResizablePanel>
<ResizableHandle
v-if="resizable"
:style="{ backgroundColor: splitLine ? undefined : 'transparent' }"
:with-handle="splitHandle"
/>
<ResizablePanel
:collapsed-size="rightCollapsedWidth"
:collapsible="rightCollapsible"
:default-size="rightWidth"
:max-size="rightMaxWidth"
:min-size="rightMinWidth"
>
<ResizableHandle v-if="resizable" :style="{ backgroundColor: splitLine ? undefined : 'transparent' }" :with-handle="splitHandle" />
<ResizablePanel :collapsed-size="rightCollapsedWidth" :collapsible="rightCollapsible" :default-size="rightWidth" :max-size="rightMaxWidth" :min-size="rightMinWidth">
<template #default>
<slot></slot>
</template>
@@ -1,2 +1,2 @@
export { default as ColPage } from './col-page.vue';
export * from './types';
export { default as ColPage } from "./col-page.vue";
export * from "./types";
@@ -1,4 +1,4 @@
import type { PageProps } from '../page/types';
import type { PageProps } from "../page/types";
export interface ColPageProps extends PageProps {
/**
@@ -1,23 +1,23 @@
<script lang="ts" setup>
import type { CountToProps } from './types';
import type { CountToProps } from "./types";
import { computed, onMounted, ref, watch } from 'vue';
import { computed, onMounted, ref, watch } from "vue";
import { isString } from '/@/vben/shared/utils';
import { isString } from "/@/vben/shared/utils";
import { TransitionPresets, useTransition } from '@vueuse/core';
import { TransitionPresets, useTransition } from "@vueuse/core";
const props = withDefaults(defineProps<CountToProps>(), {
startVal: 0,
duration: 2000,
separator: ',',
decimal: '.',
separator: ",",
decimal: ".",
decimals: 0,
delay: 0,
transition: () => TransitionPresets.easeOutExpo,
});
const emit = defineEmits(['started', 'finished']);
const emit = defineEmits(["started", "finished"]);
const lastValue = ref(props.startVal);
@@ -27,9 +27,9 @@ onMounted(() => {
watch(
() => props.endVal,
(val) => {
val => {
lastValue.value = val;
},
}
);
const currentValue = useTransition(lastValue, {
@@ -37,62 +37,43 @@ const currentValue = useTransition(lastValue, {
duration: computed(() => props.duration),
disabled: computed(() => props.disabled),
transition: computed(() => {
return isString(props.transition)
? TransitionPresets[props.transition]
: props.transition;
return isString(props.transition) ? TransitionPresets[props.transition] : props.transition;
}),
onStarted() {
emit('started');
emit("started");
},
onFinished() {
emit('finished');
emit("finished");
},
});
const numMain = computed(() => {
const result = currentValue.value
.toFixed(props.decimals)
.split('.')[0]
.split(".")[0]
?.replaceAll(/\B(?=(\d{3})+(?!\d))/g, props.separator);
return result;
});
const numDec = computed(() => {
return (
props.decimal + currentValue.value.toFixed(props.decimals).split('.')[1]
);
return props.decimal + currentValue.value.toFixed(props.decimals).split(".")[1];
});
</script>
<template>
<div class="count-to" v-bind="$attrs">
<slot name="prefix">
<div
class="count-to-prefix"
:style="prefixStyle"
:class="prefixClass"
v-if="prefix"
>
<div v-if="prefix" class="count-to-prefix" :style="prefixStyle" :class="prefixClass">
{{ prefix }}
</div>
</slot>
<div class="count-to-main" :class="mainClass" :style="mainStyle">
<span>{{ numMain }}</span>
<span
class="count-to-main-decimal"
v-if="decimals > 0"
:class="decimalClass"
:style="decimalStyle"
>
<span v-if="decimals > 0" class="count-to-main-decimal" :class="decimalClass" :style="decimalStyle">
{{ numDec }}
</span>
</div>
<slot name="suffix">
<div
class="count-to-suffix"
:style="suffixStyle"
:class="suffixClass"
v-if="suffix"
>
<div v-if="suffix" class="count-to-suffix" :style="suffixStyle" :class="suffixClass">
{{ suffix }}
</div>
</slot>
@@ -112,7 +93,7 @@ const numDec = computed(() => {
}
&-main {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
// font-size: 1.5rem;
&-decimal {
@@ -1,2 +1,2 @@
export { default as CountTo } from './count-to.vue';
export * from './types';
export { default as CountTo } from "./count-to.vue";
export * from "./types";
@@ -1,14 +1,12 @@
import type { CubicBezierPoints, EasingFunction } from '@vueuse/core';
import type { CubicBezierPoints, EasingFunction } from "@vueuse/core";
import type { StyleValue } from 'vue';
import type { StyleValue } from "vue";
import { TransitionPresets as TransitionPresetsData } from '@vueuse/core';
import { TransitionPresets as TransitionPresetsData } from "@vueuse/core";
export type TransitionPresets = keyof typeof TransitionPresetsData;
export const TransitionPresetsKeys = Object.keys(
TransitionPresetsData,
) as TransitionPresets[];
export const TransitionPresetsKeys = Object.keys(TransitionPresetsData) as TransitionPresets[];
export interface CountToProps {
/** 初始值 */
@@ -1,11 +1,11 @@
<script setup lang="ts">
import type { CSSProperties } from 'vue';
import type { CSSProperties } from "vue";
import { computed, ref, watchEffect } from 'vue';
import { computed, ref, watchEffect } from "vue";
import { VbenTooltip } from '/@/vben/shadcn-ui';
import { VbenTooltip } from "/@/vben/shadcn-ui";
import { useElementSize } from '@vueuse/core';
import { useElementSize } from "@vueuse/core";
interface Props {
/**
@@ -27,7 +27,7 @@ interface Props {
* 提示框位置
* @default 'top'
*/
placement?: 'bottom' | 'left' | 'right' | 'top';
placement?: "bottom" | "left" | "right" | "top";
/**
* 是否启用文本提示框
* @default true
@@ -59,19 +59,19 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
expand: false,
line: 1,
maxWidth: '100%',
placement: 'top',
maxWidth: "100%",
placement: "top",
tooltip: true,
tooltipBackgroundColor: '',
tooltipColor: '',
tooltipBackgroundColor: "",
tooltipColor: "",
tooltipFontSize: 14,
tooltipMaxWidth: undefined,
tooltipOverlayStyle: () => ({ textAlign: 'justify' }),
tooltipOverlayStyle: () => ({ textAlign: "justify" }),
});
const emit = defineEmits<{ expandChange: [boolean] }>();
const textMaxWidth = computed(() => {
if (typeof props.maxWidth === 'number') {
if (typeof props.maxWidth === "number") {
return `${props.maxWidth}px`;
}
return props.maxWidth;
@@ -85,15 +85,14 @@ const { width: eleWidth } = useElementSize(ellipsis);
watchEffect(
() => {
if (props.tooltip && eleWidth.value) {
defaultTooltipMaxWidth.value =
props.tooltipMaxWidth ?? eleWidth.value + 24;
defaultTooltipMaxWidth.value = props.tooltipMaxWidth ?? eleWidth.value + 24;
}
},
{ flush: 'post' },
{ flush: "post" }
);
function onExpand() {
isExpand.value = !isExpand.value;
emit('expandChange', isExpand.value);
emit("expandChange", isExpand.value);
}
function handleExpand() {
@@ -130,8 +129,8 @@ function handleExpand() {
'max-width': textMaxWidth,
}"
class="cursor-text overflow-hidden"
@click="handleExpand"
v-bind="$attrs"
@click="handleExpand"
>
<slot></slot>
</div>
@@ -1 +1 @@
export { default as EllipsisText } from './ellipsis-text.vue';
export { default as EllipsisText } from "./ellipsis-text.vue";
@@ -1,11 +1,11 @@
<script setup lang="ts">
import type { VNode } from 'vue';
import type { VNode } from "vue";
import { computed, ref, watch, watchEffect } from 'vue';
import { computed, ref, watch, watchEffect } from "vue";
import { usePagination } from '/@/vben/hooks';
import { EmptyIcon, Grip, listIcons } from '/@/vben/icons';
import { $t } from '/@/locales';
import { usePagination } from "/@/vben/hooks";
import { EmptyIcon, Grip, listIcons } from "/@/vben/icons";
import { $t } from "/@/locales";
import {
Button,
@@ -21,11 +21,11 @@ import {
VbenIcon,
VbenIconButton,
VbenPopover,
} from '/@/vben/shadcn-ui';
} from "/@/vben/shadcn-ui";
import { refDebounced, watchDebounced } from '@vueuse/core';
import { refDebounced, watchDebounced } from "@vueuse/core";
import { fetchIconsData } from './icons';
import { fetchIconsData } from "./icons";
interface Props {
pageSize?: number;
@@ -45,55 +45,51 @@ interface Props {
modelValueProp?: string;
/** 图标样式 */
iconClass?: string;
type?: 'icon' | 'input';
type?: "icon" | "input";
}
const props = withDefaults(defineProps<Props>(), {
prefix: 'ant-design',
prefix: "ant-design",
pageSize: 36,
icons: () => [],
iconSlot: 'default',
iconClass: 'size-4',
iconSlot: "default",
iconClass: "size-4",
autoFetchApi: true,
modelValueProp: 'modelValue',
modelValueProp: "modelValue",
inputComponent: undefined,
type: 'input',
type: "input",
});
const emit = defineEmits<{
change: [string];
}>();
const modelValue = defineModel({ default: '', type: String });
const modelValue = defineModel({ default: "", type: String });
const visible = ref(false);
const currentSelect = ref('');
const currentSelect = ref("");
const currentPage = ref(1);
const keyword = ref('');
const keyword = ref("");
const keywordDebounce = refDebounced(keyword, 300);
const innerIcons = ref<string[]>([]);
watchDebounced(
() => props.prefix,
async (prefix) => {
if (prefix && prefix !== 'svg' && props.autoFetchApi) {
async prefix => {
if (prefix && prefix !== "svg" && props.autoFetchApi) {
innerIcons.value = await fetchIconsData(prefix);
}
},
{ immediate: true, debounce: 500, maxWait: 1000 },
{ immediate: true, debounce: 500, maxWait: 1000 }
);
const currentList = computed(() => {
try {
if (props.prefix) {
if (
props.prefix !== 'svg' &&
props.autoFetchApi &&
props.icons.length === 0
) {
if (props.prefix !== "svg" && props.autoFetchApi && props.icons.length === 0) {
return innerIcons.value;
}
const icons = listIcons('', props.prefix);
const icons = listIcons("", props.prefix);
if (icons.length === 0) {
console.warn(`No icons found for prefix: ${props.prefix}`);
}
@@ -102,21 +98,16 @@ const currentList = computed(() => {
return props.icons;
}
} catch (error) {
console.error('Failed to load icons:', error);
console.error("Failed to load icons:", error);
return [];
}
});
const showList = computed(() => {
return currentList.value.filter((item) =>
item.includes(keywordDebounce.value),
);
return currentList.value.filter(item => item.includes(keywordDebounce.value));
});
const { paginationList, total, setCurrentPage } = usePagination(
showList,
props.pageSize,
);
const { paginationList, total, setCurrentPage } = usePagination(showList, props.pageSize);
watchEffect(() => {
currentSelect.value = modelValue.value;
@@ -124,9 +115,9 @@ watchEffect(() => {
watch(
() => currentSelect.value,
(v) => {
emit('change', v);
},
v => {
emit("change", v);
}
);
const handleClick = (icon: string) => {
@@ -158,26 +149,22 @@ function onKeywordChange(v: string) {
const searchInputProps = computed(() => {
return {
placeholder: $t('ui.iconPicker.search'),
placeholder: $t("ui.iconPicker.search"),
[props.modelValueProp]: keyword.value,
[`onUpdate:${props.modelValueProp}`]: onKeywordChange,
class: 'mx-2',
class: "mx-2",
};
});
defineExpose({ toggleOpenState, open, close });
</script>
<template>
<VbenPopover
v-model:open="visible"
:content-props="{ align: 'end', alignOffset: -11, sideOffset: 8 }"
content-class="p-0 pt-3"
>
<VbenPopover v-model:open="visible" :content-props="{ align: 'end', alignOffset: -11, sideOffset: 8 }" content-class="p-0 pt-3">
<template #trigger>
<template v-if="props.type === 'input'">
<component
v-if="props.inputComponent"
:is="inputComponent"
v-if="props.inputComponent"
:[modelValueProp]="currentSelect"
:placeholder="$t('ui.iconPicker.placeholder')"
role="combobox"
@@ -186,60 +173,24 @@ defineExpose({ toggleOpenState, open, close });
v-bind="$attrs"
>
<template #[iconSlot]>
<VbenIcon
:icon="currentSelect || Grip"
class="size-4"
aria-hidden="true"
/>
<VbenIcon :icon="currentSelect || Grip" class="size-4" aria-hidden="true" />
</template>
</component>
<div class="relative w-full" v-else>
<Input
v-bind="$attrs"
v-model="currentSelect"
:placeholder="$t('ui.iconPicker.placeholder')"
class="h-8 w-full pr-8"
role="combobox"
:aria-label="$t('ui.iconPicker.placeholder')"
aria-expanded="visible"
/>
<VbenIcon
:icon="currentSelect || Grip"
class="absolute right-1 top-1 size-6"
aria-hidden="true"
/>
<div v-else class="relative w-full">
<Input v-bind="$attrs" v-model="currentSelect" :placeholder="$t('ui.iconPicker.placeholder')" class="h-8 w-full pr-8" role="combobox" :aria-label="$t('ui.iconPicker.placeholder')" aria-expanded="visible" />
<VbenIcon :icon="currentSelect || Grip" class="absolute right-1 top-1 size-6" aria-hidden="true" />
</div>
</template>
<VbenIcon
:icon="currentSelect || Grip"
v-else
class="size-4"
v-bind="$attrs"
/>
<VbenIcon v-else :icon="currentSelect || Grip" class="size-4" v-bind="$attrs" />
</template>
<div class="mb-2 flex w-full">
<component
v-if="inputComponent"
:is="inputComponent"
v-bind="searchInputProps"
/>
<Input
v-else
class="mx-2 h-8 w-full"
:placeholder="$t('ui.iconPicker.search')"
v-model="keyword"
/>
<component :is="inputComponent" v-if="inputComponent" v-bind="searchInputProps" />
<Input v-else v-model="keyword" class="mx-2 h-8 w-full" :placeholder="$t('ui.iconPicker.search')" />
</div>
<template v-if="paginationList.length > 0">
<div class="grid max-h-[360px] w-full grid-cols-6 justify-items-center">
<VbenIconButton
v-for="(item, index) in paginationList"
:key="index"
:tooltip="item"
tooltip-side="top"
@click="handleClick(item)"
>
<VbenIconButton v-for="(item, index) in paginationList" :key="index" :tooltip="item" tooltip-side="top" @click="handleClick(item)">
<VbenIcon
:class="{
'text-primary transition-all': currentSelect === item,
@@ -248,44 +199,18 @@ defineExpose({ toggleOpenState, open, close });
/>
</VbenIconButton>
</div>
<div
v-if="total >= pageSize"
class="flex-center flex justify-end overflow-hidden border-t py-2 pr-3"
>
<Pagination
:items-per-page="36"
:sibling-count="1"
:total="total"
show-edges
size="small"
@update:page="handlePageChange"
>
<PaginationList
v-slot="{ items }"
class="flex w-full items-center gap-1"
>
<div v-if="total >= pageSize" class="flex-center flex justify-end overflow-hidden border-t py-2 pr-3">
<Pagination :items-per-page="36" :sibling-count="1" :total="total" show-edges size="small" @update:page="handlePageChange">
<PaginationList v-slot="{ items }" class="flex w-full items-center gap-1">
<PaginationFirst class="size-5" />
<PaginationPrev class="size-5" />
<template v-for="(item, index) in items">
<PaginationListItem
v-if="item.type === 'page'"
:key="index"
:value="item.value"
as-child
>
<Button
:variant="item.value === currentPage ? 'default' : 'outline'"
class="size-5 p-0 text-sm"
>
<PaginationListItem v-if="item.type === 'page'" :key="index" :value="item.value" as-child>
<Button :variant="item.value === currentPage ? 'default' : 'outline'" class="size-5 p-0 text-sm">
{{ item.value }}
</Button>
</PaginationListItem>
<PaginationEllipsis
v-else
:key="item.type"
:index="index"
class="size-5"
/>
<PaginationEllipsis v-else :key="item.type" :index="index" class="size-5" />
</template>
<PaginationNext class="size-5" />
<PaginationLast class="size-5" />
@@ -297,7 +222,7 @@ defineExpose({ toggleOpenState, open, close });
<template v-else>
<div class="flex-col-center text-muted-foreground min-h-[150px] w-full">
<EmptyIcon class="size-10" />
<div class="mt-1 text-sm">{{ $t('common.noData') }}</div>
<div class="mt-1 text-sm">{{ $t("common.noData") }}</div>
</div>
</template>
</VbenPopover>
@@ -1,4 +1,4 @@
import type { Recordable } from '/@/vben/types';
import type { Recordable } from "/@/vben/types";
/**
* 一个缓存对象,在不刷新页面时,无需重复请求远程接口
@@ -34,10 +34,7 @@ export async function fetchIconsData(prefix: string): Promise<string[]> {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 1000 * 10);
const response: IconifyResponse = await fetch(
`https://api.iconify.design/collection?prefix=${prefix}`,
{ signal: controller.signal },
).then((res) => res.json());
const response: IconifyResponse = await fetch(`https://api.iconify.design/collection?prefix=${prefix}`, { signal: controller.signal }).then(res => res.json());
clearTimeout(timeoutId);
const list = response.uncategorized || [];
if (response.categories) {
@@ -45,7 +42,7 @@ export async function fetchIconsData(prefix: string): Promise<string[]> {
list.push(...(response.categories[category] || []));
}
}
ICONS_MAP[prefix] = list.map((v) => `${prefix}:${v}`);
ICONS_MAP[prefix] = list.map(v => `${prefix}:${v}`);
} catch (error) {
console.error(`Failed to fetch icons for prefix ${prefix}:`, error);
return [] as string[];
@@ -1 +1 @@
export { default as IconPicker } from './icon-picker.vue';
export { default as IconPicker } from "./icon-picker.vue";
@@ -1,27 +1,18 @@
export * from './api-component';
export * from './captcha';
export * from './col-page';
export * from './count-to';
export * from './ellipsis-text';
export * from './icon-picker';
export * from './json-viewer';
export * from './loading';
export * from './page';
export * from './resize';
export * from './tippy';
export * from '/@/vben/form-ui';
export * from '/@/vben/popup-ui';
export * from "./api-component";
export * from "./captcha";
export * from "./col-page";
export * from "./count-to";
export * from "./ellipsis-text";
export * from "./icon-picker";
export * from "./json-viewer";
export * from "./loading";
export * from "./page";
export * from "./resize";
export * from "./tippy";
export * from "/@/vben/form-ui";
export * from "/@/vben/popup-ui";
// 给文档用
export {
VbenButton,
VbenButtonGroup,
VbenCheckButtonGroup,
VbenCountToAnimator,
VbenInputPassword,
VbenLoading,
VbenPinInput,
VbenSpinner,
} from '/@/vben/shadcn-ui';
export { VbenButton, VbenButtonGroup, VbenCheckButtonGroup, VbenCountToAnimator, VbenInputPassword, VbenLoading, VbenPinInput, VbenSpinner } from "/@/vben/shadcn-ui";
export { globalShareState } from '/@/vben/shared/global-state';
export { globalShareState } from "/@/vben/shared/global-state";
@@ -1,3 +1,3 @@
export { default as JsonViewer } from './index.vue';
export { default as JsonViewer } from "./index.vue";
export * from './types';
export * from "./types";
@@ -1,31 +1,26 @@
<script lang="ts" setup>
import type { SetupContext } from 'vue';
import type { SetupContext } from "vue";
import type { Recordable } from '/@/vben/types';
import type { Recordable } from "/@/vben/types";
import type {
JsonViewerAction,
JsonViewerProps,
JsonViewerToggle,
JsonViewerValue,
} from './types';
import type { JsonViewerAction, JsonViewerProps, JsonViewerToggle, JsonViewerValue } from "./types";
import { computed, useAttrs } from 'vue';
import { computed, useAttrs } from "vue";
// @ts-ignore
import VueJsonViewer from 'vue-json-viewer';
import VueJsonViewer from "vue-json-viewer";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import { isBoolean } from '/@/vben/shared/utils';
import { isBoolean } from "/@/vben/shared/utils";
defineOptions({ name: 'JsonViewer' });
defineOptions({ name: "JsonViewer" });
const props = withDefaults(defineProps<JsonViewerProps>(), {
expandDepth: 1,
copyable: false,
sort: false,
boxed: false,
theme: 'default-json-theme',
theme: "default-json-theme",
expanded: false,
previewMode: false,
showArrayIndex: true,
@@ -40,38 +35,35 @@ const emit = defineEmits<{
valueClick: [value: JsonViewerValue];
}>();
const attrs: SetupContext['attrs'] = useAttrs();
const attrs: SetupContext["attrs"] = useAttrs();
function handleClick(event: MouseEvent) {
if (
event.target instanceof HTMLElement &&
event.target.classList.contains('jv-item')
) {
const pathNode = event.target.closest('.jv-push');
if (!pathNode || !pathNode.hasAttribute('path')) {
if (event.target instanceof HTMLElement && event.target.classList.contains("jv-item")) {
const pathNode = event.target.closest(".jv-push");
if (!pathNode || !pathNode.hasAttribute("path")) {
return;
}
const param: JsonViewerValue = {
path: '',
value: '',
path: "",
value: "",
depth: 0,
el: event.target,
};
param.path = pathNode.getAttribute('path') || '';
param.depth = Number(pathNode.getAttribute('depth')) || 0;
param.path = pathNode.getAttribute("path") || "";
param.depth = Number(pathNode.getAttribute("depth")) || 0;
param.value = event.target.textContent || undefined;
param.value = JSON.parse(param.value);
emit('valueClick', param);
emit("valueClick", param);
}
emit('click', event);
emit("click", event);
}
const bindProps = computed<Recordable<any>>(() => {
const copyable = {
copyText: $t('ui.jsonViewer.copy'),
copiedText: $t('ui.jsonViewer.copied'),
copyText: $t("ui.jsonViewer.copy"),
copiedText: $t("ui.jsonViewer.copied"),
timeout: 2000,
...(isBoolean(props.copyable) ? {} : props.copyable),
};
@@ -79,8 +71,8 @@ const bindProps = computed<Recordable<any>>(() => {
return {
...props,
...attrs,
onCopied: (event: JsonViewerAction) => emit('copied', event),
onKeyclick: (key: string) => emit('keyClick', key),
onCopied: (event: JsonViewerAction) => emit("copied", event),
onKeyclick: (key: string) => emit("keyClick", key),
onClick: (event: MouseEvent) => handleClick(event),
copyable: props.copyable ? copyable : false,
};
@@ -94,5 +86,5 @@ const bindProps = computed<Recordable<any>>(() => {
</VueJsonViewer>
</template>
<style lang="scss">
@use './style.scss';
@use "./style.scss";
</style>
@@ -1,14 +1,14 @@
import type { App, Directive, DirectiveBinding } from 'vue';
import type { App, Directive, DirectiveBinding } from "vue";
import { h, render } from 'vue';
import { h, render } from "vue";
import { VbenLoading, VbenSpinner } from '/@/vben/shadcn-ui';
import { isString } from '/@/vben/shared/utils';
import { VbenLoading, VbenSpinner } from "/@/vben/shadcn-ui";
import { isString } from "/@/vben/shared/utils";
const LOADING_INSTANCE_KEY = Symbol('loading');
const SPINNER_INSTANCE_KEY = Symbol('spinner');
const LOADING_INSTANCE_KEY = Symbol("loading");
const SPINNER_INSTANCE_KEY = Symbol("spinner");
const CLASS_NAME_RELATIVE = 'spinner-parent--relative';
const CLASS_NAME_RELATIVE = "spinner-parent--relative";
const loadingDirective: Directive = {
mounted(el, binding) {
@@ -32,15 +32,12 @@ const loadingDirective: Directive = {
const options = getOptions(binding);
if (options && instance?.component) {
try {
Object.keys(options).forEach((key) => {
Object.keys(options).forEach(key => {
instance.component.props[key] = options[key];
});
instance.component.update();
} catch (error) {
console.error(
'Failed to update loading component in directive:',
error,
);
console.error("Failed to update loading component in directive:", error);
}
}
},
@@ -49,7 +46,7 @@ const loadingDirective: Directive = {
function getOptions(binding: DirectiveBinding) {
if (binding.value === undefined) {
return { spinning: true };
} else if (typeof binding.value === 'boolean') {
} else if (typeof binding.value === "boolean") {
return { spinning: binding.value };
} else {
return { ...binding.value };
@@ -78,15 +75,12 @@ const spinningDirective: Directive = {
const options = getOptions(binding);
if (options && instance?.component) {
try {
Object.keys(options).forEach((key) => {
Object.keys(options).forEach(key => {
instance.component.props[key] = options[key];
});
instance.component.update();
} catch (error) {
console.error(
'Failed to update spinner component in directive:',
error,
);
console.error("Failed to update spinner component in directive:", error);
}
}
},
@@ -104,12 +98,9 @@ type loadingDirectiveParams = {
* @param app
* @param params
*/
export function registerLoadingDirective(
app: App,
params?: loadingDirectiveParams,
) {
export function registerLoadingDirective(app: App, params?: loadingDirectiveParams) {
// 注入一个样式供指令使用,确保容器是相对定位
const style = document.createElement('style');
const style = document.createElement("style");
style.id = CLASS_NAME_RELATIVE;
style.innerHTML = `
.${CLASS_NAME_RELATIVE} {
@@ -118,15 +109,9 @@ export function registerLoadingDirective(
`;
document.head.append(style);
if (params?.loading !== false) {
app.directive(
isString(params?.loading) ? params.loading : 'loading',
loadingDirective,
);
app.directive(isString(params?.loading) ? params.loading : "loading", loadingDirective);
}
if (params?.spinning !== false) {
app.directive(
isString(params?.spinning) ? params.spinning : 'spinning',
spinningDirective,
);
app.directive(isString(params?.spinning) ? params.spinning : "spinning", spinningDirective);
}
}
@@ -1,3 +1,3 @@
export * from './directive';
export { default as Loading } from './loading.vue';
export { default as Spinner } from './spinner.vue';
export * from "./directive";
export { default as Loading } from "./loading.vue";
export { default as Spinner } from "./spinner.vue";
@@ -1,7 +1,9 @@
<script lang="ts" setup>
import { VbenLoading } from '/@/vben/shadcn-ui';
import { cn } from '/@/vben/shared/utils';
import { VbenLoading } from "/@/vben/shadcn-ui";
import { cn } from "/@/vben/shared/utils";
defineOptions({
name: "VbenLoading",
});
interface LoadingProps {
class?: string;
/**
@@ -20,17 +22,13 @@ interface LoadingProps {
text?: string;
}
defineOptions({ name: 'Loading' });
defineOptions({ name: "Loading" });
const props = defineProps<LoadingProps>();
</script>
<template>
<div :class="cn('relative min-h-20', props.class)">
<slot></slot>
<VbenLoading
:min-loading-time="props.minLoadingTime"
:spinning="props.spinning"
:text="props.text"
>
<VbenLoading :min-loading-time="props.minLoadingTime" :spinning="props.spinning" :text="props.text">
<template v-if="$slots.icon" #icon>
<slot name="icon"></slot>
</template>
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { VbenSpinner } from '/@/vben/shadcn-ui';
import { cn } from '/@/vben/shared/utils';
import { VbenSpinner } from "/@/vben/shadcn-ui";
import { cn } from "/@/vben/shared/utils";
interface SpinnerProps {
class?: string;
@@ -14,15 +14,12 @@ interface SpinnerProps {
*/
spinning?: boolean;
}
defineOptions({ name: 'Spinner' });
defineOptions({ name: "Spinner" });
const props = defineProps<SpinnerProps>();
</script>
<template>
<div :class="cn('relative min-h-20', props.class)">
<slot></slot>
<VbenSpinner
:min-loading-time="props.minLoadingTime"
:spinning="props.spinning"
/>
<VbenSpinner :min-loading-time="props.minLoadingTime" :spinning="props.spinning" />
</div>
</template>
@@ -1,89 +1,89 @@
import { mount } from '@vue/test-utils';
import { mount } from "@vue/test-utils";
import { describe, expect, it } from 'vitest';
import { describe, expect, it } from "vitest";
import { Page } from '..';
import { Page } from "..";
describe('page.vue', () => {
it('renders title when passed', () => {
describe("page.vue", () => {
it("renders title when passed", () => {
const wrapper = mount(Page, {
props: {
title: 'Test Title',
title: "Test Title",
},
});
expect(wrapper.text()).toContain('Test Title');
expect(wrapper.text()).toContain("Test Title");
});
it('renders description when passed', () => {
it("renders description when passed", () => {
const wrapper = mount(Page, {
props: {
description: 'Test Description',
description: "Test Description",
},
});
expect(wrapper.text()).toContain('Test Description');
expect(wrapper.text()).toContain("Test Description");
});
it('renders default slot content', () => {
it("renders default slot content", () => {
const wrapper = mount(Page, {
slots: {
default: '<p>Default Slot Content</p>',
default: "<p>Default Slot Content</p>",
},
});
expect(wrapper.html()).toContain('<p>Default Slot Content</p>');
expect(wrapper.html()).toContain("<p>Default Slot Content</p>");
});
it('renders footer slot when showFooter is true', () => {
it("renders footer slot when showFooter is true", () => {
const wrapper = mount(Page, {
props: {
showFooter: true,
},
slots: {
footer: '<p>Footer Slot Content</p>',
footer: "<p>Footer Slot Content</p>",
},
});
expect(wrapper.html()).toContain('<p>Footer Slot Content</p>');
expect(wrapper.html()).toContain("<p>Footer Slot Content</p>");
});
it('applies the custom contentClass', () => {
it("applies the custom contentClass", () => {
const wrapper = mount(Page, {
props: {
contentClass: 'custom-class',
contentClass: "custom-class",
},
});
const contentDiv = wrapper.find('.p-4');
expect(contentDiv.classes()).toContain('custom-class');
const contentDiv = wrapper.find(".p-4");
expect(contentDiv.classes()).toContain("custom-class");
});
it('does not render title slot if title prop is provided', () => {
it("does not render title slot if title prop is provided", () => {
const wrapper = mount(Page, {
props: {
title: 'Test Title',
title: "Test Title",
},
slots: {
title: '<p>Title Slot Content</p>',
title: "<p>Title Slot Content</p>",
},
});
expect(wrapper.text()).toContain('Title Slot Content');
expect(wrapper.html()).not.toContain('Test Title');
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', () => {
it("does not render description slot if description prop is provided", () => {
const wrapper = mount(Page, {
props: {
description: 'Test Description',
description: "Test Description",
},
slots: {
description: '<p>Description Slot Content</p>',
description: "<p>Description Slot Content</p>",
},
});
expect(wrapper.text()).toContain('Description Slot Content');
expect(wrapper.html()).not.toContain('Test Description');
expect(wrapper.text()).toContain("Description Slot Content");
expect(wrapper.html()).not.toContain("Test Description");
});
});
@@ -1,2 +1,2 @@
export { default as Page } from './page.vue';
export * from './types';
export { default as Page } from "./page.vue";
export * from "./types";
@@ -1,15 +1,15 @@
<script setup lang="ts">
import type { StyleValue } from 'vue';
import type { StyleValue } from "vue";
import type { PageProps } from './types';
import type { PageProps } from "./types";
import { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue';
import { computed, nextTick, onMounted, ref, useTemplateRef } from "vue";
import { CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT } from '/@/vben/shared/constants';
import { cn } from '/@/vben/shared/utils';
import { CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT } from "/@/vben/shared/constants";
import { cn } from "/@/vben/shared/utils";
defineOptions({
name: 'Page',
name: "Page",
});
const { autoContentHeight = false } = defineProps<PageProps>();
@@ -18,14 +18,14 @@ const headerHeight = ref(0);
const footerHeight = ref(0);
const shouldAutoHeight = ref(false);
const headerRef = useTemplateRef<HTMLDivElement>('headerRef');
const footerRef = useTemplateRef<HTMLDivElement>('footerRef');
const headerRef = useTemplateRef<HTMLDivElement>("headerRef");
const footerRef = useTemplateRef<HTMLDivElement>("footerRef");
const contentStyle = computed<StyleValue>(() => {
if (autoContentHeight) {
return {
height: `calc(var(${CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT}) - ${headerHeight.value}px)`,
overflowY: shouldAutoHeight.value ? 'auto' : 'unset',
overflowY: shouldAutoHeight.value ? "auto" : "unset",
};
}
return {};
@@ -50,22 +50,7 @@ onMounted(() => {
<template>
<div class="relative">
<div
v-if="
description ||
$slots.description ||
title ||
$slots.title ||
$slots.extra
"
ref="headerRef"
:class="
cn(
'bg-card border-border relative flex items-end border-b px-6 py-4',
headerClass,
)
"
>
<div v-if="description || $slots.description || title || $slots.title || $slots.extra" ref="headerRef" :class="cn('bg-card border-border relative flex items-end border-b px-6 py-4', headerClass)">
<div class="flex-auto">
<slot name="title">
<div v-if="title" class="mb-2 flex text-lg font-semibold">
@@ -89,16 +74,7 @@ onMounted(() => {
<slot></slot>
</div>
<div
v-if="$slots.footer"
ref="footerRef"
:class="
cn(
'bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4',
footerClass,
)
"
>
<div v-if="$slots.footer" ref="footerRef" :class="cn('bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4', footerClass)">
<slot name="footer"></slot>
</div>
</div>
@@ -1 +1 @@
export { default as VResize } from './resize.vue';
export { default as VResize } from "./resize.vue";
@@ -3,16 +3,7 @@
* This components is refactored from vue-drag-resize: https://github.com/kirillmurashov/vue-drag-resize
*/
import {
computed,
getCurrentInstance,
nextTick,
onBeforeUnmount,
onMounted,
ref,
toRefs,
watch,
} from 'vue';
import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, toRefs, watch } from "vue";
const props = defineProps({
stickSize: {
@@ -87,14 +78,14 @@ const props = defineProps({
type: [String, Number],
default: 200,
validator(val: number) {
return typeof val === 'string' ? val === 'auto' : val >= 0;
return typeof val === "string" ? val === "auto" : val >= 0;
},
},
h: {
type: [String, Number],
default: 200,
validator(val: number) {
return typeof val === 'string' ? val === 'auto' : val >= 0;
return typeof val === "string" ? val === "auto" : val >= 0;
},
},
minw: {
@@ -115,21 +106,21 @@ const props = defineProps({
type: Number,
default: 0,
validator(val: number) {
return typeof val === 'number';
return typeof val === "number";
},
},
y: {
type: Number,
default: 0,
validator(val: number) {
return typeof val === 'number';
return typeof val === "number";
},
},
z: {
type: [String, Number],
default: 'auto',
default: "auto",
validator(val: number) {
return typeof val === 'string' ? val === 'auto' : val >= 0;
return typeof val === "string" ? val === "auto" : val >= 0;
},
},
dragHandle: {
@@ -141,45 +132,37 @@ const props = defineProps({
default: null,
},
sticks: {
type: Array<'bl' | 'bm' | 'br' | 'ml' | 'mr' | 'tl' | 'tm' | 'tr'>,
type: Array<"bl" | "bm" | "br" | "ml" | "mr" | "tl" | "tm" | "tr">,
default() {
return ['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml'];
return ["tl", "tm", "tr", "mr", "br", "bm", "bl", "ml"];
},
},
axis: {
type: String,
default: 'both',
default: "both",
validator(val: string) {
return ['both', 'none', 'x', 'y'].includes(val);
return ["both", "none", "x", "y"].includes(val);
},
},
contentClass: {
type: String,
required: false,
default: '',
default: "",
},
});
const emit = defineEmits([
'clicked',
'dragging',
'dragstop',
'resizing',
'resizestop',
'activated',
'deactivated',
]);
const emit = defineEmits(["clicked", "dragging", "dragstop", "resizing", "resizestop", "activated", "deactivated"]);
const styleMapping = {
y: {
t: 'top',
m: 'marginTop',
b: 'bottom',
t: "top",
m: "marginTop",
b: "bottom",
},
x: {
l: 'left',
m: 'marginLeft',
r: 'right',
l: "left",
m: "marginLeft",
r: "right",
},
};
@@ -275,13 +258,7 @@ const rect = computed(() => ({
height: Math.round(height.value),
}));
const saveDimensionsBeforeMove = ({
pointerX,
pointerY,
}: {
pointerX: number;
pointerY: number;
}) => {
const saveDimensionsBeforeMove = ({ pointerX, pointerY }: { pointerX: number; pointerY: number }) => {
dimensionsBeforeMove.value.pointerX = pointerX;
dimensionsBeforeMove.value.pointerY = pointerY;
@@ -296,10 +273,7 @@ const saveDimensionsBeforeMove = ({
aspectFactor.value = width.value / height.value;
};
const sideCorrectionByLimit = (
limit: { max: number; min: number },
current: number,
) => {
const sideCorrectionByLimit = (limit: { max: number; min: number }, current: number) => {
let value = current;
if (limit.min !== null && current < limit.min) {
@@ -311,12 +285,7 @@ const sideCorrectionByLimit = (
return value;
};
const rectCorrectionByLimit = (rect: {
newBottom: number;
newLeft: number;
newRight: number;
newTop: number;
}) => {
const rectCorrectionByLimit = (rect: { newBottom: number; newLeft: number; newRight: number; newTop: number }) => {
// const { limits } = this;
let { newRight, newLeft, newBottom, newTop } = rect;
@@ -328,10 +297,7 @@ const rectCorrectionByLimit = (rect: {
newLeft = sideCorrectionByLimit(limits.value.left as RectRange, newLeft);
newRight = sideCorrectionByLimit(limits.value.right as RectRange, newRight);
newTop = sideCorrectionByLimit(limits.value.top as RectRange, newTop);
newBottom = sideCorrectionByLimit(
limits.value.bottom as RectRange,
newBottom,
);
newBottom = sideCorrectionByLimit(limits.value.bottom as RectRange, newBottom);
return {
newLeft,
@@ -341,24 +307,19 @@ const rectCorrectionByLimit = (rect: {
};
};
const rectCorrectionByAspectRatio = (rect: {
newBottom: number;
newLeft: number;
newRight: number;
newTop: number;
}) => {
const rectCorrectionByAspectRatio = (rect: { newBottom: number; newLeft: number; newRight: number; newTop: number }) => {
let { newLeft, newRight, newTop, newBottom } = rect;
// const { parentWidth, parentHeight, currentStick, aspectFactor, dimensionsBeforeMove } = this;
let newWidth = parentWidth.value! - newLeft - newRight;
let newHeight = parentHeight.value! - newTop - newBottom;
if (currentStick.value![1] === 'm') {
if (currentStick.value![1] === "m") {
const deltaHeight = newHeight - dimensionsBeforeMove.value.height;
newLeft -= (deltaHeight * aspectFactor.value!) / 2;
newRight -= (deltaHeight * aspectFactor.value!) / 2;
} else if (currentStick.value![0] === 'm') {
} else if (currentStick.value![0] === "m") {
const deltaWidth = newWidth - dimensionsBeforeMove.value.width;
newTop -= deltaWidth / aspectFactor.value! / 2;
@@ -366,7 +327,7 @@ const rectCorrectionByAspectRatio = (rect: {
} else if (newWidth / newHeight > aspectFactor.value!) {
newWidth = aspectFactor.value! * newHeight;
if (currentStick.value![1] === 'l') {
if (currentStick.value![1] === "l") {
newLeft = parentWidth.value! - newRight - newWidth;
} else {
newRight = parentWidth.value! - newLeft - newWidth;
@@ -374,7 +335,7 @@ const rectCorrectionByAspectRatio = (rect: {
} else {
newHeight = newWidth / aspectFactor.value!;
if (currentStick.value![0] === 't') {
if (currentStick.value![0] === "t") {
newTop = parentHeight.value! - newBottom - newHeight;
} else {
newBottom = parentHeight.value! - newTop - newHeight;
@@ -390,22 +351,17 @@ const stickMove = (delta: { x: number; y: number }) => {
let newLeft = dimensionsBeforeMove.value.left;
let newRight = dimensionsBeforeMove.value.right;
switch (currentStick.value![0]) {
case 'b': {
case "b": {
newBottom = dimensionsBeforeMove.value.bottom + delta.y;
if (snapToGrid.value) {
newBottom =
(parentHeight.value as number) -
Math.round(
((parentHeight.value as number) - newBottom) / gridY.value,
) *
gridY.value;
newBottom = (parentHeight.value as number) - Math.round(((parentHeight.value as number) - newBottom) / gridY.value) * gridY.value;
}
break;
}
case 't': {
case "t": {
newTop = dimensionsBeforeMove.value.top - delta.y;
if (snapToGrid.value) {
@@ -420,7 +376,7 @@ const stickMove = (delta: { x: number; y: number }) => {
}
switch (currentStick.value![1]) {
case 'l': {
case "l": {
newLeft = dimensionsBeforeMove.value.left - delta.x;
if (snapToGrid.value) {
@@ -430,14 +386,11 @@ const stickMove = (delta: { x: number; y: number }) => {
break;
}
case 'r': {
case "r": {
newRight = dimensionsBeforeMove.value.right + delta.x;
if (snapToGrid.value) {
newRight =
(parentWidth.value as number) -
Math.round(((parentWidth.value as number) - newRight) / gridX.value) *
gridX.value;
newRight = (parentWidth.value as number) - Math.round(((parentWidth.value as number) - newRight) / gridX.value) * gridX.value;
}
break;
@@ -468,7 +421,7 @@ const stickMove = (delta: { x: number; y: number }) => {
top.value = newTop;
bottom.value = newBottom;
emit('resizing', rect.value);
emit("resizing", rect.value);
};
const stickUp = () => {
@@ -498,8 +451,8 @@ const stickUp = () => {
bottom: { min: null, max: null },
};
emit('resizing', rect.value);
emit('resizestop', rect.value);
emit("resizing", rect.value);
emit("resizestop", rect.value);
};
const calcDragLimitation = () => {
@@ -546,40 +499,24 @@ const calcResizeLimits = () => {
if (aspectRatio.value) {
const aspectLimits = {
left: {
min:
left.value! -
Math.min(top.value!, bottom.value!) * aspectFactor.value! * 2,
max:
left.value! +
((height.value - minh.value!) / 2) * aspectFactor.value! * 2,
min: left.value! - Math.min(top.value!, bottom.value!) * aspectFactor.value! * 2,
max: left.value! + ((height.value - minh.value!) / 2) * aspectFactor.value! * 2,
},
right: {
min:
right.value! -
Math.min(top.value!, bottom.value!) * aspectFactor.value! * 2,
max:
right.value! +
((height.value - minh.value!) / 2) * aspectFactor.value! * 2,
min: right.value! - Math.min(top.value!, bottom.value!) * aspectFactor.value! * 2,
max: right.value! + ((height.value - minh.value!) / 2) * aspectFactor.value! * 2,
},
top: {
min:
top.value! -
(Math.min(left.value!, right.value!) / aspectFactor.value!) * 2,
max:
top.value! +
((width.value - minw.value) / 2 / aspectFactor.value!) * 2,
min: top.value! - (Math.min(left.value!, right.value!) / aspectFactor.value!) * 2,
max: top.value! + ((width.value - minw.value) / 2 / aspectFactor.value!) * 2,
},
bottom: {
min:
bottom.value! -
(Math.min(left.value!, right.value!) / aspectFactor.value!) * 2,
max:
bottom.value! +
((width.value - minw.value) / 2 / aspectFactor.value!) * 2,
min: bottom.value! - (Math.min(left.value!, right.value!) / aspectFactor.value!) * 2,
max: bottom.value! + ((width.value - minw.value) / 2 / aspectFactor.value!) * 2,
},
};
if (currentStick.value![0] === 'm') {
if (currentStick.value![0] === "m") {
limits.left = {
min: Math.max(limits.left.min!, aspectLimits.left.min),
max: Math.min(limits.left.max, aspectLimits.left.max),
@@ -588,7 +525,7 @@ const calcResizeLimits = () => {
min: Math.max(limits.right.min!, aspectLimits.right.min),
max: Math.min(limits.right.max, aspectLimits.right.max),
};
} else if (currentStick.value![1] === 'm') {
} else if (currentStick.value![1] === "m") {
limits.top = {
min: Math.max(limits.top.min!, aspectLimits.top.min),
max: Math.min(limits.top.max, aspectLimits.top.max),
@@ -610,8 +547,8 @@ const positionStyle = computed(() => ({
}));
const sizeStyle = computed(() => ({
width: w.value === 'auto' ? 'auto' : `${width.value}px`,
height: h.value === 'auto' ? 'auto' : `${height.value}px`,
width: w.value === "auto" ? "auto" : `${width.value}px`,
height: h.value === "auto" ? "auto" : `${height.value}px`,
}));
const stickStyles = computed(() => (stick: string) => {
@@ -619,12 +556,8 @@ const stickStyles = computed(() => (stick: string) => {
width: `${stickSize.value / parentScaleX.value}px`,
height: `${stickSize.value / parentScaleY.value}px`,
};
stickStyle[
styleMapping.y[stick[0] as 'b' | 'm' | 't'] as 'height' | 'width'
] = `${stickSize.value / parentScaleX.value / -2}px`;
stickStyle[
styleMapping.x[stick[1] as 'l' | 'm' | 'r'] as 'height' | 'width'
] = `${stickSize.value / parentScaleX.value / -2}px`;
stickStyle[styleMapping.y[stick[0] as "b" | "m" | "t"] as "height" | "width"] = `${stickSize.value / parentScaleX.value / -2}px`;
stickStyle[styleMapping.x[stick[1] as "l" | "m" | "r"] as "height" | "width"] = `${stickSize.value / parentScaleX.value / -2}px`;
return stickStyle;
});
@@ -639,17 +572,9 @@ const bodyMove = (delta: { x: number; y: number }) => {
let alignLeft = true;
let diffT = newTop - Math.floor(newTop / gridY.value) * gridY.value;
let diffB =
(parentHeight.value as number) -
newBottom -
Math.floor(((parentHeight.value as number) - newBottom) / gridY.value) *
gridY.value;
let diffB = (parentHeight.value as number) - newBottom - Math.floor(((parentHeight.value as number) - newBottom) / gridY.value) * gridY.value;
let diffL = newLeft - Math.floor(newLeft / gridX.value) * gridX.value;
let diffR =
(parentWidth.value as number) -
newRight -
Math.floor(((parentWidth.value as number) - newRight) / gridX.value) *
gridX.value;
let diffR = (parentWidth.value as number) - newRight - Math.floor(((parentWidth.value as number) - newRight) / gridX.value) * gridX.value;
if (diffT > gridY.value / 2) {
diffT -= gridY.value;
@@ -677,20 +602,15 @@ const bodyMove = (delta: { x: number; y: number }) => {
newRight = (parentWidth.value as number) - width.value - newLeft;
}
({
newLeft: left.value,
newRight: right.value,
newTop: top.value,
newBottom: bottom.value,
} = rectCorrectionByLimit({ newLeft, newRight, newTop, newBottom }));
({ newLeft: left.value, newRight: right.value, newTop: top.value, newBottom: bottom.value } = rectCorrectionByLimit({ newLeft, newRight, newTop, newBottom }));
emit('dragging', rect.value);
emit("dragging", rect.value);
};
const bodyUp = () => {
bodyDrag.value = false;
emit('dragging', rect.value);
emit('dragstop', rect.value);
emit("dragging", rect.value);
emit("dragstop", rect.value);
// dimensionsBeforeMove.value = { pointerX: 0, pointerY: 0, x: 0, y: 0, w: 0, h: 0 };
Object.assign(dimensionsBeforeMove.value, {
@@ -710,11 +630,7 @@ const bodyUp = () => {
};
};
const stickDown = (
stick: string,
ev: { pageX: any; pageY: any; touches?: any },
force = false,
) => {
const stickDown = (stick: string, ev: { pageX: any; pageY: any; touches?: any }, force = false) => {
if ((!isResizable.value || !active.value) && !force) {
return;
}
@@ -753,15 +669,15 @@ const move = (ev: MouseEvent & TouchEvent) => {
if (bodyDrag.value) {
switch (axis.value) {
case 'none': {
case "none": {
return;
}
case 'x': {
case "x": {
delta.y = 0;
break;
}
case 'y': {
case "y": {
delta.x = 0;
break;
@@ -789,15 +705,15 @@ const deselect = () => {
const domEvents = ref(
new Map([
['mousedown', deselect],
['mouseleave', up],
['mousemove', move],
['mouseup', up],
['touchcancel', up],
['touchend', up],
['touchmove', move],
['touchstart', up],
]),
["mousedown", deselect],
["mouseleave", up],
["mousemove", move],
["mouseup", up],
["touchcancel", up],
["touchend", up],
["touchmove", move],
["touchstart", up],
])
);
const container = ref<HTMLDivElement>();
@@ -812,33 +728,21 @@ onMounted(() => {
left.value = x.value;
top.value = y.value;
right.value = (parentWidth.value -
(w.value === 'auto' ? container.value!.scrollWidth : (w.value as number)) -
left.value) as number;
bottom.value = (parentHeight.value -
(h.value === 'auto' ? container.value!.scrollHeight : (h.value as number)) -
top.value) as number;
right.value = (parentWidth.value - (w.value === "auto" ? container.value!.scrollWidth : (w.value as number)) - left.value) as number;
bottom.value = (parentHeight.value - (h.value === "auto" ? container.value!.scrollHeight : (h.value as number)) - top.value) as number;
addEvents(domEvents.value);
if (dragHandle.value) {
[...($el?.querySelectorAll(dragHandle.value) || [])].forEach(
(dragHandle) => {
(dragHandle as HTMLElement).dataset.dragHandle = String(
currentInstance?.uid,
);
},
);
[...($el?.querySelectorAll(dragHandle.value) || [])].forEach(dragHandle => {
(dragHandle as HTMLElement).dataset.dragHandle = String(currentInstance?.uid);
});
}
if (dragCancel.value) {
[...($el?.querySelectorAll(dragCancel.value) || [])].forEach(
(cancelHandle) => {
(cancelHandle as HTMLElement).dataset.dragCancel = String(
currentInstance?.uid,
);
},
);
[...($el?.querySelectorAll(dragCancel.value) || [])].forEach(cancelHandle => {
(cancelHandle as HTMLElement).dataset.dragCancel = String(currentInstance?.uid);
});
}
});
@@ -857,25 +761,17 @@ const bodyDown = (ev: MouseEvent & TouchEvent) => {
return;
}
emit('clicked', ev);
emit("clicked", ev);
if (!active.value) {
return;
}
if (
dragHandle.value &&
(target! as HTMLElement).dataset.dragHandle !==
getCurrentInstance()?.uid.toString()
) {
if (dragHandle.value && (target! as HTMLElement).dataset.dragHandle !== getCurrentInstance()?.uid.toString()) {
return;
}
if (
dragCancel.value &&
(target! as HTMLElement).dataset.dragCancel ===
getCurrentInstance()?.uid.toString()
) {
if (dragCancel.value && (target! as HTMLElement).dataset.dragCancel === getCurrentInstance()?.uid.toString()) {
return;
}
@@ -903,31 +799,31 @@ const bodyDown = (ev: MouseEvent & TouchEvent) => {
watch(
() => active.value,
(isActive) => {
isActive => {
if (isActive) {
emit('activated');
emit("activated");
} else {
emit('deactivated');
emit("deactivated");
}
},
}
);
watch(
() => isActive.value,
(val) => {
val => {
active.value = val;
},
{ immediate: true },
{ immediate: true }
);
watch(
() => z.value,
(val) => {
if ((val as number) >= 0 || val === 'auto') {
val => {
if ((val as number) >= 0 || val === "auto") {
zIndex.value = val as number;
}
},
{ immediate: true },
{ immediate: true }
);
watch(
@@ -939,14 +835,13 @@ watch(
const delta = oldVal - newVal;
bodyDown({ pageX: left.value!, pageY: top.value! } as MouseEvent &
TouchEvent);
bodyDown({ pageX: left.value!, pageY: top.value! } as MouseEvent & TouchEvent);
bodyMove({ x: delta, y: 0 });
nextTick(() => {
bodyUp();
});
},
}
);
watch(
@@ -958,14 +853,13 @@ watch(
const delta = oldVal - newVal;
bodyDown({ pageX: left.value, pageY: top.value } as MouseEvent &
TouchEvent);
bodyDown({ pageX: left.value, pageY: top.value } as MouseEvent & TouchEvent);
bodyMove({ x: 0, y: delta });
nextTick(() => {
bodyUp();
});
},
}
);
watch(
@@ -975,20 +869,16 @@ watch(
return;
}
const stick = 'mr';
const stick = "mr";
const delta = (oldVal as number) - (newVal as number);
stickDown(
stick,
{ pageX: right.value, pageY: top.value! + height.value / 2 },
true,
);
stickDown(stick, { pageX: right.value, pageY: top.value! + height.value / 2 }, true);
stickMove({ x: delta, y: 0 });
nextTick(() => {
stickUp();
});
},
}
);
watch(
@@ -998,36 +888,32 @@ watch(
return;
}
const stick = 'bm';
const stick = "bm";
const delta = (oldVal as number) - (newVal as number);
stickDown(
stick,
{ pageX: left.value! + width.value / 2, pageY: bottom.value },
true,
);
stickDown(stick, { pageX: left.value! + width.value / 2, pageY: bottom.value }, true);
stickMove({ x: 0, y: delta });
nextTick(() => {
stickUp();
});
},
}
);
watch(
() => parentW.value,
(val) => {
val => {
right.value = val - width.value - left.value!;
parentWidth.value = val;
},
}
);
watch(
() => parentH.value,
(val) => {
val => {
bottom.value = val - height.value - top.value!;
parentHeight.value = val;
},
}
);
</script>
@@ -1049,12 +935,8 @@ watch(
:class="[`resize-stick-${stick}`, isResizable ? '' : 'not-resizable']"
:style="stickStyles(stick)"
class="resize-stick"
@mousedown.stop.prevent="
stickDown(stick, $event as TouchEvent & MouseEvent)
"
@touchstart.stop.prevent="
stickDown(stick, $event as TouchEvent & MouseEvent)
"
@mousedown.stop.prevent="stickDown(stick, $event as TouchEvent & MouseEvent)"
@touchstart.stop.prevent="stickDown(stick, $event as TouchEvent & MouseEvent)"
></div>
</div>
</template>
@@ -1072,7 +954,7 @@ watch(
box-sizing: border-box;
width: 100%;
height: 100%;
content: '';
content: "";
outline: 1px dashed #d6d6d6;
}
@@ -1,18 +1,15 @@
import type { ComputedRef, Directive } from 'vue';
import type { ComputedRef, Directive } from "vue";
import { useTippy } from 'vue-tippy';
import { useTippy } from "vue-tippy";
export default function useTippyDirective(isDark: ComputedRef<boolean>) {
const directive: Directive = {
mounted(el, binding, vnode) {
const opts =
typeof binding.value === 'string'
? { content: binding.value }
: binding.value || {};
const opts = typeof binding.value === "string" ? { content: binding.value } : binding.value || {};
const modifiers = Object.keys(binding.modifiers || {});
const placement = modifiers.find((modifier) => modifier !== 'arrow');
const withArrow = modifiers.includes('arrow');
const placement = modifiers.find(modifier => modifier !== "arrow");
const withArrow = modifiers.includes("arrow");
if (placement) {
opts.placement = opts.placement || placement;
@@ -52,13 +49,13 @@ export default function useTippyDirective(isDark: ComputedRef<boolean>) {
};
}
if (el.getAttribute('title') && !opts.content) {
opts.content = el.getAttribute('title');
el.removeAttribute('title');
if (el.getAttribute("title") && !opts.content) {
opts.content = el.getAttribute("title");
el.removeAttribute("title");
}
if (el.getAttribute('content') && !opts.content) {
opts.content = el.getAttribute('content');
if (el.getAttribute("content") && !opts.content) {
opts.content = el.getAttribute("content");
}
useTippy(el, opts);
@@ -72,21 +69,15 @@ export default function useTippyDirective(isDark: ComputedRef<boolean>) {
},
updated(el, binding) {
const opts =
typeof binding.value === 'string'
? { content: binding.value, theme: isDark.value ? '' : 'light' }
: Object.assign(
{ theme: isDark.value ? '' : 'light' },
binding.value,
);
const opts = typeof binding.value === "string" ? { content: binding.value, theme: isDark.value ? "" : "light" } : Object.assign({ theme: isDark.value ? "" : "light" }, binding.value);
if (el.getAttribute('title') && !opts.content) {
opts.content = el.getAttribute('title');
el.removeAttribute('title');
if (el.getAttribute("title") && !opts.content) {
opts.content = el.getAttribute("title");
el.removeAttribute("title");
}
if (el.getAttribute('content') && !opts.content) {
opts.content = el.getAttribute('content');
if (el.getAttribute("content") && !opts.content) {
opts.content = el.getAttribute("content");
}
if (el.$tippy) {
@@ -1,33 +1,27 @@
import type { DefaultProps, Props } from 'tippy.js';
import type { DefaultProps, Props } from "tippy.js";
import type { App, SetupContext } from 'vue';
import type { App, SetupContext } from "vue";
import { h, watchEffect } from 'vue';
import { setDefaultProps, Tippy as TippyComponent } from 'vue-tippy';
import { h, watchEffect } from "vue";
import { setDefaultProps, Tippy as TippyComponent } from "vue-tippy";
import { usePreferences } from '/@/vben/preferences';
import { usePreferences } from "/@/vben/preferences";
import useTippyDirective from './directive';
import useTippyDirective from "./directive";
import 'tippy.js/dist/tippy.css';
import 'tippy.js/dist/backdrop.css';
import 'tippy.js/themes/light.css';
import 'tippy.js/animations/scale.css';
import 'tippy.js/animations/shift-toward.css';
import 'tippy.js/animations/shift-away.css';
import 'tippy.js/animations/perspective.css';
import "tippy.js/dist/tippy.css";
import "tippy.js/dist/backdrop.css";
import "tippy.js/themes/light.css";
import "tippy.js/animations/scale.css";
import "tippy.js/animations/shift-toward.css";
import "tippy.js/animations/shift-away.css";
import "tippy.js/animations/perspective.css";
const { isDark } = usePreferences();
export type TippyProps = Partial<
Props & {
animation?:
| 'fade'
| 'perspective'
| 'scale'
| 'shift-away'
| 'shift-toward'
| boolean;
theme?: 'auto' | 'dark' | 'light';
animation?: "fade" | "perspective" | "scale" | "shift-away" | "shift-toward" | boolean;
theme?: "auto" | "dark" | "light";
}
>;
@@ -35,25 +29,25 @@ export function initTippy(app: App<Element>, options?: DefaultProps) {
setDefaultProps({
allowHTML: true,
delay: [500, 200],
theme: isDark.value ? '' : 'light',
theme: isDark.value ? "" : "light",
...options,
});
if (!options || !Reflect.has(options, 'theme') || options.theme === 'auto') {
if (!options || !Reflect.has(options, "theme") || options.theme === "auto") {
watchEffect(() => {
setDefaultProps({ theme: isDark.value ? '' : 'light' });
setDefaultProps({ theme: isDark.value ? "" : "light" });
});
}
app.directive('tippy', useTippyDirective(isDark));
app.directive("tippy", useTippyDirective(isDark));
}
export const Tippy = (props: any, { attrs, slots }: SetupContext) => {
let theme: string = (attrs.theme as string) ?? 'auto';
if (theme === 'auto') {
theme = isDark.value ? '' : 'light';
let theme: string = (attrs.theme as string) ?? "auto";
if (theme === "auto") {
theme = isDark.value ? "" : "light";
}
if (theme === 'dark') {
theme = '';
if (theme === "dark") {
theme = "";
}
return h(
TippyComponent,
@@ -62,6 +56,6 @@ export const Tippy = (props: any, { attrs, slots }: SetupContext) => {
...attrs,
theme,
},
slots,
slots
);
};
@@ -1,2 +1,2 @@
export * from './components';
export * from './ui';
export * from "./components";
export * from "./ui";
@@ -1,4 +1,4 @@
import type { Component } from 'vue';
import type { Component } from "vue";
interface AboutProps {
description?: string;
@@ -1,29 +1,25 @@
<script setup lang="ts">
import type { AboutProps, DescriptionItem } from './about';
import type { AboutProps, DescriptionItem } from "./about";
import { h } from 'vue';
import { h } from "vue";
import {
VBEN_DOC_URL,
VBEN_GITHUB_URL,
VBEN_PREVIEW_URL,
} from '/@/vben/constants';
import { VBEN_DOC_URL, VBEN_GITHUB_URL, VBEN_PREVIEW_URL } from "/@/vben/constants";
import { VbenRenderContent } from '/@/vben/shadcn-ui';
import { VbenRenderContent } from "/@/vben/shadcn-ui";
import { Page } from '../../components';
import { Page } from "../../components";
interface Props extends AboutProps {}
defineOptions({
name: 'AboutUI',
name: "AboutUI",
});
withDefaults(defineProps<Props>(), {
description:
'是一个现代化开箱即用的中后台解决方案,采用最新的技术栈,包括 Vue 3.0、Vite、TailwindCSS 和 TypeScript 等前沿技术,代码规范严谨,提供丰富的配置选项,旨在为中大型项目的开发提供现成的开箱即用解决方案及丰富的示例,同时,它也是学习和深入前端技术的一个极佳示例。',
name: 'Vben Admin',
title: '关于项目',
"是一个现代化开箱即用的中后台解决方案,采用最新的技术栈,包括 Vue 3.0、Vite、TailwindCSS 和 TypeScript 等前沿技术,代码规范严谨,提供丰富的配置选项,旨在为中大型项目的开发提供现成的开箱即用解决方案及丰富的示例,同时,它也是学习和深入前端技术的一个极佳示例。",
name: "Vben Admin",
title: "关于项目",
});
declare global {
@@ -42,12 +38,7 @@ declare global {
};
}
const renderLink = (href: string, text: string) =>
h(
'a',
{ href, target: '_blank', class: 'vben-link' },
{ default: () => text },
);
const renderLink = (href: string, text: string) => h("a", { href, target: "_blank", class: "vben-link" }, { default: () => text });
const {
authorEmail,
@@ -65,47 +56,44 @@ const {
const vbenDescriptionItems: DescriptionItem[] = [
{
content: version,
title: '版本号',
title: "版本号",
},
{
content: license,
title: '开源许可协议',
title: "开源许可协议",
},
{
content: buildTime,
title: '最后构建时间',
title: "最后构建时间",
},
{
content: renderLink(homepage, '点击查看'),
title: '主页',
content: renderLink(homepage, "点击查看"),
title: "主页",
},
{
content: renderLink(VBEN_DOC_URL, '点击查看'),
title: '文档地址',
content: renderLink(VBEN_DOC_URL, "点击查看"),
title: "文档地址",
},
{
content: renderLink(VBEN_PREVIEW_URL, '点击查看'),
title: '预览地址',
content: renderLink(VBEN_PREVIEW_URL, "点击查看"),
title: "预览地址",
},
{
content: renderLink(VBEN_GITHUB_URL, '点击查看'),
title: 'Github',
content: renderLink(VBEN_GITHUB_URL, "点击查看"),
title: "Github",
},
{
content: h('div', [
renderLink(authorUrl, `${authorName} `),
renderLink(`mailto:${authorEmail}`, authorEmail),
]),
title: '作者',
content: h("div", [renderLink(authorUrl, `${authorName} `), renderLink(`mailto:${authorEmail}`, authorEmail)]),
title: "作者",
},
];
const dependenciesItems = Object.keys(dependencies).map((key) => ({
const dependenciesItems = Object.keys(dependencies).map(key => ({
content: dependencies[key],
title: key,
}));
const devDependenciesItems = Object.keys(devDependencies).map((key) => ({
const devDependenciesItems = Object.keys(devDependencies).map(key => ({
content: devDependencies[key],
title: key,
}));
@@ -1 +1 @@
export { default as About } from './about.vue';
export { default as About } from "./about.vue";
@@ -1,8 +1,6 @@
<template>
<div class="mb-7 sm:mx-auto sm:w-full sm:max-w-md">
<h2
class="text-foreground mb-3 text-3xl font-bold leading-9 tracking-tight lg:text-4xl"
>
<h2 class="text-foreground mb-3 text-3xl font-bold leading-9 tracking-tight lg:text-4xl">
<slot></slot>
</h2>
@@ -1,17 +1,17 @@
<script setup lang="ts">
import type { Recordable } from '/@/vben/types';
import type { Recordable } from "/@/vben/types";
import type { VbenFormSchema } from '/@/vben/form-ui';
import type { VbenFormSchema } from "/@/vben/form-ui";
import { computed, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { computed, reactive } from "vue";
import { useRouter } from "vue-router";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import { useVbenForm } from '/@/vben/form-ui';
import { VbenButton } from '/@/vben/shadcn-ui';
import { useVbenForm } from "/@/vben/form-ui";
import { VbenButton } from "/@/vben/shadcn-ui";
import Title from './auth-title.vue';
import Title from "./auth-title.vue";
interface Props {
formSchema: VbenFormSchema[];
@@ -38,15 +38,15 @@ interface Props {
}
defineOptions({
name: 'AuthenticationCodeLogin',
name: "AuthenticationCodeLogin",
});
const props = withDefaults(defineProps<Props>(), {
loading: false,
loginPath: '/auth/login',
submitButtonText: '',
subTitle: '',
title: '',
loginPath: "/auth/login",
submitButtonText: "",
subTitle: "",
title: "",
});
const emit = defineEmits<{
@@ -63,14 +63,14 @@ const [Form, formApi] = useVbenForm(
},
schema: computed(() => props.formSchema),
showDefaultActions: false,
}),
})
);
async function handleSubmit() {
const { valid } = await formApi.validate();
const values = await formApi.getValues();
if (valid) {
emit('submit', values);
emit("submit", values);
}
}
@@ -86,13 +86,11 @@ defineExpose({
<template>
<div>
<Title>
<slot name="title">
{{ title || $t('authentication.welcomeBack') }} 📲
</slot>
<slot name="title"> {{ title || $t("authentication.welcomeBack") }} 📲 </slot>
<template #desc>
<span class="text-muted-foreground">
<slot name="subTitle">
{{ subTitle || $t('authentication.codeSubtitle') }}
{{ subTitle || $t("authentication.codeSubtitle") }}
</slot>
</span>
</template>
@@ -107,11 +105,11 @@ defineExpose({
@click="handleSubmit"
>
<slot name="submitButtonText">
{{ submitButtonText || $t('common.login') }}
{{ submitButtonText || $t("common.login") }}
</slot>
</VbenButton>
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
{{ $t('common.back') }}
{{ $t("common.back") }}
</VbenButton>
</div>
</template>
@@ -1,7 +1,7 @@
export { default as AuthenticationCodeLogin } from './code-login.vue';
export { default as AuthenticationForgetPassword } from './forget-password.vue';
export { default as AuthenticationLoginExpiredModal } from './login-expired-modal.vue';
export { default as AuthenticationLogin } from './login.vue';
export { default as AuthenticationQrCodeLogin } from './qrcode-login.vue';
export { default as AuthenticationRegister } from './register.vue';
export type { AuthenticationProps } from './types';
export { default as AuthenticationCodeLogin } from "./code-login.vue";
export { default as AuthenticationForgetPassword } from "./forget-password.vue";
export { default as AuthenticationLoginExpiredModal } from "./login-expired-modal.vue";
export { default as AuthenticationLogin } from "./login.vue";
export { default as AuthenticationQrCodeLogin } from "./qrcode-login.vue";
export { default as AuthenticationRegister } from "./register.vue";
export type { AuthenticationProps } from "./types";
@@ -1,10 +1,10 @@
<script setup lang="ts">
import type { AuthenticationProps } from './types';
import type { AuthenticationProps } from "./types";
import { computed, watch } from 'vue';
import { computed, watch } from "vue";
import { useVbenModal } from '/@/vben/popup-ui';
import { Slot, VbenAvatar } from '/@/vben/shadcn-ui';
import { useVbenModal } from "/@/vben/popup-ui";
import { Slot, VbenAvatar } from "/@/vben/shadcn-ui";
interface Props extends AuthenticationProps {
avatar?: string;
@@ -12,23 +12,23 @@ interface Props extends AuthenticationProps {
}
defineOptions({
name: 'LoginExpiredModal',
name: "LoginExpiredModal",
});
const props = withDefaults(defineProps<Props>(), {
avatar: '',
avatar: "",
zIndex: 0,
});
const open = defineModel<boolean>('open');
const open = defineModel<boolean>("open");
const [Modal, modalApi] = useVbenModal();
watch(
() => open.value,
(val) => {
val => {
modalApi.setState({ isOpen: val });
},
}
);
const getZIndex = computed(() => {
@@ -40,10 +40,10 @@ const getZIndex = computed(() => {
*/
function calcZIndex() {
let maxZ = 0;
const elements = document.querySelectorAll('*');
[...elements].forEach((element) => {
const elements = document.querySelectorAll("*");
[...elements].forEach(element => {
const style = window.getComputedStyle(element);
const zIndex = style.getPropertyValue('z-index');
const zIndex = style.getPropertyValue("z-index");
if (zIndex && !Number.isNaN(Number.parseInt(zIndex))) {
maxZ = Math.max(maxZ, Number.parseInt(zIndex));
}
@@ -65,13 +65,7 @@ function calcZIndex() {
class="border-none px-10 py-6 text-center shadow-xl sm:w-[600px] sm:rounded-2xl md:h-[unset]"
>
<VbenAvatar :src="avatar" class="mx-auto mb-6 size-20" />
<Slot
:show-forget-password="false"
:show-register="false"
:show-remember-me="false"
:sub-title="$t('authentication.loginAgainSubTitle')"
:title="$t('authentication.loginAgainTitle')"
>
<Slot :show-forget-password="false" :show-register="false" :show-remember-me="false" :sub-title="$t('authentication.loginAgainSubTitle')" :title="$t('authentication.loginAgainTitle')">
<slot> </slot>
</Slot>
</Modal>
@@ -1,45 +1,45 @@
<script setup lang="ts">
import type { Recordable } from '/@/vben/types';
import type { Recordable } from "/@/vben/types";
import type { VbenFormSchema } from '/@/vben/form-ui';
import type { VbenFormSchema } from "/@/vben/form-ui";
import type { AuthenticationProps } from './types';
import type { AuthenticationProps } from "./types";
import { computed, onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { computed, onMounted, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import { useVbenForm } from '/@/vben/form-ui';
import { VbenButton, VbenCheckbox } from '/@/vben/shadcn-ui';
import { useVbenForm } from "/@/vben/form-ui";
import { VbenButton, VbenCheckbox } from "/@/vben/shadcn-ui";
import Title from './auth-title.vue';
import ThirdPartyLogin from './third-party-login.vue';
import Title from "./auth-title.vue";
import ThirdPartyLogin from "./third-party-login.vue";
interface Props extends AuthenticationProps {
formSchema: VbenFormSchema[];
}
defineOptions({
name: 'AuthenticationLogin',
name: "AuthenticationLogin",
});
const props = withDefaults(defineProps<Props>(), {
codeLoginPath: '/auth/code-login',
forgetPasswordPath: '/auth/forget-password',
codeLoginPath: "/auth/code-login",
forgetPasswordPath: "/auth/forget-password",
formSchema: () => [],
loading: false,
qrCodeLoginPath: '/auth/qrcode-login',
registerPath: '/auth/register',
qrCodeLoginPath: "/auth/qrcode-login",
registerPath: "/auth/register",
showCodeLogin: true,
showForgetPassword: true,
showQrcodeLogin: true,
showRegister: true,
showRememberMe: true,
showThirdPartyLogin: true,
submitButtonText: '',
subTitle: '',
title: '',
submitButtonText: "",
subTitle: "",
title: "",
});
const emit = defineEmits<{
@@ -54,13 +54,13 @@ const [Form, formApi] = useVbenForm(
},
schema: computed(() => props.formSchema),
showDefaultActions: false,
}),
})
);
const router = useRouter();
const REMEMBER_ME_KEY = `REMEMBER_ME_USERNAME_${location.hostname}`;
const localUsername = localStorage.getItem(REMEMBER_ME_KEY) || '';
const localUsername = localStorage.getItem(REMEMBER_ME_KEY) || "";
const rememberMe = ref(!!localUsername);
@@ -68,11 +68,8 @@ async function handleSubmit() {
const { valid } = await formApi.validate();
const values = await formApi.getValues();
if (valid) {
localStorage.setItem(
REMEMBER_ME_KEY,
rememberMe.value ? values?.username : '',
);
emit('submit', values);
localStorage.setItem(REMEMBER_ME_KEY, rememberMe.value ? values?.username : "");
emit("submit", values);
}
}
@@ -82,7 +79,7 @@ function handleGo(path: string) {
onMounted(() => {
if (localUsername) {
formApi.setFieldValue('username', localUsername);
formApi.setFieldValue("username", localUsername);
}
});
@@ -96,12 +93,12 @@ defineExpose({
<slot name="title">
<Title>
<slot name="title">
{{ title || `${$t('authentication.welcomeBack')} 👋🏻` }}
{{ title || `${$t("authentication.welcomeBack")} 👋🏻` }}
</slot>
<template #desc>
<span class="text-muted-foreground">
<slot name="subTitle">
{{ subTitle || $t('authentication.loginSubtitle') }}
{{ subTitle || $t("authentication.loginSubtitle") }}
</slot>
</span>
</template>
@@ -110,26 +107,15 @@ defineExpose({
<Form />
<div
v-if="showRememberMe || showForgetPassword"
class="mb-6 flex justify-between"
>
<div v-if="showRememberMe || showForgetPassword" class="mb-6 flex justify-between">
<div class="flex-center">
<VbenCheckbox
v-if="showRememberMe"
v-model:checked="rememberMe"
name="rememberMe"
>
{{ $t('authentication.rememberMe') }}
<VbenCheckbox v-if="showRememberMe" v-model:checked="rememberMe" name="rememberMe">
{{ $t("authentication.rememberMe") }}
</VbenCheckbox>
</div>
<span
v-if="showForgetPassword"
class="vben-link text-sm font-normal"
@click="handleGo(forgetPasswordPath)"
>
{{ $t('authentication.forgetPassword') }}
<span v-if="showForgetPassword" class="vben-link text-sm font-normal" @click="handleGo(forgetPasswordPath)">
{{ $t("authentication.forgetPassword") }}
</span>
</div>
<VbenButton
@@ -141,28 +127,15 @@ defineExpose({
class="w-full"
@click="handleSubmit"
>
{{ submitButtonText || $t('common.login') }}
{{ submitButtonText || $t("common.login") }}
</VbenButton>
<div
v-if="showCodeLogin || showQrcodeLogin"
class="mb-2 mt-4 flex items-center justify-between"
>
<VbenButton
v-if="showCodeLogin"
class="w-1/2"
variant="outline"
@click="handleGo(codeLoginPath)"
>
{{ $t('authentication.mobileLogin') }}
<div v-if="showCodeLogin || showQrcodeLogin" class="mb-2 mt-4 flex items-center justify-between">
<VbenButton v-if="showCodeLogin" class="w-1/2" variant="outline" @click="handleGo(codeLoginPath)">
{{ $t("authentication.mobileLogin") }}
</VbenButton>
<VbenButton
v-if="showQrcodeLogin"
class="ml-4 w-1/2"
variant="outline"
@click="handleGo(qrCodeLoginPath)"
>
{{ $t('authentication.qrcodeLogin') }}
<VbenButton v-if="showQrcodeLogin" class="ml-4 w-1/2" variant="outline" @click="handleGo(qrCodeLoginPath)">
{{ $t("authentication.qrcodeLogin") }}
</VbenButton>
</div>
@@ -173,12 +146,9 @@ defineExpose({
<slot name="to-register">
<div v-if="showRegister" class="mt-3 text-center text-sm">
{{ $t('authentication.accountTip') }}
<span
class="vben-link text-sm font-normal"
@click="handleGo(registerPath)"
>
{{ $t('authentication.createAccount') }}
{{ $t("authentication.accountTip") }}
<span class="vben-link text-sm font-normal" @click="handleGo(registerPath)">
{{ $t("authentication.createAccount") }}
</span>
</div>
</slot>
@@ -1,14 +1,14 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { ref } from "vue";
import { useRouter } from "vue-router";
import { $t } from '/@/vben/locales';
import { $t } from "/@/vben/locales";
import { VbenButton } from '/@/vben/shadcn-ui';
import { VbenButton } from "/@/vben/shadcn-ui";
import { useQRCode } from '@vueuse/integrations/useQRCode';
import { useQRCode } from "@vueuse/integrations/useQRCode";
import Title from './auth-title.vue';
import Title from "./auth-title.vue";
interface Props {
/**
@@ -38,24 +38,24 @@ interface Props {
}
defineOptions({
name: 'AuthenticationQrCodeLogin',
name: "AuthenticationQrCodeLogin",
});
const props = withDefaults(defineProps<Props>(), {
description: '',
description: "",
loading: false,
loginPath: '/auth/login',
submitButtonText: '',
subTitle: '',
title: '',
loginPath: "/auth/login",
submitButtonText: "",
subTitle: "",
title: "",
});
const router = useRouter();
const text = ref('https://vben.vvbin.cn');
const text = ref("https://vben.vvbin.cn");
const qrcode = useQRCode(text, {
errorCorrectionLevel: 'H',
errorCorrectionLevel: "H",
margin: 4,
});
@@ -67,13 +67,11 @@ function goToLogin() {
<template>
<div>
<Title>
<slot name="title">
{{ title || $t('authentication.welcomeBack') }} 📱
</slot>
<slot name="title"> {{ title || $t("authentication.welcomeBack") }} 📱 </slot>
<template #desc>
<span class="text-muted-foreground">
<slot name="subTitle">
{{ subTitle || $t('authentication.qrcodeSubtitle') }}
{{ subTitle || $t("authentication.qrcodeSubtitle") }}
</slot>
</span>
</template>
@@ -83,13 +81,13 @@ function goToLogin() {
<img :src="qrcode" alt="qrcode" class="w-1/2" />
<p class="text-muted-foreground mt-4 text-sm">
<slot name="description">
{{ description || $t('authentication.qrcodePrompt') }}
{{ description || $t("authentication.qrcodePrompt") }}
</slot>
</p>
</div>
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
{{ $t('common.back') }}
{{ $t("common.back") }}
</VbenButton>
</div>
</template>
@@ -1,17 +1,17 @@
<script setup lang="ts">
import type { Recordable } from '/@/vben/types';
import type { Recordable } from "/@/vben/types";
import type { VbenFormSchema } from '/@/vben/form-ui';
import type { VbenFormSchema } from "/@/vben/form-ui";
import { computed, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { computed, reactive } from "vue";
import { useRouter } from "vue-router";
import { $t } from '/@/locales';
import { $t } from "/@/locales";
import { useVbenForm } from '/@/vben/form-ui';
import { VbenButton } from '/@/vben/shadcn-ui';
import { useVbenForm } from "/@/vben/form-ui";
import { VbenButton } from "/@/vben/shadcn-ui";
import Title from './auth-title.vue';
import Title from "./auth-title.vue";
interface Props {
formSchema: VbenFormSchema[];
@@ -38,16 +38,16 @@ interface Props {
}
defineOptions({
name: 'RegisterForm',
name: "RegisterForm",
});
const props = withDefaults(defineProps<Props>(), {
formSchema: () => [],
loading: false,
loginPath: '/auth/login',
submitButtonText: '',
subTitle: '',
title: '',
loginPath: "/auth/login",
submitButtonText: "",
subTitle: "",
title: "",
});
const emit = defineEmits<{
@@ -62,7 +62,7 @@ const [Form, formApi] = useVbenForm(
},
schema: computed(() => props.formSchema),
showDefaultActions: false,
}),
})
);
const router = useRouter();
@@ -71,7 +71,7 @@ async function handleSubmit() {
const { valid } = await formApi.validate();
const values = await formApi.getValues();
if (valid) {
emit('submit', values as { password: string; username: string });
emit("submit", values as { password: string; username: string });
}
}
@@ -87,12 +87,10 @@ defineExpose({
<template>
<div>
<Title>
<slot name="title">
{{ title || $t('authentication.createAnAccount') }} 🚀
</slot>
<slot name="title"> {{ title || $t("authentication.createAnAccount") }} 🚀 </slot>
<template #desc>
<slot name="subTitle">
{{ subTitle || $t('authentication.signUpSubtitle') }}
{{ subTitle || $t("authentication.signUpSubtitle") }}
</slot>
</template>
</Title>
@@ -108,13 +106,13 @@ defineExpose({
@click="handleSubmit"
>
<slot name="submitButtonText">
{{ submitButtonText || $t('authentication.signUp') }}
{{ submitButtonText || $t("authentication.signUp") }}
</slot>
</VbenButton>
<div class="mt-4 text-center text-sm">
{{ $t('authentication.alreadyHaveAccount') }}
{{ $t("authentication.alreadyHaveAccount") }}
<span class="vben-link text-sm font-normal" @click="goToLogin()">
{{ $t('authentication.goToLogin') }}
{{ $t("authentication.goToLogin") }}
</span>
</div>
</div>
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { MdiGithub, MdiGoogle, MdiQqchat, MdiWechat } from '/@/vben/icons';
import { $t } from '/@/locales';
import { MdiGithub, MdiGoogle, MdiQqchat, MdiWechat } from "/@/vben/icons";
import { $t } from "/@/locales";
import { VbenIconButton } from '/@/vben/shadcn-ui';
import { VbenIconButton } from "/@/vben/shadcn-ui";
defineOptions({
name: 'ThirdPartyLogin',
name: "ThirdPartyLogin",
});
</script>
@@ -14,7 +14,7 @@ defineOptions({
<div class="mt-4 flex items-center justify-between">
<span class="border-input w-[35%] border-b dark:border-gray-600"></span>
<span class="text-muted-foreground text-center text-xs uppercase">
{{ $t('authentication.thirdPartyLogin') }}
{{ $t("authentication.thirdPartyLogin") }}
</span>
<span class="border-input w-[35%] border-b dark:border-gray-600"></span>
</div>
@@ -1,12 +1,12 @@
<script setup lang="ts">
import { Card, CardContent, CardHeader, CardTitle } from '/@/vben/shadcn-ui';
import { Card, CardContent, CardHeader, CardTitle } from "/@/vben/shadcn-ui";
interface Props {
title: string;
}
defineOptions({
name: 'AnalysisChartCard',
name: "AnalysisChartCard",
});
withDefaults(defineProps<Props>(), {});
@@ -1,16 +1,16 @@
<script setup lang="ts">
import type { TabOption } from '/@/vben/types';
import type { TabOption } from "/@/vben/types";
import { computed } from 'vue';
import { computed } from "vue";
import { Tabs, TabsContent, TabsList, TabsTrigger } from '/@/vben/shadcn-ui';
import { Tabs, TabsContent, TabsList, TabsTrigger } from "/@/vben/shadcn-ui";
interface Props {
tabs: TabOption[];
}
defineOptions({
name: 'AnalysisChartsTabs',
name: "AnalysisChartsTabs",
});
const props = withDefaults(defineProps<Props>(), {
@@ -1,22 +1,14 @@
<script setup lang="ts">
import type { AnalysisOverviewItem } from '../typing';
import type { AnalysisOverviewItem } from "../typing";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
VbenCountToAnimator,
VbenIcon,
} from '/@/vben/shadcn-ui';
import { Card, CardContent, CardFooter, CardHeader, CardTitle, VbenCountToAnimator, VbenIcon } from "/@/vben/shadcn-ui";
interface Props {
items: AnalysisOverviewItem[];
}
defineOptions({
name: 'AnalysisOverview',
name: "AnalysisOverview",
});
withDefaults(defineProps<Props>(), {
@@ -33,21 +25,12 @@ withDefaults(defineProps<Props>(), {
</CardHeader>
<CardContent class="flex items-center justify-between">
<VbenCountToAnimator
:end-val="item.value"
:start-val="1"
class="text-xl"
prefix=""
/>
<VbenCountToAnimator :end-val="item.value" :start-val="1" class="text-xl" prefix="" />
<VbenIcon :icon="item.icon" class="size-8 flex-shrink-0" />
</CardContent>
<CardFooter class="justify-between">
<span>{{ item.totalTitle }}</span>
<VbenCountToAnimator
:end-val="item.totalValue"
:start-val="1"
prefix=""
/>
<VbenCountToAnimator :end-val="item.totalValue" :start-val="1" prefix="" />
</CardFooter>
</Card>
</template>
@@ -1,3 +1,3 @@
export { default as AnalysisChartCard } from './analysis-chart-card.vue';
export { default as AnalysisChartsTabs } from './analysis-charts-tabs.vue';
export { default as AnalysisOverview } from './analysis-overview.vue';
export { default as AnalysisChartCard } from "./analysis-chart-card.vue";
export { default as AnalysisChartsTabs } from "./analysis-charts-tabs.vue";
export { default as AnalysisOverview } from "./analysis-overview.vue";
@@ -1,3 +1,3 @@
export * from './analysis';
export type * from './typing';
export * from './workbench';
export * from "./analysis";
export type * from "./typing";
export * from "./workbench";
@@ -1,4 +1,4 @@
import type { Component } from 'vue';
import type { Component } from "vue";
interface AnalysisOverviewItem {
icon: Component | string;
@@ -39,10 +39,4 @@ interface WorkbenchQuickNavItem {
url?: string;
}
export type {
AnalysisOverviewItem,
WorkbenchProjectItem,
WorkbenchQuickNavItem,
WorkbenchTodoItem,
WorkbenchTrendItem,
};
export type { AnalysisOverviewItem, WorkbenchProjectItem, WorkbenchQuickNavItem, WorkbenchTodoItem, WorkbenchTrendItem };
@@ -1,5 +1,5 @@
export { default as WorkbenchHeader } from './workbench-header.vue';
export { default as WorkbenchProject } from './workbench-project.vue';
export { default as WorkbenchQuickNav } from './workbench-quick-nav.vue';
export { default as WorkbenchTodo } from './workbench-todo.vue';
export { default as WorkbenchTrends } from './workbench-trends.vue';
export { default as WorkbenchHeader } from "./workbench-header.vue";
export { default as WorkbenchProject } from "./workbench-project.vue";
export { default as WorkbenchQuickNav } from "./workbench-quick-nav.vue";
export { default as WorkbenchTodo } from "./workbench-todo.vue";
export { default as WorkbenchTrends } from "./workbench-trends.vue";
@@ -1,25 +1,22 @@
<script lang="ts" setup>
import { VbenAvatar } from '/@/vben/shadcn-ui';
import { VbenAvatar } from "/@/vben/shadcn-ui";
interface Props {
avatar?: string;
}
defineOptions({
name: 'WorkbenchHeader',
name: "WorkbenchHeader",
});
withDefaults(defineProps<Props>(), {
avatar: '',
avatar: "",
});
</script>
<template>
<div class="card-box p-4 py-6 lg:flex">
<VbenAvatar :src="avatar" class="size-20" />
<div
v-if="$slots.title || $slots.description"
class="flex flex-col justify-center md:ml-6 md:mt-0"
>
<div v-if="$slots.title || $slots.description" class="flex flex-col justify-center md:ml-6 md:mt-0">
<h1 v-if="$slots.title" class="text-md font-semibold md:text-xl">
<slot name="title"></slot>
</h1>
@@ -1,13 +1,7 @@
<script setup lang="ts">
import type { WorkbenchProjectItem } from '../typing';
import type { WorkbenchProjectItem } from "../typing";
import {
Card,
CardContent,
CardHeader,
CardTitle,
VbenIcon,
} from '/@/vben/shadcn-ui';
import { Card, CardContent, CardHeader, CardTitle, VbenIcon } from "/@/vben/shadcn-ui";
interface Props {
items: WorkbenchProjectItem[];
@@ -15,14 +9,14 @@ interface Props {
}
defineOptions({
name: 'WorkbenchProject',
name: "WorkbenchProject",
});
withDefaults(defineProps<Props>(), {
items: () => [],
});
defineEmits(['click']);
defineEmits(["click"]);
</script>
<template>
@@ -41,12 +35,7 @@ defineEmits(['click']);
class="border-border group w-full cursor-pointer border-r border-t p-4 transition-all hover:shadow-xl md:w-1/2 lg:w-1/3"
>
<div class="flex items-center">
<VbenIcon
:color="item.color"
:icon="item.icon"
class="size-8 transition-all duration-300 group-hover:scale-110"
@click="$emit('click', item)"
/>
<VbenIcon :color="item.color" :icon="item.icon" class="size-8 transition-all duration-300 group-hover:scale-110" @click="$emit('click', item)" />
<span class="ml-4 text-lg font-medium">{{ item.title }}</span>
</div>
<div class="text-foreground/80 mt-4 flex h-10">
@@ -1,13 +1,7 @@
<script setup lang="ts">
import type { WorkbenchQuickNavItem } from '../typing';
import type { WorkbenchQuickNavItem } from "../typing";
import {
Card,
CardContent,
CardHeader,
CardTitle,
VbenIcon,
} from '/@/vben/shadcn-ui';
import { Card, CardContent, CardHeader, CardTitle, VbenIcon } from "/@/vben/shadcn-ui";
interface Props {
items: WorkbenchQuickNavItem[];
@@ -15,14 +9,14 @@ interface Props {
}
defineOptions({
name: 'WorkbenchQuickNav',
name: "WorkbenchQuickNav",
});
withDefaults(defineProps<Props>(), {
items: () => [],
});
defineEmits(['click']);
defineEmits(["click"]);
</script>
<template>
@@ -41,11 +35,7 @@ defineEmits(['click']);
class="flex-col-center border-border group w-1/3 cursor-pointer border-r border-t py-8 hover:shadow-xl"
@click="$emit('click', item)"
>
<VbenIcon
:color="item.color"
:icon="item.icon"
class="size-7 transition-all duration-300 group-hover:scale-125"
/>
<VbenIcon :color="item.color" :icon="item.icon" class="size-7 transition-all duration-300 group-hover:scale-125" />
<span class="text-md mt-2 truncate">{{ item.title }}</span>
</div>
</template>
@@ -1,13 +1,7 @@
<script setup lang="ts">
import type { WorkbenchTodoItem } from '../typing';
import type { WorkbenchTodoItem } from "../typing";
import {
Card,
CardContent,
CardHeader,
CardTitle,
VbenCheckbox,
} from '/@/vben/shadcn-ui';
import { Card, CardContent, CardHeader, CardTitle, VbenCheckbox } from "/@/vben/shadcn-ui";
interface Props {
items: WorkbenchTodoItem[];
@@ -15,7 +9,7 @@ interface Props {
}
defineOptions({
name: 'WorkbenchTodo',
name: "WorkbenchTodo",
});
withDefaults(defineProps<Props>(), {
@@ -45,10 +39,7 @@ withDefaults(defineProps<Props>(), {
{{ item.title }}
</p>
<!-- eslint-disable vue/no-v-html -->
<p
class="text-foreground/80 *:text-primary mt-1 truncate text-xs leading-5"
v-html="item.content"
></p>
<p class="text-foreground/80 *:text-primary mt-1 truncate text-xs leading-5" v-html="item.content"></p>
</div>
</div>
<div class="hidden h-full shrink-0 sm:flex sm:flex-col sm:items-end">
@@ -1,13 +1,7 @@
<script setup lang="ts">
import type { WorkbenchTrendItem } from '../typing';
import type { WorkbenchTrendItem } from "../typing";
import {
Card,
CardContent,
CardHeader,
CardTitle,
VbenIcon,
} from '/@/vben/shadcn-ui';
import { Card, CardContent, CardHeader, CardTitle, VbenIcon } from "/@/vben/shadcn-ui";
interface Props {
items: WorkbenchTrendItem[];
@@ -15,7 +9,7 @@ interface Props {
}
defineOptions({
name: 'WorkbenchTrends',
name: "WorkbenchTrends",
});
withDefaults(defineProps<Props>(), {
@@ -30,26 +24,15 @@ withDefaults(defineProps<Props>(), {
</CardHeader>
<CardContent class="flex flex-wrap p-5 pt-0">
<ul class="divide-border w-full divide-y" role="list">
<li
v-for="item in items"
:key="item.title"
class="flex justify-between gap-x-6 py-5"
>
<li v-for="item in items" :key="item.title" class="flex justify-between gap-x-6 py-5">
<div class="flex min-w-0 items-center gap-x-4">
<VbenIcon
:icon="item.avatar"
alt=""
class="size-10 flex-none rounded-full"
/>
<VbenIcon :icon="item.avatar" alt="" class="size-10 flex-none rounded-full" />
<div class="min-w-0 flex-auto">
<p class="text-foreground text-sm font-semibold leading-6">
{{ item.title }}
</p>
<!-- eslint-disable vue/no-v-html -->
<p
class="text-foreground/80 *:text-primary mt-1 truncate text-xs leading-5"
v-html="item.content"
></p>
<p class="text-foreground/80 *:text-primary mt-1 truncate text-xs leading-5" v-html="item.content"></p>
</div>
</div>
<div class="hidden h-full shrink-0 sm:flex sm:flex-col sm:items-end">
@@ -16,7 +16,7 @@ interface FallbackProps {
/**
* @zh_CN 内置类型
*/
status?: '403' | '404' | '500' | 'coming-soon' | 'offline';
status?: "403" | "404" | "500" | "coming-soon" | "offline";
/**
* @zh_CN 页面提示语
*/
@@ -1,38 +1,35 @@
<script setup lang="ts">
import type { FallbackProps } from './fallback';
import type { FallbackProps } from "./fallback";
import { computed, defineAsyncComponent } from 'vue';
import { useRouter } from 'vue-router';
import { computed, defineAsyncComponent } from "vue";
import { useRouter } from "vue-router";
import { ArrowLeft, RotateCw } from '/@/vben/icons';
import { $t } from '/@/locales';
import { ArrowLeft, RotateCw } from "/@/vben/icons";
import { $t } from "/@/locales";
import { VbenButton } from '/@/vben/shadcn-ui';
import { VbenButton } from "/@/vben/shadcn-ui";
interface Props extends FallbackProps {}
defineOptions({
name: 'Fallback',
// @ts-ignore
name: "Fallback",
});
const props = withDefaults(defineProps<Props>(), {
description: '',
homePath: '/',
image: '',
description: "",
homePath: "/",
image: "",
showBack: true,
status: 'coming-soon',
title: '',
status: "coming-soon",
title: "",
});
const Icon403 = defineAsyncComponent(() => import('./icons/icon-403.vue'));
const Icon404 = defineAsyncComponent(() => import('./icons/icon-404.vue'));
const Icon500 = defineAsyncComponent(() => import('./icons/icon-500.vue'));
const IconHello = defineAsyncComponent(
() => import('./icons/icon-coming-soon.vue'),
);
const IconOffline = defineAsyncComponent(
() => import('./icons/icon-offline.vue'),
);
const Icon403 = defineAsyncComponent(() => import("./icons/icon-403.vue"));
const Icon404 = defineAsyncComponent(() => import("./icons/icon-404.vue"));
const Icon500 = defineAsyncComponent(() => import("./icons/icon-500.vue"));
const IconHello = defineAsyncComponent(() => import("./icons/icon-coming-soon.vue"));
const IconOffline = defineAsyncComponent(() => import("./icons/icon-offline.vue"));
const titleText = computed(() => {
if (props.title) {
@@ -40,23 +37,23 @@ const titleText = computed(() => {
}
switch (props.status) {
case '403': {
return $t('ui.fallback.forbidden');
case "403": {
return $t("ui.fallback.forbidden");
}
case '404': {
return $t('ui.fallback.pageNotFound');
case "404": {
return $t("ui.fallback.pageNotFound");
}
case '500': {
return $t('ui.fallback.internalError');
case "500": {
return $t("ui.fallback.internalError");
}
case 'coming-soon': {
return $t('ui.fallback.comingSoon');
case "coming-soon": {
return $t("ui.fallback.comingSoon");
}
case 'offline': {
return $t('ui.fallback.offlineError');
case "offline": {
return $t("ui.fallback.offlineError");
}
default: {
return '';
return "";
}
}
});
@@ -66,39 +63,39 @@ const descText = computed(() => {
return props.description;
}
switch (props.status) {
case '403': {
return $t('ui.fallback.forbiddenDesc');
case "403": {
return $t("ui.fallback.forbiddenDesc");
}
case '404': {
return $t('ui.fallback.pageNotFoundDesc');
case "404": {
return $t("ui.fallback.pageNotFoundDesc");
}
case '500': {
return $t('ui.fallback.internalErrorDesc');
case "500": {
return $t("ui.fallback.internalErrorDesc");
}
case 'offline': {
return $t('ui.fallback.offlineErrorDesc');
case "offline": {
return $t("ui.fallback.offlineErrorDesc");
}
default: {
return '';
return "";
}
}
});
const fallbackIcon = computed(() => {
switch (props.status) {
case '403': {
case "403": {
return Icon403;
}
case '404': {
case "404": {
return Icon404;
}
case '500': {
case "500": {
return Icon500;
}
case 'coming-soon': {
case "coming-soon": {
return IconHello;
}
case 'offline': {
case "offline": {
return IconOffline;
}
default: {
@@ -108,11 +105,11 @@ const fallbackIcon = computed(() => {
});
const showBack = computed(() => {
return props.status === '403' || props.status === '404';
return props.status === "403" || props.status === "404";
});
const showRefresh = computed(() => {
return props.status === '500' || props.status === 'offline';
return props.status === "500" || props.status === "offline";
});
const { push } = useRouter();
@@ -130,34 +127,24 @@ function refresh() {
<template>
<div class="flex size-full flex-col items-center justify-center duration-300">
<img v-if="image" :src="image" class="md:1/3 w-1/2 lg:w-1/4" />
<component
:is="fallbackIcon"
v-else-if="fallbackIcon"
class="md:1/3 h-1/3 w-1/2 lg:w-1/4"
/>
<component :is="fallbackIcon" v-else-if="fallbackIcon" class="md:1/3 h-1/3 w-1/2 lg:w-1/4" />
<div class="flex-col-center">
<slot v-if="$slots.title" name="title"></slot>
<p
v-else-if="titleText"
class="text-foreground mt-8 text-2xl md:text-3xl lg:text-4xl"
>
<p v-else-if="titleText" class="text-foreground mt-8 text-2xl md:text-3xl lg:text-4xl">
{{ titleText }}
</p>
<slot v-if="$slots.describe" name="describe"></slot>
<p
v-else-if="descText"
class="text-muted-foreground md:text-md my-4 lg:text-lg"
>
<p v-else-if="descText" class="text-muted-foreground md:text-md my-4 lg:text-lg">
{{ descText }}
</p>
<slot v-if="$slots.action" name="action"></slot>
<VbenButton v-else-if="showBack" size="lg" @click="back">
<ArrowLeft class="mr-2 size-4" />
{{ $t('common.backToHome') }}
{{ $t("common.backToHome") }}
</VbenButton>
<VbenButton v-else-if="showRefresh" size="lg" @click="refresh">
<RotateCw class="mr-2 size-4" />
{{ $t('common.refresh') }}
{{ $t("common.refresh") }}
</VbenButton>
</div>
</div>
@@ -1,25 +1,11 @@
<template>
<svg
height="659.29778"
viewBox="0 0 586 659.29778"
width="586"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<svg height="659.29778" viewBox="0 0 586 659.29778" width="586" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="332.47856" cy="254" fill="#f2f2f2" r="254.00001" />
<path
d="M498.46363,113.58835H33.17063c-.99774-.02133-1.78931-.84746-1.76797-1.84521,.02069-.96771,.80026-1.74727,1.76797-1.76796H498.46363c.99774,.02133,1.78931,.84746,1.76794,1.84521-.02069,.96771-.80023,1.74727-1.76794,1.76796Z"
fill="#cacaca"
/>
<rect
fill="#fff"
height="34.98639"
rx="17.49318"
ry="17.49318"
width="163.61147"
x="193.77441"
y="174.47256"
/>
<rect fill="#fff" height="34.98639" rx="17.49318" ry="17.49318" width="163.61147" x="193.77441" y="174.47256" />
<path
d="M128.17493,244.44534H422.98542c9.66122,0,17.49316,7.83197,17.49316,17.49319h0c0,9.66122-7.83194,17.49319-17.49316,17.49319H128.17493c-9.66122,0-17.49318-7.83197-17.49318-17.49319h0c0-9.66122,7.83196-17.49319,17.49318-17.49319Z"
fill="#fff"
@@ -32,22 +18,10 @@
d="M91.64085,657.75932l-.69385-.06793c-23.54068-2.42871-44.82135-15.08929-58.18845-34.61835-3.66138-5.44159-6.62299-11.32251-8.815-17.50409l-.21069-.58966,.62375-.05048c7.44699-.59924,15.09732-1.86292,18.49585-2.46417l-21.91473-7.42511-.1355-.65033c-1.29926-6.10406,1.24612-12.38458,6.4285-15.86176,5.19641-3.64447,12.08731-3.76111,17.40405-.29449,2.38599,1.52399,4.88162,3.03339,7.29489,4.49359,8.29321,5.01636,16.8688,10.20337,23.29828,17.30121,9.74951,10.97778,14.02298,25.76984,11.63,40.25562l4.7829,17.47595Z"
fill="#f2f2f2"
/>
<polygon
fill="#a0616a"
points="171.30016 646.86102 182.10017 646.85999 187.23916 605.198 171.29716 605.19897 171.30016 646.86102"
/>
<path
d="M170.9192,658.12816l33.21436-.00122v-.41998c-.00049-7.13965-5.78833-12.92737-12.92798-12.92773h-.00079l-6.06702-4.60278-11.3197,4.60345-2.89941,.00012,.00055,13.34814Z"
fill="#2f2e41"
/>
<polygon
fill="#a0616a"
points="84.74116 616.94501 93.38016 623.42603 122.49316 593.185 109.74116 583.61902 84.74116 616.94501"
/>
<path
d="M77.67448,625.72966l26.569,19.93188,.25208-.336c4.2843-5.71136,3.12799-13.81433-2.58279-18.09937l-.00064-.00049-2.09079-7.32275-11.81735-3.11102-2.31931-1.73993-8.01019,10.67767Z"
fill="#2f2e41"
/>
<polygon fill="#a0616a" points="171.30016 646.86102 182.10017 646.85999 187.23916 605.198 171.29716 605.19897 171.30016 646.86102" />
<path d="M170.9192,658.12816l33.21436-.00122v-.41998c-.00049-7.13965-5.78833-12.92737-12.92798-12.92773h-.00079l-6.06702-4.60278-11.3197,4.60345-2.89941,.00012,.00055,13.34814Z" fill="#2f2e41" />
<polygon fill="#a0616a" points="84.74116 616.94501 93.38016 623.42603 122.49316 593.185 109.74116 583.61902 84.74116 616.94501" />
<path d="M77.67448,625.72966l26.569,19.93188,.25208-.336c4.2843-5.71136,3.12799-13.81433-2.58279-18.09937l-.00064-.00049-2.09079-7.32275-11.81735-3.11102-2.31931-1.73993-8.01019,10.67767Z" fill="#2f2e41" />
<path
d="M120.64463,451.35271s.59625,16.26422,1.3483,29.30737c.12335,2.13916-4.88821,4.46301-4.75842,6.7901,.08609,1.54395,1.02808,3.04486,1.1156,4.65472,.09235,1.69897-1.20822,3.20282-1.1156,4.95984,.09052,1.71667,1.57422,3.6853,1.66373,5.44244,.96317,18.9093,4.45459,41.54633,.9584,47.87439-1.72299,3.11871-23.68533,46.32446-23.68533,46.32446,0,0,12.23666,18.35498,15.73285,12.23663,4.61771-8.08099,40.20615-45.88745,40.20615-53.10712,0-7.21088,8.23346-61.25323,8.23346-61.25323l5.74103,31.98169,2.63239,6.33655-.82715,3.71997,1.70117,5.02045,.09192,4.96838,1.65619,9.22614s-4.98199,71.88159-2.17633,73.88312c2.81439,2.01038,16.44086,5.62018,18.04901,2.01038,1.59955-3.6098,12.0108-75.01947,12.0108-75.01947,0,0,1.6781-32.72424,3.49622-63.14111,.1048-1.76556,1.34607-3.89825,1.4422-5.63763,.11365-2.01898-.67297-4.64111-.56818-6.599,.11365-2.24628,1.11005-3.82831,1.20618-5.97852,.74292-16.6156-3.42761-36.84912-4.7561-38.84192-4.01202-6.01343-7.62177-10.82074-7.62177-10.82074,0,0-54.03558-17.75403-68.47485,.28625l-3.30185,25.37585Z"
fill="#2f2e41"
@@ -65,25 +39,13 @@
fill="#3f3d56"
/>
<circle cx="167.29993" cy="248.60526" fill="#a0616a" r="24.9798" />
<path
d="M167.8769,273.59047c-.20135,.00662-.4032,.01108-.6048,.01657-.0863,.22388-.17938,.44583-.2868,.66357l.8916-.68015Z"
fill="#2f2e41"
/>
<path
d="M174.73243,249.29823c.03918,.24612,.09912,.48846,.17914,.72449-.03302-.24731-.09308-.49026-.17914-.72449Z"
fill="#2f2e41"
/>
<path d="M167.8769,273.59047c-.20135,.00662-.4032,.01108-.6048,.01657-.0863,.22388-.17938,.44583-.2868,.66357l.8916-.68015Z" fill="#2f2e41" />
<path d="M174.73243,249.29823c.03918,.24612,.09912,.48846,.17914,.72449-.03302-.24731-.09308-.49026-.17914-.72449Z" fill="#2f2e41" />
<path
d="M192.59852,224.6942c-1.0282,3.19272-1.94586-.85715-5.32825-.12869-4.06885,.87625-8.80377,.57532-12.13586-1.91879-4.96478-3.64273-11.39874-4.62335-17.22333-2.62509-5.70154,2.01706-15.25348,3.43933-16.73907,9.30179-.51642,2.03781-.7215,4.24933-1.97321,5.9382-1.09436,1.47662-2.82166,2.31854-4.26608,3.45499-4.87726,3.83743-1.14954,14.73981,1.15881,20.50046,2.30838,5.76065,7.60355,9.95721,13.42526,12.10678,5.63281,2.07977,11.7464,2.44662,17.75531,2.28317,1.04517-2.7106,.59363-5.84137-.26874-8.65134-.93359-3.04199-2.31592-5.97791-2.70593-9.13599s.46643-6.74527,3.11444-8.50986c2.4339-1.62192,6.39465-.63388,7.32062,1.98843-.54028-3.27841,2.7807-6.4509,6.20508-7.00882,3.67651-.599,7.35291,.72833,11.01886,1.38901s2.36475-14.77301,.64209-18.98425Z"
fill="#2f2e41"
/>
<circle
cx="281.3585"
cy="285.71051"
fill="hsl(var(--primary))"
r="51.12006"
transform="translate(-26.58509 542.54478) rotate(-85.26884)"
/>
<circle cx="281.3585" cy="285.71051" fill="hsl(var(--primary))" r="51.12006" transform="translate(-26.58509 542.54478) rotate(-85.26884)" />
<path
d="M294.78675,264.41051l-13.42828,13.42828-13.42828-13.42828c-2.17371-2.17374-5.69806-2.17374-7.87177,0s-2.17371,5.69803,0,7.87177l13.42828,13.42828-13.42828,13.42828c-2.17169,2.17575-2.1684,5.70007,.00739,7.87177,2.17285,2.16879,5.69153,2.16879,7.86438-.00003l13.42828-13.42828,13.42828,13.42828c2.17578,2.17169,5.70007,2.1684,7.87177-.00735,2.16882-2.17288,2.16882-5.6915,0-7.86438l-13.42828-13.42828,13.42828-13.42828c2.17371-2.17374,2.17371-5.69803,0-7.87177s-5.69806-2.17374-7.87177,0h0Z"
fill="#fff"
@@ -101,27 +63,9 @@
fill="#cacaca"
/>
<g>
<ellipse
cx="56.77685"
cy="82.05834"
fill="#3f3d56"
rx="8.45661"
ry="8.64507"
/>
<ellipse
cx="85.9906"
cy="82.05834"
fill="#3f3d56"
rx="8.45661"
ry="8.64507"
/>
<ellipse
cx="115.20435"
cy="82.05834"
fill="#3f3d56"
rx="8.45661"
ry="8.64507"
/>
<ellipse cx="56.77685" cy="82.05834" fill="#3f3d56" rx="8.45661" ry="8.64507" />
<ellipse cx="85.9906" cy="82.05834" fill="#3f3d56" rx="8.45661" ry="8.64507" />
<ellipse cx="115.20435" cy="82.05834" fill="#3f3d56" rx="8.45661" ry="8.64507" />
<path
d="M148.51577,88.89113c-.25977,0-.51904-.10059-.71484-.30078l-5.70605-5.83301c-.38037-.38867-.38037-1.00977,0-1.39844l5.70605-5.83252c.38721-.39453,1.021-.40088,1.41406-.01562,.39502,.38623,.40186,1.01953,.01562,1.41406l-5.02197,5.1333,5.02197,5.13379c.38623,.39453,.37939,1.02783-.01562,1.41406-.19434,.19043-.44678,.28516-.69922,.28516Z"
fill="#3f3d56"
@@ -142,10 +86,7 @@
d="M481.11398,74.91416h-10.60999c-1.21002,0-2.19,.97998-2.19,2.19v10.62c0,1.21002,.97998,2.19,2.19,2.19h10.60999c1.21002,0,2.20001-.97998,2.20001-2.19v-10.62c0-1.21002-.98999-2.19-2.20001-2.19Z"
fill="#3f3d56"
/>
<path
d="M321.19229,78.95414h-84.81c-1.48004,0-2.67004,1.20001-2.67004,2.67004s1.19,2.66998,2.67004,2.66998h84.81c1.46997,0,2.66998-1.20001,2.66998-2.66998s-1.20001-2.67004-2.66998-2.67004Z"
fill="#3f3d56"
/>
<path d="M321.19229,78.95414h-84.81c-1.48004,0-2.67004,1.20001-2.67004,2.67004s1.19,2.66998,2.67004,2.66998h84.81c1.46997,0,2.66998-1.20001,2.66998-2.66998s-1.20001-2.67004-2.66998-2.67004Z" fill="#3f3d56" />
</g>
</svg>
</template>
@@ -1,11 +1,5 @@
<template>
<svg
height="571"
viewBox="0 0 860 571"
width="860"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<svg height="571" viewBox="0 0 860 571" width="860" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M605.66974,324.95306c-7.66934-12.68446-16.7572-26.22768-30.98954-30.36953-16.482-4.7965-33.4132,4.73193-47.77473,14.13453a1392.15692,1392.15692,0,0,0-123.89338,91.28311l.04331.49238q46.22556-3.1878,92.451-6.37554c22.26532-1.53546,45.29557-3.2827,64.97195-13.8156,7.46652-3.99683,14.74475-9.33579,23.20555-9.70782,10.51175-.46217,19.67733,6.87923,26.8802,14.54931,42.60731,45.371,54.937,114.75409,102.73817,154.61591A1516.99453,1516.99453,0,0,0,605.66974,324.95306Z"
fill="#f2f2f2"
@@ -31,11 +25,7 @@
fill="#f2f2f2"
transform="translate(-169.93432 -164.42601)"
/>
<path
d="M306.18775,338.7841l-3.61042,2.93462c1.22123-1.02713,2.4908-1.99013,3.795-2.90144C306.31073,338.80665,306.24935,338.79473,306.18775,338.7841Z"
fill="#f2f2f2"
transform="translate(-169.93432 -164.42601)"
/>
<path d="M306.18775,338.7841l-3.61042,2.93462c1.22123-1.02713,2.4908-1.99013,3.795-2.90144C306.31073,338.80665,306.24935,338.79473,306.18775,338.7841Z" fill="#f2f2f2" transform="translate(-169.93432 -164.42601)" />
<path
d="M831.54929,486.84576c-3.6328-4.42207-7.56046-9.05222-12.99421-10.84836l-5.07308.20008A575.436,575.436,0,0,0,966.74929,651.418Q899.14929,569.13192,831.54929,486.84576Z"
fill="#f2f2f2"
@@ -127,28 +117,12 @@
<circle cx="728.24878" cy="559" fill="hsl(var(--foreground))" r="11" />
<circle cx="755.24878" cy="419" fill="hsl(var(--foreground))" r="11" />
<circle cx="723.24878" cy="317" fill="hsl(var(--foreground))" r="11" />
<path
d="M434.1831,583.426a10.949,10.949,0,1,1-.21-2.16A10.9921,10.9921,0,0,1,434.1831,583.426Z"
fill="hsl(var(--foreground))"
transform="translate(-169.93432 -164.42601)"
/>
<path d="M434.1831,583.426a10.949,10.949,0,1,1-.21-2.16A10.9921,10.9921,0,0,1,434.1831,583.426Z" fill="hsl(var(--foreground))" transform="translate(-169.93432 -164.42601)" />
<circle cx="484.24878" cy="349" fill="hsl(var(--foreground))" r="11" />
<path
d="M545.1831,513.426a10.949,10.949,0,1,1-.21-2.16A10.9921,10.9921,0,0,1,545.1831,513.426Z"
fill="hsl(var(--foreground))"
transform="translate(-169.93432 -164.42601)"
/>
<path
d="M403.1831,481.426a10.949,10.949,0,1,1-.21-2.16A10.9921,10.9921,0,0,1,403.1831,481.426Z"
fill="hsl(var(--foreground))"
transform="translate(-169.93432 -164.42601)"
/>
<path d="M545.1831,513.426a10.949,10.949,0,1,1-.21-2.16A10.9921,10.9921,0,0,1,545.1831,513.426Z" fill="hsl(var(--foreground))" transform="translate(-169.93432 -164.42601)" />
<path d="M403.1831,481.426a10.949,10.949,0,1,1-.21-2.16A10.9921,10.9921,0,0,1,403.1831,481.426Z" fill="hsl(var(--foreground))" transform="translate(-169.93432 -164.42601)" />
<circle cx="599.24878" cy="443" fill="hsl(var(--foreground))" r="11" />
<circle cx="426.24878" cy="338" fill="hsl(var(--foreground))" r="16" />
<path
d="M1028.875,735.26666l-857.75.30733a1.19068,1.19068,0,1,1,0-2.38136l857.75-.30734a1.19069,1.19069,0,0,1,0,2.38137Z"
fill="#cacaca"
transform="translate(-169.93432 -164.42601)"
/>
<path d="M1028.875,735.26666l-857.75.30733a1.19068,1.19068,0,1,1,0-2.38136l857.75-.30734a1.19069,1.19069,0,0,1,0,2.38137Z" fill="#cacaca" transform="translate(-169.93432 -164.42601)" />
</svg>
</template>
@@ -1,33 +1,11 @@
<template>
<svg
height="699"
viewBox="0 0 1119 699"
width="1119"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<svg height="699" viewBox="0 0 1119 699" width="1119" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>server down</title>
<circle cx="292.60911" cy="213" fill="#f2f2f2" r="213" />
<path
d="M31.39089,151.64237c0,77.49789,48.6181,140.20819,108.70073,140.20819"
fill="#2f2e41"
transform="translate(-31.39089 -100.5)"
/>
<path
d="M140.09162,291.85056c0-78.36865,54.255-141.78356,121.30372-141.78356"
fill="hsl(var(--primary))"
transform="translate(-31.39089 -100.5)"
/>
<path
d="M70.77521,158.66768c0,73.61476,31.00285,133.18288,69.31641,133.18288"
fill="hsl(var(--primary))"
transform="translate(-31.39089 -100.5)"
/>
<path
d="M140.09162,291.85056c0-100.13772,62.7103-181.16788,140.20819-181.16788"
fill="#2f2e41"
transform="translate(-31.39089 -100.5)"
/>
<path d="M31.39089,151.64237c0,77.49789,48.6181,140.20819,108.70073,140.20819" fill="#2f2e41" transform="translate(-31.39089 -100.5)" />
<path d="M140.09162,291.85056c0-78.36865,54.255-141.78356,121.30372-141.78356" fill="hsl(var(--primary))" transform="translate(-31.39089 -100.5)" />
<path d="M70.77521,158.66768c0,73.61476,31.00285,133.18288,69.31641,133.18288" fill="hsl(var(--primary))" transform="translate(-31.39089 -100.5)" />
<path d="M140.09162,291.85056c0-100.13772,62.7103-181.16788,140.20819-181.16788" fill="#2f2e41" transform="translate(-31.39089 -100.5)" />
<path
d="M117.22379,292.83905s15.41555-.47479,20.06141-3.783,23.713-7.2585,24.86553-1.95278,23.16671,26.38821,5.76263,26.5286-40.43935-2.711-45.07627-5.53549S117.22379,292.83905,117.22379,292.83905Z"
fill="#a8a8a8"
@@ -42,65 +20,20 @@
<ellipse cx="198.60911" cy="424.5" opacity="0.1" rx="157" ry="21.35866" />
<ellipse cx="836.60911" cy="660.5" fill="#3f3d56" rx="283" ry="38.5" />
<ellipse cx="310.60911" cy="645.5" fill="#3f3d56" rx="170" ry="23.12721" />
<path
d="M494,726.5c90,23,263-30,282-90"
fill="none"
stroke="#2f2e41"
stroke-miterlimit="10"
stroke-width="2"
transform="translate(-31.39089 -100.5)"
/>
<path
d="M341,359.5s130-36,138,80-107,149-17,172"
fill="none"
stroke="#2f2e41"
stroke-miterlimit="10"
stroke-width="2"
transform="translate(-31.39089 -100.5)"
/>
<path
d="M215.40233,637.78332s39.0723-10.82,41.47675,24.04449-32.15951,44.78287-5.10946,51.69566"
fill="none"
stroke="#2f2e41"
stroke-miterlimit="10"
stroke-width="2"
transform="translate(-31.39089 -100.5)"
/>
<path
d="M810.09554,663.73988,802.218,714.03505s-38.78182,20.60284-11.51335,21.20881,155.73324,0,155.73324,0,24.84461,0-14.54318-21.81478l-7.87756-52.719Z"
fill="#2f2e41"
transform="translate(-31.39089 -100.5)"
/>
<path d="M494,726.5c90,23,263-30,282-90" fill="none" stroke="#2f2e41" stroke-miterlimit="10" stroke-width="2" transform="translate(-31.39089 -100.5)" />
<path d="M341,359.5s130-36,138,80-107,149-17,172" fill="none" stroke="#2f2e41" stroke-miterlimit="10" stroke-width="2" transform="translate(-31.39089 -100.5)" />
<path d="M215.40233,637.78332s39.0723-10.82,41.47675,24.04449-32.15951,44.78287-5.10946,51.69566" fill="none" stroke="#2f2e41" stroke-miterlimit="10" stroke-width="2" transform="translate(-31.39089 -100.5)" />
<path d="M810.09554,663.73988,802.218,714.03505s-38.78182,20.60284-11.51335,21.20881,155.73324,0,155.73324,0,24.84461,0-14.54318-21.81478l-7.87756-52.719Z" fill="#2f2e41" transform="translate(-31.39089 -100.5)" />
<path
d="M785.21906,734.69812c6.193-5.51039,16.9989-11.252,16.9989-11.252l7.87756-50.2952,113.9216.10717,7.87756,49.582c9.185,5.08711,14.8749,8.987,18.20362,11.97818,5.05882-1.15422,10.58716-5.44353-18.20362-21.38921l-7.87756-52.719-113.9216,3.02983L802.218,714.03506S769.62985,731.34968,785.21906,734.69812Z"
opacity="0.1"
transform="translate(-31.39089 -100.5)"
/>
<rect
fill="#2f2e41"
height="357.51989"
rx="18.04568"
width="513.25314"
x="578.43291"
y="212.68859"
/>
<rect
fill="#3f3d56"
height="267.83694"
width="478.71308"
x="595.70294"
y="231.77652"
/>
<rect fill="#2f2e41" height="357.51989" rx="18.04568" width="513.25314" x="578.43291" y="212.68859" />
<rect fill="#3f3d56" height="267.83694" width="478.71308" x="595.70294" y="231.77652" />
<circle cx="835.05948" cy="223.29299" fill="#f2f2f2" r="3.02983" />
<path
d="M1123.07694,621.32226V652.6628a18.04341,18.04341,0,0,1-18.04568,18.04568H627.86949A18.04341,18.04341,0,0,1,609.8238,652.6628V621.32226Z"
fill="#2f2e41"
transform="translate(-31.39089 -100.5)"
/>
<polygon
fill="#2f2e41"
points="968.978 667.466 968.978 673.526 642.968 673.526 642.968 668.678 643.417 667.466 651.452 645.651 962.312 645.651 968.978 667.466"
/>
<path d="M1123.07694,621.32226V652.6628a18.04341,18.04341,0,0,1-18.04568,18.04568H627.86949A18.04341,18.04341,0,0,1,609.8238,652.6628V621.32226Z" fill="#2f2e41" transform="translate(-31.39089 -100.5)" />
<polygon fill="#2f2e41" points="968.978 667.466 968.978 673.526 642.968 673.526 642.968 668.678 643.417 667.466 651.452 645.651 962.312 645.651 968.978 667.466" />
<path
d="M1125.828,762.03359c-.59383,2.539-2.83591,5.21743-7.90178,7.75032-18.179,9.08949-55.1429-2.42386-55.1429-2.42386s-28.4804-4.84773-28.4804-17.573a22.72457,22.72457,0,0,1,2.49658-1.48459c7.64294-4.04351,32.98449-14.02122,77.9177.42248a18.73921,18.73921,0,0,1,8.54106,5.59715C1125.07908,756.45353,1126.50669,759.15715,1125.828,762.03359Z"
fill="#2f2e41"
@@ -111,72 +44,21 @@
opacity="0.1"
transform="translate(-31.39089 -100.5)"
/>
<ellipse
cx="1066.53846"
cy="654.13477"
fill="#f2f2f2"
rx="7.87756"
ry="2.42386"
/>
<ellipse cx="1066.53846" cy="654.13477" fill="#f2f2f2" rx="7.87756" ry="2.42386" />
<circle cx="835.05948" cy="545.66686" fill="#f2f2f2" r="11.51335" />
<polygon
opacity="0.1"
points="968.978 667.466 968.978 673.526 642.968 673.526 642.968 668.678 643.417 667.466 968.978 667.466"
/>
<polygon opacity="0.1" points="968.978 667.466 968.978 673.526 642.968 673.526 642.968 668.678 643.417 667.466 968.978 667.466" />
<rect fill="#2f2e41" height="242" width="208" x="108.60911" y="159" />
<rect fill="#3f3d56" height="86" width="250" x="87.60911" y="135" />
<rect fill="#3f3d56" height="86" width="250" x="87.60911" y="237" />
<rect fill="#3f3d56" height="86" width="250" x="87.60911" y="339" />
<rect
fill="#6c63ff"
height="16"
opacity="0.4"
width="16"
x="271.60911"
y="150"
/>
<rect
fill="#6c63ff"
height="16"
opacity="0.8"
width="16"
x="294.60911"
y="150"
/>
<rect fill="#6c63ff" height="16" opacity="0.4" width="16" x="271.60911" y="150" />
<rect fill="#6c63ff" height="16" opacity="0.8" width="16" x="294.60911" y="150" />
<rect fill="#6c63ff" height="16" width="16" x="317.60911" y="150" />
<rect
fill="#6c63ff"
height="16"
opacity="0.4"
width="16"
x="271.60911"
y="251"
/>
<rect
fill="#6c63ff"
height="16"
opacity="0.8"
width="16"
x="294.60911"
y="251"
/>
<rect fill="#6c63ff" height="16" opacity="0.4" width="16" x="271.60911" y="251" />
<rect fill="#6c63ff" height="16" opacity="0.8" width="16" x="294.60911" y="251" />
<rect fill="#6c63ff" height="16" width="16" x="317.60911" y="251" />
<rect
fill="#6c63ff"
height="16"
opacity="0.4"
width="16"
x="271.60911"
y="352"
/>
<rect
fill="#6c63ff"
height="16"
opacity="0.8"
width="16"
x="294.60911"
y="352"
/>
<rect fill="#6c63ff" height="16" opacity="0.4" width="16" x="271.60911" y="352" />
<rect fill="#6c63ff" height="16" opacity="0.8" width="16" x="294.60911" y="352" />
<rect fill="#6c63ff" height="16" width="16" x="317.60911" y="352" />
<circle cx="316.60911" cy="538" fill="#2f2e41" r="79" />
<rect fill="#2f2e41" height="43" width="24" x="280.60911" y="600" />
@@ -190,26 +72,8 @@
fill="#6c63ff"
transform="translate(-31.39089 -100.5)"
/>
<ellipse
cx="417.21511"
cy="611.34365"
fill="#2f2e41"
rx="39.5"
ry="12.40027"
transform="translate(-238.28665 112.98044) rotate(-23.17116)"
/>
<ellipse
cx="269.21511"
cy="664.34365"
fill="#2f2e41"
rx="39.5"
ry="12.40027"
transform="translate(-271.07969 59.02084) rotate(-23.17116)"
/>
<path
d="M394,661.5c0,7.732-19.90861,23-42,23s-43-14.268-43-22,20.90861-6,43-6S394,653.768,394,661.5Z"
fill="#fff"
transform="translate(-31.39089 -100.5)"
/>
<ellipse cx="417.21511" cy="611.34365" fill="#2f2e41" rx="39.5" ry="12.40027" transform="translate(-238.28665 112.98044) rotate(-23.17116)" />
<ellipse cx="269.21511" cy="664.34365" fill="#2f2e41" rx="39.5" ry="12.40027" transform="translate(-271.07969 59.02084) rotate(-23.17116)" />
<path d="M394,661.5c0,7.732-19.90861,23-42,23s-43-14.268-43-22,20.90861-6,43-6S394,653.768,394,661.5Z" fill="#fff" transform="translate(-31.39089 -100.5)" />
</svg>
</template>
@@ -1,127 +1,46 @@
<template>
<svg
data-name="Layer 1"
height="424.8366"
viewBox="0 0 979.32677 424.8366"
width="979.32677"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<svg data-name="Layer 1" height="424.8366" viewBox="0 0 979.32677 424.8366" width="979.32677" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M993.71816,412.83936H419.142a9.19888,9.19888,0,0,0,0,18.39776H435.417V651.3026a9.19888,9.19888,0,0,0,18.39776,0l.1398-220.06548h461.1557l42.52,220.06548a9.19887,9.19887,0,1,0,18.39775,0l2.67633-220.06548h15.01383a9.19888,9.19888,0,0,0,0-18.39776Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M518.73716,371.85047v38.9547H421.141a19.48915,19.48915,0,1,1-1.35523-38.95474q.67739-.02358,1.35523,0Z"
fill="#f2f2f2"
transform="translate(-110.33661 -237.5817)"
/>
<path d="M518.73716,371.85047v38.9547H421.141a19.48915,19.48915,0,1,1-1.35523-38.95474q.67739-.02358,1.35523,0Z" fill="#f2f2f2" transform="translate(-110.33661 -237.5817)" />
<path
d="M521.13449,410.50552a1.49881,1.49881,0,0,1-1.49822,1.49822H419.40273a20.52615,20.52615,0,0,1,0-41.05229H519.63627a1.49827,1.49827,0,1,1,0,2.99653H419.40273a17.52964,17.52964,0,0,0,0,35.05924H519.63627A1.49883,1.49883,0,0,1,521.13449,410.50552Z"
fill="hsl(var(--primary))"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M518.73716,380.84H413.85905a.29966.29966,0,0,1-.00552-.59929H518.73716a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M518.73716,388.03169H413.85905a.29966.29966,0,0,1-.00552-.59929H518.73716a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M518.73716,395.22332H413.85905a.29966.29966,0,0,1-.00552-.59929H518.73716a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M518.73716,402.41487H413.85905a.29966.29966,0,0,1-.00552-.59929H518.73716a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M500.33941,330.80932v38.95474H402.74324a19.48915,19.48915,0,0,1-1.35522-38.95474q.67737-.02358,1.35522,0Z"
fill="#f2f2f2"
transform="translate(-110.33661 -237.5817)"
/>
<path d="M518.73716,380.84H413.85905a.29966.29966,0,0,1-.00552-.59929H518.73716a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M518.73716,388.03169H413.85905a.29966.29966,0,0,1-.00552-.59929H518.73716a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M518.73716,395.22332H413.85905a.29966.29966,0,0,1-.00552-.59929H518.73716a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M518.73716,402.41487H413.85905a.29966.29966,0,0,1-.00552-.59929H518.73716a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M500.33941,330.80932v38.95474H402.74324a19.48915,19.48915,0,0,1-1.35522-38.95474q.67737-.02358,1.35522,0Z" fill="#f2f2f2" transform="translate(-110.33661 -237.5817)" />
<path
d="M502.73673,369.46442a1.49885,1.49885,0,0,1-1.49822,1.49826H401.005a20.52614,20.52614,0,0,1,0-41.05229H501.23851a1.49826,1.49826,0,1,1,0,2.99652H401.005a17.52964,17.52964,0,0,0,0,35.05928H501.23851A1.49884,1.49884,0,0,1,502.73673,369.46442Z"
fill="#3f3d56"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M500.33941,339.79886H395.4613a.29966.29966,0,0,1-.00553-.59929H500.33941a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M500.33941,346.99054H395.4613a.29966.29966,0,0,1-.00553-.59929H500.33941a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M500.33941,354.18217H395.4613a.29966.29966,0,0,1-.00553-.59929H500.33941a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M500.33941,361.37376H395.4613a.29966.29966,0,0,1-.00553-.59929H500.33941a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M613.87355,550.68347V516.71838a5.661,5.661,0,0,0-5.66085-5.66085H479.4284a5.661,5.661,0,0,0-5.66084,5.66085v33.96509Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<rect
fill="#ccc"
height="43.87158"
width="140.10602"
x="363.43092"
y="325.83868"
/>
<path
d="M473.76756,620.02887V653.994a5.661,5.661,0,0,0,5.66084,5.66084H608.2127a5.661,5.661,0,0,0,5.66085-5.66084V620.02887Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path d="M500.33941,339.79886H395.4613a.29966.29966,0,0,1-.00553-.59929H500.33941a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M500.33941,346.99054H395.4613a.29966.29966,0,0,1-.00553-.59929H500.33941a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M500.33941,354.18217H395.4613a.29966.29966,0,0,1-.00553-.59929H500.33941a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M500.33941,361.37376H395.4613a.29966.29966,0,0,1-.00553-.59929H500.33941a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M613.87355,550.68347V516.71838a5.661,5.661,0,0,0-5.66085-5.66085H479.4284a5.661,5.661,0,0,0-5.66084,5.66085v33.96509Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<rect fill="#ccc" height="43.87158" width="140.10602" x="363.43092" y="325.83868" />
<path d="M473.76756,620.02887V653.994a5.661,5.661,0,0,0,5.66084,5.66084H608.2127a5.661,5.661,0,0,0,5.66085-5.66084V620.02887Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<circle cx="432.77633" cy="294.70402" fill="#fff" r="4.24564" />
<circle cx="432.77633" cy="351.3125" fill="#fff" r="4.24564" />
<circle cx="433.00385" cy="406.72228" fill="#fff" r="4.24564" />
<path
d="M597.989,472.33053v38.9547H500.39287a19.48916,19.48916,0,0,1-1.35647-38.9547q.678-.02358,1.35647,0Z"
fill="#f2f2f2"
transform="translate(-110.33661 -237.5817)"
/>
<path d="M597.989,472.33053v38.9547H500.39287a19.48916,19.48916,0,0,1-1.35647-38.9547q.678-.02358,1.35647,0Z" fill="#f2f2f2" transform="translate(-110.33661 -237.5817)" />
<path
d="M600.38637,510.98558a1.49881,1.49881,0,0,1-1.49822,1.49822H498.65461a20.52615,20.52615,0,0,1-.0247-41.05229H598.88815a1.49827,1.49827,0,1,1,0,2.99653H498.65461a17.52963,17.52963,0,0,0,0,35.05923H598.88815A1.49885,1.49885,0,0,1,600.38637,510.98558Z"
fill="#3f3d56"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M597.989,481.32H493.111a.29966.29966,0,0,1-.00553-.59929H597.98913a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M597.989,488.51175H493.111a.29966.29966,0,0,1-.00553-.59929H597.98913a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M597.989,495.70338H493.111a.29966.29966,0,0,1-.00553-.59929H597.98913a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M597.989,502.89493H493.111a.29966.29966,0,0,1-.00553-.59929H597.98913a.29966.29966,0,0,1,0,.59929Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path d="M597.989,481.32H493.111a.29966.29966,0,0,1-.00553-.59929H597.98913a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M597.989,488.51175H493.111a.29966.29966,0,0,1-.00553-.59929H597.98913a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M597.989,495.70338H493.111a.29966.29966,0,0,1-.00553-.59929H597.98913a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M597.989,502.89493H493.111a.29966.29966,0,0,1-.00553-.59929H597.98913a.29966.29966,0,0,1,0,.59929Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path
d="M483.36747,317.81415H438.90162a2.74745,2.74745,0,0,0-1.21689.28306l-11.22288,5.61835a2.0452,2.0452,0,0,0,0,3.76443l11.22288,5.61835a2.74718,2.74718,0,0,0,1.21689.28306h44.46585a2.33381,2.33381,0,0,0,2.4628-2.16532v-11.2367A2.3338,2.3338,0,0,0,483.36747,317.81415Z"
fill="#3f3d56"
@@ -162,16 +81,8 @@
fill="#e6e6e6"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M811.1898,389.574H600.50692a4.174,4.174,0,0,1-4.16467-4.174V355.69092H815.35446V385.4A4.17408,4.17408,0,0,1,811.1898,389.574Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M815.57469,369.73213H596.15V242.61337a5.0375,5.0375,0,0,1,5.03186-5.03167h209.361a5.03755,5.03755,0,0,1,5.03191,5.03167Z"
fill="#3f3d56"
transform="translate(-110.33661 -237.5817)"
/>
<path d="M811.1898,389.574H600.50692a4.174,4.174,0,0,1-4.16467-4.174V355.69092H815.35446V385.4A4.17408,4.17408,0,0,1,811.1898,389.574Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
<path d="M815.57469,369.73213H596.15V242.61337a5.0375,5.0375,0,0,1,5.03186-5.03167h209.361a5.03755,5.03755,0,0,1,5.03191,5.03167Z" fill="#3f3d56" transform="translate(-110.33661 -237.5817)" />
<path
d="M802.46932,360.50584h-193.214a3.88344,3.88344,0,0,1-3.87919-3.87908V250.68707a3.88365,3.88365,0,0,1,3.87919-3.87932h193.214a3.88366,3.88366,0,0,1,3.8792,3.87932V356.62676A3.88345,3.88345,0,0,1,802.46932,360.50584Z"
fill="#fff"
@@ -192,36 +103,16 @@
fill="#e4e4e4"
transform="translate(-110.33661 -237.5817)"
/>
<rect
fill="#e4e4e4"
height="118.48951"
width="20.52816"
x="747.4019"
y="303.23122"
/>
<rect fill="#e4e4e4" height="118.48951" width="20.52816" x="747.4019" y="303.23122" />
<path
d="M799.31222,658.58132c0,2.218,31.10721.858,69.47992.858s69.47991,1.36012,69.47991-.858-31.1072-19.807-69.47991-19.807S799.31222,656.36323,799.31222,658.58132Z"
fill="#e4e4e4"
transform="translate(-110.33661 -237.5817)"
/>
<polygon
fill="#ffb6b6"
points="675.186 407.461 659.908 407.46 652.64 348.531 675.188 348.532 675.186 407.461"
/>
<path
d="M789.41863,659.852l-49.2623-.00183v-.62309a19.17528,19.17528,0,0,1,19.17426-19.17395h.00122l30.08773.00122Z"
fill="#2f2e41"
transform="translate(-110.33661 -237.5817)"
/>
<polygon
fill="#ffb6b6"
points="630.031 407.461 614.753 407.46 607.485 348.531 630.033 348.532 630.031 407.461"
/>
<path
d="M744.2636,659.852l-49.2623-.00183v-.62309a19.1753,19.1753,0,0,1,19.17426-19.17395h.00122l30.08773.00122Z"
fill="#2f2e41"
transform="translate(-110.33661 -237.5817)"
/>
<polygon fill="#ffb6b6" points="675.186 407.461 659.908 407.46 652.64 348.531 675.188 348.532 675.186 407.461" />
<path d="M789.41863,659.852l-49.2623-.00183v-.62309a19.17528,19.17528,0,0,1,19.17426-19.17395h.00122l30.08773.00122Z" fill="#2f2e41" transform="translate(-110.33661 -237.5817)" />
<polygon fill="#ffb6b6" points="630.031 407.461 614.753 407.46 607.485 348.531 630.033 348.532 630.031 407.461" />
<path d="M744.2636,659.852l-49.2623-.00183v-.62309a19.1753,19.1753,0,0,1,19.17426-19.17395h.00122l30.08773.00122Z" fill="#2f2e41" transform="translate(-110.33661 -237.5817)" />
<circle cx="766.88656" cy="41.63615" fill="#ffb6b6" r="26.56401" />
<path
d="M920.21655,461.22417s8.91308,47.1307-24.99958,53.13247-82.86639,10.21993-82.86639,10.21993L790.36706,627.14324l-29.53443-2.63675s3.928-123.46737,13.5876-133.127,70.71212-38.58282,70.71212-38.58282Z"
@@ -253,10 +144,6 @@
fill="hsl(var(--primary))"
transform="translate(-110.33661 -237.5817)"
/>
<path
d="M1088.24817,662.4183H111.75183a1.41521,1.41521,0,1,1,0-2.83042h976.49634a1.41521,1.41521,0,1,1,0,2.83042Z"
fill="#ccc"
transform="translate(-110.33661 -237.5817)"
/>
<path d="M1088.24817,662.4183H111.75183a1.41521,1.41521,0,1,1,0-2.83042h976.49634a1.41521,1.41521,0,1,1,0,2.83042Z" fill="#ccc" transform="translate(-110.33661 -237.5817)" />
</svg>
</template>
@@ -1,61 +1,24 @@
<template>
<svg
height="458.68642"
viewBox="0 0 656 458.68642"
width="656"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<svg height="458.68642" viewBox="0 0 656 458.68642" width="656" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect fill="#3f3d56" height="2" width="656" y="434.34322" />
<g>
<path
d="M471.97092,210.81397c-6.0733-36.41747-37.72842-64.16942-75.86423-64.16942H240.14931c-38.12099,0-69.76869,27.72972-75.86421,64.12497-.70358,4.16241-1.06653,8.44331-1.06653,12.80573v135.88599c0,4.36237,.36295,8.63589,1.06653,12.79831,4.85126,28.99625,25.92996,52.49686,53.58563,60.84393,7.05095,2.13306,14.53143,3.28104,22.27859,3.28104h155.9574c7.74716,0,15.22763-1.14798,22.27859-3.28104,27.66309-8.35449,48.74921-31.86993,53.58563-60.88837,.6962-4.14758,1.05911-8.40628,1.05911-12.75388V223.57525c0-4.34758-.36292-8.61369-1.05911-12.76128h-.00003Zm-62.66592,222.28954c-4.2883,.76285-8.69516,1.16281-13.19827,1.16281H240.14931c-4.50313,0-8.90997-.39999-13.19829-1.16281-35.01768-6.22885-61.60677-36.83228-61.60677-73.64224v-45.10526c0-127.45004,103.31242-165.58582,230.76244-165.58582,41.31314,0,74.80505,33.49194,74.80505,74.80505v135.88599c-100.29059,13.42047-26.58911,67.41339-61.60678,73.64224l.00003,.00003Z"
fill="#3f3d56"
/>
<polygon
fill="hsl(var(--primary))"
points="349.16196 249.18644 355.16196 288.18642 443.16196 276.18642 434.66196 230.6195 349.16196 249.18644"
/>
<rect
fill="#2f2e41"
height="37.66125"
width="36.38461"
x="381.84177"
y="30.34218"
/>
<polygon
fill="#ffb6b6"
points="385.16196 70.18643 394.16196 43.18643 411.70447 43.18643 412.62653 70.18643 385.16196 70.18643"
/>
<polygon
isolation="isolate"
opacity=".1"
points="385.16196 70.18643 394.16196 43.18643 411.70447 43.18643 412.62653 70.18643 385.16196 70.18643"
/>
<path
d="M394.66196,310.68642l-1,104-1,8v11.48425l15,1.51575,1-23s16-45,12-80-2-25-2-25l-24,3Z"
fill="#ffb6b6"
/>
<polygon fill="hsl(var(--primary))" points="349.16196 249.18644 355.16196 288.18642 443.16196 276.18642 434.66196 230.6195 349.16196 249.18644" />
<rect fill="#2f2e41" height="37.66125" width="36.38461" x="381.84177" y="30.34218" />
<polygon fill="#ffb6b6" points="385.16196 70.18643 394.16196 43.18643 411.70447 43.18643 412.62653 70.18643 385.16196 70.18643" />
<polygon isolation="isolate" opacity=".1" points="385.16196 70.18643 394.16196 43.18643 411.70447 43.18643 412.62653 70.18643 385.16196 70.18643" />
<path d="M394.66196,310.68642l-1,104-1,8v11.48425l15,1.51575,1-23s16-45,12-80-2-25-2-25l-24,3Z" fill="#ffb6b6" />
<path
d="M404.18408,318.85363l-36.90134,97.23831-1.97873,7.81567-4.1777,10.69742-14.52368-4.04477,7.43539-21.78796s1.46619-47.7373,17.92432-78.88422,10.9574-22.5596,10.9574-22.5596l21.26434,11.52512v.00003Z"
fill="#ffb6b6"
/>
<path
d="M385.16196,67.18643l-27,12,17.23959,89.01208-2.72385,127.75565-18,38s-3.01575,21.73227,27.98425,7.73227,66-18,66-18l-8.5-58.5-7.5-153.5,1-34-22-14s-26.5,3.5-26.5,3.50001Z"
fill="#2f2e41"
/>
<path
d="M370.1243,335.34322l-29.96231-50.15677,34.23959-116.98792-16.23959-89.01208,28.49045-12.19685s14.74915,14.36248,14.74915,26.20894-31.27728,242.1447-31.27728,242.1447v-.00003Z"
fill="#e6e6e6"
/>
<path
d="M435.1243,325.34322l-27.19693-233.62811c-.34341-2.94999,.16013-5.93678,1.45178-8.6111l7.78284-16.11441,30.5,8.69685-12.26041,95.51208,32.76041,93.98792-33.03769,60.15677Z"
fill="#e6e6e6"
/>
<path
d="M410.66196,433.68642s-19-11-21-5-3,11-3,11c0,0-5,19,10,19s14-8.64172,14-8.64172v-16.35828Z"
fill="#2f2e41"
/>
<path d="M385.16196,67.18643l-27,12,17.23959,89.01208-2.72385,127.75565-18,38s-3.01575,21.73227,27.98425,7.73227,66-18,66-18l-8.5-58.5-7.5-153.5,1-34-22-14s-26.5,3.5-26.5,3.50001Z" fill="#2f2e41" />
<path d="M370.1243,335.34322l-29.96231-50.15677,34.23959-116.98792-16.23959-89.01208,28.49045-12.19685s14.74915,14.36248,14.74915,26.20894-31.27728,242.1447-31.27728,242.1447v-.00003Z" fill="#e6e6e6" />
<path d="M435.1243,325.34322l-27.19693-233.62811c-.34341-2.94999,.16013-5.93678,1.45178-8.6111l7.78284-16.11441,30.5,8.69685-12.26041,95.51208,32.76041,93.98792-33.03769,60.15677Z" fill="#e6e6e6" />
<path d="M410.66196,433.68642s-19-11-21-5-3,11-3,11c0,0-5,19,10,19s14-8.64172,14-8.64172v-16.35828Z" fill="#2f2e41" />
<path
d="M344.53574,427.60598s21.69977-3.33459,21.3801,2.9819c-.3197,6.31647-1.20709,11.33768-1.20709,11.33768,0,0-2.25433,19.51712-16.22662,14.06046s-9.89713-13.14252-9.89713-13.14252l5.95078-15.23749-.00003-.00003Z"
fill="#2f2e41"
@@ -70,12 +33,7 @@
fill="#2f2e41"
/>
<g>
<circle
cx="333.2486"
cy="323.64455"
fill="hsl(var(--primary))"
r="85"
/>
<circle cx="333.2486" cy="323.64455" fill="hsl(var(--primary))" r="85" />
<g>
<path
d="M384.17838,316.82296h-10.56668c-1.64377-9.68713-6.7168-18.46011-14.2923-24.71729-17.43427-14.39993-43.24109-11.94022-57.64099,5.49411-.04913,.05563-.09644,.11282-.14169,.17151-1.15063,1.49146-.87427,3.63333,.61716,4.784,1.49118,1.1507,3.63306,.87448,4.78394-.61697,6.25537-7.5788,15.72369-12.40167,26.31064-12.40167,16.20853,.00195,30.17899,11.40631,33.42572,27.28629h-9.31805c-.3988,.00012-.78458,.13992-1.09082,.39502-.72375,.60281-.82175,1.6781-.21915,2.40186l13.41125,16.09894c.06577,.07889,.13855,.1517,.21759,.21747,.72324,.60327,1.79871,.50583,2.40186-.21747l13.41125-16.09894c.25504-.30624,.3949-.69223,.39514-1.09082,.00027-.94186-.763-1.70566-1.70486-1.70605v.00003Z"
@@ -92,10 +50,7 @@
d="M356.98148,237.19363c-1.02939,7.36621-5.66458,12.80598-10.35239,12.15012-4.68781-.65588-7.65225-7.15837-6.62149-14.52707,.37137-2.94914,1.4436-5.76646,3.12701-8.21626l4.75577-31.15587,14.57297,2.54338-6.23553,30.44414c.94736,2.81844,1.20581,5.82278,.75369,8.76157h-.00003Z"
fill="#ffb6b6"
/>
<path
d="M369.66196,77.68643s-15-5-17,13-4,39.99999-4,39.99999c0,0-9,21-5,32s11,3.3307,4,12.66534-6.02478,40.04724-6.02478,40.04724l22.52478-1.13387s12.5-82.57875,12.5-84.57875-7-52-7-52v.00004Z"
fill="#e6e6e6"
/>
<path d="M369.66196,77.68643s-15-5-17,13-4,39.99999-4,39.99999c0,0-9,21-5,32s11,3.3307,4,12.66534-6.02478,40.04724-6.02478,40.04724l22.52478-1.13387s12.5-82.57875,12.5-84.57875-7-52-7-52v.00004Z" fill="#e6e6e6" />
<g>
<path
id="uuid-6bf35aa9-e432-4b51-af77-8f4eb19e6e42-575"
@@ -1,2 +1,2 @@
export type * from './fallback';
export { default as Fallback } from './fallback.vue';
export type * from "./fallback";
export { default as Fallback } from "./fallback.vue";
@@ -1,4 +1,4 @@
export * from './about';
export * from './authentication';
export * from './dashboard';
export * from './fallback';
export * from "./about";
export * from "./authentication";
export * from "./dashboard";
export * from "./fallback";
@@ -1,24 +1,24 @@
import type { SortableOptions } from 'sortablejs';
import type { SortableOptions } from "sortablejs";
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useSortable } from '../use-sortable';
import { useSortable } from "../use-sortable";
describe('useSortable', () => {
describe("useSortable", () => {
beforeEach(() => {
vi.mock('sortablejs/modular/sortable.complete.esm.js', () => ({
vi.mock("sortablejs/modular/sortable.complete.esm.js", () => ({
default: {
create: vi.fn(),
},
}));
});
it('should call Sortable.create with the correct options', async () => {
it("should call Sortable.create with the correct options", async () => {
// Create a mock element
const mockElement = document.createElement('div') as HTMLDivElement;
const mockElement = document.createElement("div") as HTMLDivElement;
// Define custom options
const customOptions: SortableOptions = {
group: 'test-group',
group: "test-group",
sort: false,
};
@@ -29,9 +29,7 @@ describe('useSortable', () => {
await initializeSortable();
// Import sortablejs to access the mocked create function
const Sortable = await import(
'sortablejs/modular/sortable.complete.esm.js'
);
const Sortable = await import("sortablejs/modular/sortable.complete.esm.js");
// Verify that Sortable.create was called with the correct parameters
expect(Sortable.default.create).toHaveBeenCalledTimes(1);
@@ -42,7 +40,7 @@ describe('useSortable', () => {
delay: 400,
delayOnTouchOnly: true,
...customOptions,
}),
})
);
});
});
@@ -27,7 +27,7 @@ export function useLayoutContentStyle() {
position: "fixed",
top: `${top}px`,
width: `${width}px`,
zIndex: 150
zIndex: 150,
};
});
@@ -61,7 +61,7 @@ export function useLayoutHeaderStyle() {
},
setLayoutHeaderHeight: (height: number) => {
headerHeight.value = `${height}px`;
}
},
};
}
@@ -74,6 +74,6 @@ export function useLayoutFooterStyle() {
},
setLayoutFooterHeight: (height: number) => {
footerHeight.value = `${height}px`;
}
},
};
}
@@ -78,7 +78,7 @@ const useNamespace = (block: string) => {
em,
is,
m,
namespace
namespace,
};
};
@@ -42,7 +42,7 @@ export function usePriorityValue<T extends Record<string, any>, S extends Record
export function usePriorityValues<T extends Record<string, any>, S extends Ref<Record<string, any>> = Readonly<Ref<NoInfer<T>, NoInfer<T>>>>(props: T, state: S | undefined) {
const result: { [K in keyof T]: ComputedRef<T[K]> } = {} as never;
(Object.keys(props) as (keyof T)[]).forEach((key) => {
(Object.keys(props) as (keyof T)[]).forEach(key => {
result[key] = usePriorityValue(key as keyof typeof props, props, state);
});
@@ -57,13 +57,13 @@ export function usePriorityValues<T extends Record<string, any>, S extends Ref<R
export function useForwardPriorityValues<T extends Record<string, any>, S extends Ref<Record<string, any>> = Readonly<Ref<NoInfer<T>, NoInfer<T>>>>(props: T, state: S | undefined) {
const computedResult: { [K in keyof T]: ComputedRef<T[K]> } = {} as never;
(Object.keys(props) as (keyof T)[]).forEach((key) => {
(Object.keys(props) as (keyof T)[]).forEach(key => {
computedResult[key] = usePriorityValue(key as keyof typeof props, props, state);
});
return computed(() => {
const unwrapResult: Record<string, any> = {};
Object.keys(props).forEach((key) => {
Object.keys(props).forEach(key => {
unwrapResult[key] = unref(computedResult[key]);
});
return unwrapResult as { [K in keyof T]: T[K] };
@@ -17,7 +17,7 @@ export function useScrollLock() {
const layoutFixedNodes = document.querySelectorAll<HTMLElement>(`.${SCROLL_FIXED_CLASS}`);
const nodes = [...layoutFixedNodes];
if (nodes.length > 0) {
nodes.forEach((node) => {
nodes.forEach(node => {
node.dataset.transition = node.style.transition;
node.style.transition = "none";
node.style.paddingRight = `${scrollbarWidth}px`;
@@ -34,7 +34,7 @@ export function useScrollLock() {
const layoutFixedNodes = document.querySelectorAll<HTMLElement>(`.${SCROLL_FIXED_CLASS}`);
const nodes = [...layoutFixedNodes];
if (nodes.length > 0) {
nodes.forEach((node) => {
nodes.forEach(node => {
node.style.paddingRight = "";
requestAnimationFrame(() => {
node.style.transition = node.dataset.transition || "";
@@ -22,6 +22,6 @@ export const useSimpleLocale = createSharedComposable(() => {
return {
$t,
currentLocale,
setSimpleLocale
setSimpleLocale,
};
});
@@ -7,7 +7,7 @@ export const messages: Record<Locale, Record<string, string>> = {
confirm: "Confirm",
expand: "Expand",
reset: "Reset",
submit: "Submit"
submit: "Submit",
},
"zh-CN": {
cancel: "取消",
@@ -15,8 +15,8 @@ export const messages: Record<Locale, Record<string, string>> = {
confirm: "确认",
expand: "展开",
reset: "重置",
submit: "提交"
}
submit: "提交",
},
};
export const getMessages = (locale: Locale) => messages[locale];
@@ -11,13 +11,13 @@ function useSortable<T extends HTMLElement>(sortableContainer: T, options: Sorta
animation: 300,
delay: 400,
delayOnTouchOnly: true,
...options
...options,
});
return sortable as Sortable;
};
return {
initializeSortable
initializeSortable,
};
}
@@ -19,10 +19,10 @@ export interface LanguageOption {
export const SUPPORT_LANGUAGES: LanguageOption[] = [
{
label: "简体中文",
value: "zh-CN"
value: "zh-CN",
},
{
label: "English",
value: "en-US"
}
value: "en-US",
},
];
@@ -88,13 +88,13 @@
box-shadow: 0 0 0 1000px transparent inset;
} */
input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button {
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
@apply m-0 appearance-none;
}
/* 只有非mac下才进行调整,mac下使用默认滚动条 */
html:not([data-platform='macOs']) {
html:not([data-platform="macOs"]) {
::-webkit-scrollbar {
@apply h-[10px] w-[10px];
}
@@ -3,19 +3,19 @@
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.side-content[data-side='top'] {
.side-content[data-side="top"] {
animation-name: slide-up;
}
.side-content[data-side='bottom'] {
.side-content[data-side="bottom"] {
animation-name: slide-down;
}
.side-content[data-side='left'] {
.side-content[data-side="left"] {
animation-name: slide-left;
}
.side-content[data-side='right'] {
.side-content[data-side="right"] {
animation-name: slide-right;
}
@@ -1,6 +1,6 @@
.dark,
.dark[data-theme='custom'],
.dark[data-theme='default'] {
.dark[data-theme="custom"],
.dark[data-theme="default"] {
/* Default background color of <body />...etc */
--background: 222.34deg 10.43% 12.27%;
@@ -107,8 +107,8 @@
color-scheme: dark;
}
.dark[data-theme='violet'],
[data-theme='violet'] .dark {
.dark[data-theme="violet"],
[data-theme="violet"] .dark {
--background: 224 71.4% 4.1%;
--background-deep: var(--background);
--foreground: 210 20% 98%;
@@ -133,8 +133,8 @@
--header: 224 71.4% 4.1%;
}
.dark[data-theme='pink'],
[data-theme='pink'] .dark {
.dark[data-theme="pink"],
[data-theme="pink"] .dark {
--background: 20 14.3% 4.1%;
--background-deep: var(--background);
--foreground: 0 0% 95%;
@@ -159,8 +159,8 @@
--header: 20 14.3% 4.1%;
}
.dark[data-theme='rose'],
[data-theme='rose'] .dark {
.dark[data-theme="rose"],
[data-theme="rose"] .dark {
--background: 0 0% 3.9%;
--background-deep: var(--background);
--foreground: 0 0% 98%;
@@ -185,8 +185,8 @@
--header: 0 0% 3.9%;
}
.dark[data-theme='sky-blue'],
[data-theme='sky-blue'] .dark {
.dark[data-theme="sky-blue"],
[data-theme="sky-blue"] .dark {
--background: 222.2 84% 4.9%;
--background-deep: var(--background);
--foreground: 210 40% 98%;
@@ -211,8 +211,8 @@
--header: 222.2 84% 4.9%;
}
.dark[data-theme='deep-blue'],
[data-theme='deep-blue'] .dark {
.dark[data-theme="deep-blue"],
[data-theme="deep-blue"] .dark {
--background: 222.2 84% 4.9%;
--background-deep: var(--background);
--foreground: 210 40% 98%;
@@ -237,8 +237,8 @@
--header: 222.2 84% 4.9%;
}
.dark[data-theme='green'],
[data-theme='green'] .dark {
.dark[data-theme="green"],
[data-theme="green"] .dark {
--background: 20 14.3% 4.1%;
--background-deep: var(--background);
--foreground: 0 0% 95%;
@@ -263,8 +263,8 @@
--header: 20 14.3% 4.1%;
}
.dark[data-theme='deep-green'],
[data-theme='deep-green'] .dark {
.dark[data-theme="deep-green"],
[data-theme="deep-green"] .dark {
--background: 20 14.3% 4.1%;
--background-deep: var(--background);
--foreground: 0 0% 95%;
@@ -289,8 +289,8 @@
--header: 20 14.3% 4.1%;
}
.dark[data-theme='orange'],
[data-theme='orange'] .dark {
.dark[data-theme="orange"],
[data-theme="orange"] .dark {
--background: 20 14.3% 4.1%;
--background-deep: var(--background);
--foreground: 60 9.1% 97.8%;
@@ -315,8 +315,8 @@
--header: 20 14.3% 4.1%;
}
.dark[data-theme='yellow'],
[data-theme='yellow'] .dark {
.dark[data-theme="yellow"],
[data-theme="yellow"] .dark {
--background: 20 14.3% 4.1%;
--background-deep: var(--background);
--foreground: 60 9.1% 97.8%;
@@ -341,8 +341,8 @@
--header: 20 14.3% 4.1%;
}
.dark[data-theme='zinc'],
[data-theme='zinc'] .dark {
.dark[data-theme="zinc"],
[data-theme="zinc"] .dark {
--background: 240 10% 3.9%;
--background-deep: var(--background);
--foreground: 0 0% 98%;
@@ -367,8 +367,8 @@
--header: 240 10% 3.9%;
}
.dark[data-theme='neutral'],
[data-theme='neutral'] .dark {
.dark[data-theme="neutral"],
[data-theme="neutral"] .dark {
--background: 0 0% 3.9%;
--background-deep: var(--background);
--foreground: 0 0% 98%;
@@ -393,8 +393,8 @@
--header: 0 0% 3.9%;
}
.dark[data-theme='slate'],
[data-theme='slate'] .dark {
.dark[data-theme="slate"],
[data-theme="slate"] .dark {
--background: 222.2 84% 4.9%;
--background-deep: var(--background);
--foreground: 210 40% 98%;
@@ -419,8 +419,8 @@
--header: 222.2 84% 4.9%;
}
.dark[data-theme='gray'],
[data-theme='gray'] .dark {
.dark[data-theme="gray"],
[data-theme="gray"] .dark {
--background: 224 71.4% 4.1%;
--background-deep: var(--background);
--foreground: 210 20% 98%;
@@ -1,10 +1,7 @@
:root {
/** 弹出层的基础层级 **/
--popup-z-index: 2000;
--font-family:
-apple-system, blinkmacsystemfont, 'Segoe UI', roboto, 'Helvetica Neue',
arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
--font-family: -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
/* Default background color of <body />...etc */
--background: 0 0% 100%;
@@ -108,7 +105,7 @@
color-scheme: light;
}
[data-theme='violet'] {
[data-theme="violet"] {
/* --background: 0 0% 100%; */
--foreground: 224 71.4% 4.1%;
--card: 0 0% 100%;
@@ -129,7 +126,7 @@
--ring: 262.1 83.3% 57.8%;
}
[data-theme='pink'] {
[data-theme="pink"] {
/* --background: 0 0% 100%; */
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
@@ -150,7 +147,7 @@
--ring: 346.8 77.2% 49.8%;
}
[data-theme='rose'] {
[data-theme="rose"] {
/* --background: 0 0% 100%; */
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
@@ -171,7 +168,7 @@
--ring: 346.8 77.2% 49.8%;
}
[data-theme='sky-blue'] {
[data-theme="sky-blue"] {
/* --background: 0 0% 100%; */
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
@@ -192,7 +189,7 @@
--ring: 221.2 83.2% 53.3%;
}
[data-theme='deep-blue'] {
[data-theme="deep-blue"] {
/* --background: 0 0% 100%; */
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
@@ -213,7 +210,7 @@
--ring: 221.2 83.2% 53.3%;
}
[data-theme='green'] {
[data-theme="green"] {
/* --background: 0 0% 100%; */
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
@@ -234,7 +231,7 @@
--ring: 142.1 76.2% 36.3%;
}
[data-theme='deep-green'] {
[data-theme="deep-green"] {
/* --background: 0 0% 100%; */
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
@@ -255,7 +252,7 @@
--ring: 142.1 76.2% 36.3%;
}
[data-theme='orange'] {
[data-theme="orange"] {
/* --background: 0 0% 100%; */
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
@@ -276,7 +273,7 @@
--ring: 24.6 95% 53.1%;
}
[data-theme='yellow'] {
[data-theme="yellow"] {
/* --background: 0 0% 100%; */
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
@@ -297,7 +294,7 @@
--ring: 20 14.3% 4.1%;
}
[data-theme='zinc'] {
[data-theme="zinc"] {
/* --background: 0 0% 100%; */
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
@@ -318,7 +315,7 @@
--ring: 240 5.9% 10%;
}
[data-theme='neutral'] {
[data-theme="neutral"] {
/* --background: 0 0% 100%; */
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
@@ -339,7 +336,7 @@
--ring: 0 0% 3.9%;
}
[data-theme='slate'] {
[data-theme="slate"] {
/* --background: 0 0% 100%; */
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
@@ -360,7 +357,7 @@
--ring: 222.2 84% 4.9%;
}
[data-theme='gray'] {
[data-theme="gray"] {
/* --background: 0 0% 100%; */
--foreground: 224 71.4% 4.1%;
--card: 0 0% 100%;
@@ -1,4 +1,4 @@
import './default.css';
import './dark.css';
import "./default.css";
import "./dark.css";
export {};
@@ -1,7 +1,7 @@
@forward './constants';
@forward "./constants";
@mixin b($block) {
$B: $namespace + '-' + $block !global;
$B: $namespace + "-" + $block !global;
.#{$B} {
@content;
@@ -1,5 +1,5 @@
$namespace: 'vben' !default;
$common-separator: '-' !default;
$element-separator: '__' !default;
$modifier-separator: '--' !default;
$state-prefix: 'is' !default;
$namespace: "vben" !default;
$common-separator: "-" !default;
$element-separator: "__" !default;
$modifier-separator: "--" !default;
$state-prefix: "is" !default;
@@ -18,7 +18,7 @@ const resetButtonOptions = computed(() => {
return {
content: `${$t.value("reset")}`,
show: true,
...unref(rootProps).resetButtonOptions
...unref(rootProps).resetButtonOptions,
};
});
@@ -26,7 +26,7 @@ const submitButtonOptions = computed(() => {
return {
content: `${$t.value("submit")}`,
show: true,
...unref(rootProps).submitButtonOptions
...unref(rootProps).submitButtonOptions,
};
});
@@ -38,7 +38,7 @@ const queryFormStyle = computed(() => {
if (!unref(rootProps).actionWrapperClass) {
return {
"grid-column": `-2 / -1`,
marginLeft: "auto"
marginLeft: "auto",
};
}
@@ -83,7 +83,7 @@ watch(
defineExpose({
handleReset,
handleSubmit
handleSubmit,
});
</script>
<template>
@@ -20,11 +20,11 @@ export const COMPONENT_MAP: Record<BaseFormComponentType, Component> = {
VbenInput,
VbenInputPassword,
VbenPinInput,
VbenSelect
VbenSelect,
};
export const COMPONENT_BIND_EVENT_MAP: Partial<Record<BaseFormComponentType, string>> = {
VbenCheckbox: "checked"
VbenCheckbox: "checked",
};
export function setupVbenForm<T extends BaseFormComponentType = BaseFormComponentType>(options: VbenFormAdapterOptions<T>) {
@@ -35,7 +35,7 @@ export function setupVbenForm<T extends BaseFormComponentType = BaseFormComponen
Object.assign(DEFAULT_FORM_COMMON_CONFIG, {
disabledOnChangeListener,
disabledOnInputListener,
emptyStateValue
emptyStateValue,
});
if (defineRules) {
@@ -27,7 +27,7 @@ function getDefaultState(): VbenFormProps {
submitButtonOptions: {},
submitOnChange: false,
submitOnEnter: false,
wrapperClass: "grid-cols-1"
wrapperClass: "grid-cols-1",
};
}
@@ -54,14 +54,14 @@ export class FormApi {
this.store = new Store<VbenFormProps>(
{
...defaultState,
...storeState
...storeState,
},
{
onUpdate: () => {
this.prevState = this.state;
this.state = this.store.state;
this.updateState();
}
},
}
);
@@ -102,7 +102,7 @@ export class FormApi {
return async (needMerge: boolean = true) => {
try {
const results = await Promise.all(
chain.map(async (api) => {
chain.map(async api => {
const validateResult = await api.validate();
if (!validateResult.valid) {
return;
@@ -122,7 +122,7 @@ export class FormApi {
};
}
return target[prop];
}
},
});
return proxy;
@@ -133,7 +133,7 @@ export class FormApi {
Object.assign(this.form, formActions);
this.stateHandler.setConditionTrue();
this.setLatestSubmissionValues({
...toRaw(this.handleRangeTimeValue(this.form.values))
...toRaw(this.handleRangeTimeValue(this.form.values)),
});
this.isMounted = true;
}
@@ -147,10 +147,10 @@ export class FormApi {
const fieldSet = new Set(fields);
const schema = this.state?.schema ?? [];
const filterSchema = schema.filter((item) => !fieldSet.has(item.fieldName));
const filterSchema = schema.filter(item => !fieldSet.has(item.fieldName));
this.setState({
schema: filterSchema
schema: filterSchema,
});
}
@@ -165,7 +165,7 @@ export class FormApi {
async resetValidate() {
const form = await this.getForm();
const fields = Object.keys(form.errors.value);
fields.forEach((field) => {
fields.forEach(field => {
form.setFieldError(field, undefined);
});
}
@@ -181,11 +181,11 @@ export class FormApi {
setState(stateOrFn: ((prev: VbenFormProps) => Partial<VbenFormProps>) | Partial<VbenFormProps>) {
if (isFunction(stateOrFn)) {
this.store.setState((prev) => {
this.store.setState(prev => {
return mergeWithArrayOverride(stateOrFn(prev), prev);
});
} else {
this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev));
this.store.setState(prev => mergeWithArrayOverride(stateOrFn, prev));
}
}
@@ -239,7 +239,7 @@ export class FormApi {
updateSchema(schema: Partial<FormSchema>[]) {
const updated: Partial<FormSchema>[] = [...schema];
const hasField = updated.every((item) => Reflect.has(item, "fieldName") && item.fieldName);
const hasField = updated.every(item => Reflect.has(item, "fieldName") && item.fieldName);
if (!hasField) {
console.error("All items in the schema array must have a valid `fieldName` property to be updated");
@@ -249,7 +249,7 @@ export class FormApi {
const updatedMap: Record<string, any> = {};
updated.forEach((item) => {
updated.forEach(item => {
if (item.fieldName) {
updatedMap[item.fieldName] = item;
}
@@ -351,8 +351,8 @@ export class FormApi {
const prevSchema = this.prevState?.schema ?? [];
// 进行了删除schema操作
if (currentSchema.length < prevSchema.length) {
const currentFields = new Set(currentSchema.map((item) => item.fieldName));
const deletedSchema = prevSchema.filter((item) => !currentFields.has(item.fieldName));
const currentFields = new Set(currentSchema.map(item => item.fieldName));
const deletedSchema = prevSchema.filter(item => !currentFields.has(item.fieldName));
for (const schema of deletedSchema) {
this.form?.setFieldValue?.(schema.fieldName, undefined);
}
@@ -16,6 +16,6 @@ export const useFormContext = () => {
return {
componentBindEventMap,
componentMap,
isVertical
isVertical,
};
};
@@ -30,7 +30,7 @@ export default function useDependencies(getDependencies: () => FormItemDependenc
const triggerFieldValues = computed(() => {
// 该字段可能会被多个字段触发
const triggerFields = getDependencies()?.triggerFields ?? [];
return triggerFields.map((dep) => {
return triggerFields.map(dep => {
return values.value[dep];
});
});
@@ -105,6 +105,6 @@ export default function useDependencies(getDependencies: () => FormItemDependenc
isDisabled,
isIf,
isRequired,
isShow
isShow,
};
}
@@ -59,7 +59,7 @@ export function useExpandable(props: FormRenderProps) {
const containerRect = container?.getBoundingClientRect();
formItems.forEach((el) => {
formItems.forEach(el => {
const itemRect = el.getBoundingClientRect();
// 计算元素在第几行
@@ -1,27 +1,20 @@
<script setup lang="ts">
import type { ZodType } from 'zod';
import type { ZodType } from "zod";
import type { FormSchema, MaybeComponentProps } from '../types';
import type { FormSchema, MaybeComponentProps } from "../types";
import { computed, nextTick, useTemplateRef, watch } from 'vue';
import { computed, nextTick, useTemplateRef, watch } from "vue";
import {
FormControl,
FormDescription,
FormField,
FormItem,
FormMessage,
VbenRenderContent,
} from '/@/vben/shadcn-ui';
import { cn, isFunction, isObject, isString } from '/@/vben/shared/utils';
import { FormControl, FormDescription, FormField, FormItem, FormMessage, VbenRenderContent } from "/@/vben/shadcn-ui";
import { cn, isFunction, isObject, isString } from "/@/vben/shared/utils";
import { toTypedSchema } from '@vee-validate/zod';
import { useFieldError, useFormValues } from 'vee-validate';
import { toTypedSchema } from "@vee-validate/zod";
import { useFieldError, useFormValues } from "vee-validate";
import { injectRenderFormProps, useFormContext } from './context';
import useDependencies from './dependencies';
import FormLabel from './form-label.vue';
import { isEventObjectLike } from './helper';
import { injectRenderFormProps, useFormContext } from "./context";
import useDependencies from "./dependencies";
import FormLabel from "./form-label.vue";
import { isEventObjectLike } from "./helper";
interface Props extends FormSchema {}
@@ -54,15 +47,13 @@ const { componentBindEventMap, componentMap, isVertical } = useFormContext();
const formRenderProps = injectRenderFormProps();
const values = useFormValues();
const errors = useFieldError(fieldName);
const fieldComponentRef = useTemplateRef<HTMLInputElement>('fieldComponentRef');
const fieldComponentRef = useTemplateRef<HTMLInputElement>("fieldComponentRef");
const formApi = formRenderProps.form;
const compact = formRenderProps.compact;
const isInValid = computed(() => errors.value?.length > 0);
const FieldComponent = computed(() => {
const finalComponent = isString(component)
? componentMap.value[component]
: component;
const finalComponent = isString(component) ? componentMap.value[component] : component;
if (!finalComponent) {
//
console.warn(`Component ${component} is not registered`);
@@ -70,17 +61,10 @@ const FieldComponent = computed(() => {
return finalComponent;
});
const {
dynamicComponentProps,
dynamicRules,
isDisabled,
isIf,
isRequired,
isShow,
} = useDependencies(() => dependencies);
const { dynamicComponentProps, dynamicRules, isDisabled, isIf, isRequired, isShow } = useDependencies(() => dependencies);
const labelStyle = computed(() => {
return labelClass?.includes('w-') || isVertical.value
return labelClass?.includes("w-") || isVertical.value
? {}
: {
width: `${labelWidth}px`,
@@ -109,14 +93,14 @@ const shouldRequired = computed(() => {
}
if (isString(currentRules.value)) {
return ['required', 'selectRequired'].includes(currentRules.value);
return ["required", "selectRequired"].includes(currentRules.value);
}
let isOptional = currentRules?.value?.isOptional?.();
//
const typeName = currentRules?.value?._def?.typeName;
if (typeName === 'ZodDefault') {
if (typeName === "ZodDefault") {
const innerType = currentRules?.value?._def.innerType;
if (innerType) {
isOptional = innerType.isOptional?.();
@@ -133,7 +117,7 @@ const fieldRules = computed(() => {
let rules = currentRules.value;
if (!rules) {
return isRequired.value ? 'required' : null;
return isRequired.value ? "required" : null;
}
if (isString(rules)) {
@@ -151,9 +135,7 @@ const fieldRules = computed(() => {
});
const computedProps = computed(() => {
const finalComponentProps = isFunction(componentProps)
? componentProps(values.value, formApi!)
: componentProps;
const finalComponentProps = isFunction(componentProps) ? componentProps(values.value, formApi!) : componentProps;
return {
...commonComponentProps,
@@ -164,14 +146,14 @@ const computedProps = computed(() => {
watch(
() => computedProps.value?.autofocus,
(value) => {
value => {
if (value === true) {
nextTick(() => {
autofocus();
});
}
},
{ immediate: true },
{ immediate: true }
);
const shouldDisabled = computed(() => {
@@ -193,7 +175,7 @@ const fieldProps = computed(() => {
const rules = fieldRules.value;
return {
keepValue: true,
label: isString(label) ? label : '',
label: isString(label) ? label : "",
...(rules ? { rules } : {}),
...(formFieldProps as Record<string, any>),
};
@@ -201,18 +183,14 @@ const fieldProps = computed(() => {
function fieldBindEvent(slotProps: Record<string, any>) {
const modelValue = slotProps.componentField.modelValue;
const handler = slotProps.componentField['onUpdate:modelValue'];
const handler = slotProps.componentField["onUpdate:modelValue"];
const bindEventField =
modelPropName ||
(isString(component) ? componentBindEventMap.value?.[component] : null);
const bindEventField = modelPropName || (isString(component) ? componentBindEventMap.value?.[component] : null);
let value = modelValue;
// antd design event
if (modelValue && isObject(modelValue) && bindEventField) {
value = isEventObjectLike(modelValue)
? modelValue?.target?.[bindEventField]
: (modelValue?.[bindEventField] ?? modelValue);
value = isEventObjectLike(modelValue) ? modelValue?.target?.[bindEventField] : (modelValue?.[bindEventField] ?? modelValue);
}
if (bindEventField) {
@@ -246,12 +224,8 @@ function createComponentProps(slotProps: Record<string, any>) {
...slotProps.componentField,
...computedProps.value,
...bindEvents,
...(Reflect.has(computedProps.value, 'onChange')
? { onChange: computedProps.value.onChange }
: {}),
...(Reflect.has(computedProps.value, 'onInput')
? { onInput: computedProps.value.onInput }
: {}),
...(Reflect.has(computedProps.value, "onChange") ? { onChange: computedProps.value.onChange } : {}),
...(Reflect.has(computedProps.value, "onInput") ? { onInput: computedProps.value.onInput } : {}),
};
return binds;
@@ -270,12 +244,7 @@ function autofocus() {
</script>
<template>
<FormField
v-if="isIf"
v-bind="fieldProps"
v-slot="slotProps"
:name="fieldName"
>
<FormField v-if="isIf" v-slot="slotProps" v-bind="fieldProps" :name="fieldName">
<FormItem
v-show="isShow"
:class="{
@@ -297,7 +266,7 @@ function autofocus() {
'mr-2 flex-shrink-0 justify-end': !isVertical,
'mb-1 flex-row': isVertical,
},
labelClass,
labelClass
)
"
:help="help"
@@ -326,21 +295,13 @@ function autofocus() {
:is="FieldComponent"
ref="fieldComponentRef"
:class="{
'border-destructive focus:border-destructive hover:border-destructive/80 focus:shadow-[0_0_0_2px_rgba(255,38,5,0.06)]':
isInValid,
'border-destructive focus:border-destructive hover:border-destructive/80 focus:shadow-[0_0_0_2px_rgba(255,38,5,0.06)]': isInValid,
}"
v-bind="createComponentProps(slotProps)"
:disabled="shouldDisabled"
>
<template
v-for="name in renderContentKey"
:key="name"
#[name]="renderSlotProps"
>
<VbenRenderContent
:content="customContentRender[name]"
v-bind="{ ...renderSlotProps, formContext: slotProps }"
/>
<template v-for="name in renderContentKey" :key="name" #[name]="renderSlotProps">
<VbenRenderContent :content="customContentRender[name]" v-bind="{ ...renderSlotProps, formContext: slotProps }" />
</template>
<!-- <slot></slot> -->
</component>
@@ -1,12 +1,8 @@
<script setup lang="ts">
import type { CustomRenderType } from '../types';
import type { CustomRenderType } from "../types";
import {
FormLabel,
VbenHelpTooltip,
VbenRenderContent,
} from '/@/vben/shadcn-ui';
import { cn } from '/@/vben/shared/utils';
import { FormLabel, VbenHelpTooltip, VbenRenderContent } from "/@/vben/shadcn-ui";
import { cn } from "/@/vben/shared/utils";
interface Props {
class?: string;
@@ -21,7 +21,7 @@ const props = withDefaults(defineProps<Props & { globalCommonConfig?: FormCommon
commonConfig: () => ({}),
globalCommonConfig: () => ({}),
showCollapseButton: false,
wrapperClass: "grid-cols-1 sm:grid-cols-2 md:grid-cols-3"
wrapperClass: "grid-cols-1 sm:grid-cols-2 md:grid-cols-3",
});
const emits = defineEmits<{
@@ -34,7 +34,7 @@ const { isCalculated, keepFormItemIndex, wrapperRef } = useExpandable(props);
const shapes = computed(() => {
const resultShapes: FormShape[] = [];
props.schema?.forEach((schema) => {
props.schema?.forEach(schema => {
const { fieldName } = schema;
const rules = schema.rules as ZodTypeAny;
@@ -49,7 +49,7 @@ const shapes = computed(() => {
default: getDefaultValueInZodStack(rules),
fieldName,
required: !["ZodNullable", "ZodOptional"].includes(typeName),
rules: baseRules
rules: baseRules,
});
});
return resultShapes;
@@ -60,10 +60,10 @@ const formComponent = computed(() => (props.form ? "form" : Form));
const formComponentProps = computed(() => {
return props.form
? {
onSubmit: props.form.handleSubmit((val: any) => emits("submit", val))
onSubmit: props.form.handleSubmit((val: any) => emits("submit", val)),
}
: {
onSubmit: (val: GenericObject) => emits("submit", val)
onSubmit: (val: GenericObject) => emits("submit", val),
};
});
@@ -91,7 +91,7 @@ const computedSchema = computed(
labelClass = "",
labelWidth = 100,
modelPropName = "",
wrapperClass = ""
wrapperClass = "",
} = mergeWithArrayOverride(props.commonConfig, props.globalCommonConfig);
return (props.schema || []).map((schema, index) => {
const keepIndex = keepFormItemIndex.value;
@@ -117,10 +117,10 @@ const computedSchema = computed(
controlClass: cn(controlClass, schema.controlClass),
formFieldProps: {
...formFieldProps,
...schema.formFieldProps
...schema.formFieldProps,
},
formItemClass: cn("flex-shrink-0", { hidden }, formItemClass, schema.formItemClass),
labelClass: cn(labelClass, schema.labelClass)
labelClass: cn(labelClass, schema.labelClass),
};
});
}

Some files were not shown because too many files have changed in this diff Show More