This commit is contained in:
dap
2025-02-18 14:08:05 +08:00
29 changed files with 1086 additions and 46 deletions

View File

@@ -22,23 +22,29 @@ export namespace AuthApi {
* 登录
*/
export async function loginApi(data: AuthApi.LoginParams) {
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
return requestClient.post<AuthApi.LoginResult>('/auth/login', data, {
withCredentials: true,
});
}
/**
* 刷新accessToken
*/
export async function refreshTokenApi() {
return baseRequestClient.post<AuthApi.RefreshTokenResult>('/auth/refresh', {
withCredentials: true,
});
return baseRequestClient.post<AuthApi.RefreshTokenResult>(
'/auth/refresh',
null,
{
withCredentials: true,
},
);
}
/**
* 退出登录
*/
export async function logoutApi() {
return baseRequestClient.post('/auth/logout', {
return baseRequestClient.post('/auth/logout', null, {
withCredentials: true,
});
}

View File

@@ -111,3 +111,9 @@ export const requestClient = createRequestClient(apiURL, {
});
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
export interface PageFetchParams {
[key: string]: any;
pageNo?: number;
pageSize?: number;
}

View File

@@ -2,6 +2,7 @@ import { createApp, watchEffect } from 'vue';
import { registerAccessDirective } from '@vben/access';
import { initTippy } from '@vben/common-ui';
import { MotionPlugin } from '@vben/plugins/motion';
import { preferences } from '@vben/preferences';
import { initStores } from '@vben/stores';
import '@vben/styles';
@@ -49,6 +50,9 @@ async function bootstrap(namespace: string) {
// 配置@tanstack/vue-query
app.use(VueQueryPlugin);
// 配置Motion插件
app.use(MotionPlugin);
// 动态更新标题
watchEffect(() => {
if (preferences.app.dynamicTitle) {

View File

@@ -255,6 +255,33 @@ const routes: RouteRecordRaw[] = [
title: 'Tippy',
},
},
{
name: 'JsonViewer',
path: '/examples/json-viewer',
component: () => import('#/views/examples/json-viewer/index.vue'),
meta: {
icon: 'tabler:json',
title: 'JsonViewer',
},
},
{
name: 'Motion',
path: '/examples/motion',
component: () => import('#/views/examples/motion/index.vue'),
meta: {
icon: 'mdi:animation-play',
title: 'Motion',
},
},
{
name: 'CountTo',
path: '/examples/count-to',
component: () => import('#/views/examples/count-to/index.vue'),
meta: {
icon: 'mdi:animation-play',
title: 'CountTo',
},
},
],
},
];

View File

@@ -0,0 +1,178 @@
<script lang="ts" setup>
import type { CountToProps, TransitionPresets } from '@vben/common-ui';
import { reactive } from 'vue';
import { CountTo, Page, TransitionPresetsKeys } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import {
Button,
Card,
Col,
Form,
FormItem,
Input,
InputNumber,
message,
Row,
Select,
Switch,
} from 'ant-design-vue';
const props = reactive<CountToProps & { transition: TransitionPresets }>({
decimal: '.',
decimals: 2,
decimalStyle: {
fontSize: 'small',
fontStyle: 'italic',
},
delay: 0,
disabled: false,
duration: 2000,
endVal: 100_000,
mainStyle: {
color: 'hsl(var(--primary))',
fontSize: 'xx-large',
fontWeight: 'bold',
},
prefix: '¥',
prefixStyle: {
paddingRight: '0.5rem',
},
separator: ',',
startVal: 0,
suffix: '元',
suffixStyle: {
paddingLeft: '0.5rem',
},
transition: 'easeOutQuart',
});
function changeNumber() {
props.endVal =
Math.floor(Math.random() * 100_000_000) / 10 ** (props.decimals || 0);
}
function openDocumentation() {
window.open('https://vueuse.org/core/useTransition/', '_blank');
}
function onStarted() {
message.loading({
content: '动画已开始',
duration: 0,
key: 'animator-info',
});
}
function onFinished() {
message.success({
content: '动画已结束',
duration: 2,
key: 'animator-info',
});
}
</script>
<template>
<Page title="CountTo" description="数字滚动动画组件。使用">
<template #description>
<span>
使用useTransition封装的数字滚动动画组件每次改变当前值都会产生过渡动画
</span>
<Button type="link" @click="openDocumentation">
查看useTransition文档
</Button>
</template>
<Card title="基本用法">
<div class="flex w-full items-center justify-center pb-4">
<CountTo v-bind="props" @started="onStarted" @finished="onFinished" />
</div>
<Form :model="props">
<Row :gutter="20">
<Col :span="8">
<FormItem label="初始值" name="startVal">
<InputNumber v-model:value="props.startVal" />
</FormItem>
</Col>
<Col :span="8">
<FormItem label="当前值" name="endVal">
<InputNumber
v-model:value="props.endVal"
class="w-full"
:precision="props.decimals"
>
<template #addonAfter>
<IconifyIcon
v-tippy="`设置一个随机值`"
class="size-5 cursor-pointer outline-none"
icon="ix:random-filled"
@click="changeNumber"
/>
</template>
</InputNumber>
</FormItem>
</Col>
<Col :span="8">
<FormItem label="禁用动画" name="disabled">
<Switch v-model:checked="props.disabled" />
</FormItem>
</Col>
<Col :span="8">
<FormItem label="延迟动画" name="delay">
<InputNumber v-model:value="props.delay" :min="0" />
</FormItem>
</Col>
<Col :span="8">
<FormItem label="持续时间" name="duration">
<InputNumber v-model:value="props.duration" :min="0" />
</FormItem>
</Col>
<Col :span="8">
<FormItem label="小数位数" name="decimals">
<InputNumber
v-model:value="props.decimals"
:min="0"
:precision="0"
/>
</FormItem>
</Col>
<Col :span="8">
<FormItem label="分隔符" name="separator">
<Input v-model:value="props.separator" />
</FormItem>
</Col>
<Col :span="8">
<FormItem label="小数点" name="decimal">
<Input v-model:value="props.decimal" />
</FormItem>
</Col>
<Col :span="8">
<FormItem label="动画" name="transition">
<Select v-model:value="props.transition">
<Select.Option
v-for="preset in TransitionPresetsKeys"
:key="preset"
:value="preset"
>
{{ preset }}
</Select.Option>
</Select>
</FormItem>
</Col>
<Col :span="8">
<FormItem label="前缀" name="prefix">
<Input v-model:value="props.prefix" />
</FormItem>
</Col>
<Col :span="8">
<FormItem label="后缀" name="suffix">
<Input v-model:value="props.suffix" />
</FormItem>
</Col>
</Row>
</Form>
</Card>
</Page>
</template>

View File

@@ -0,0 +1,66 @@
export const json1 = {
additionalInfo: {
author: 'Your Name',
debug: true,
version: '1.3.10',
versionCode: 132,
},
additionalNotes: 'This JSON is used for demonstration purposes',
tools: [
{
description: 'Description of Tool 1',
name: 'Tool 1',
},
{
description: 'Description of Tool 2',
name: 'Tool 2',
},
{
description: 'Description of Tool 3',
name: 'Tool 3',
},
{
description: 'Description of Tool 4',
name: 'Tool 4',
},
],
};
export const json2 = JSON.parse(`
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-3.5-turbo-0613",
"system_fingerprint": "fp_44709d6fcb",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello there, how may I assist you today?"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21,
"debug_mode": true
},
"debug": {
"startAt": "2021-08-01T00:00:00Z",
"logs": [
{
"timestamp": "2021-08-01T00:00:00Z",
"message": "This is a debug message",
"extra":[ "extra1", "extra2" ]
},
{
"timestamp": "2021-08-01T00:00:01Z",
"message": "This is another debug message",
"extra":[ "extra3", "extra4" ]
}
]
}
}
`);

View File

@@ -0,0 +1,51 @@
<script lang="ts" setup>
import type { JsonViewerAction, JsonViewerValue } from '@vben/common-ui';
import { JsonViewer, Page } from '@vben/common-ui';
import { Card, message } from 'ant-design-vue';
import { json1, json2 } from './data';
function handleKeyClick(key: string) {
message.info(`点击了Key ${key}`);
}
function handleValueClick(value: JsonViewerValue) {
message.info(`点击了Value ${JSON.stringify(value)}`);
}
function handleCopied(_event: JsonViewerAction) {
message.success('已复制JSON');
}
</script>
<template>
<Page
title="Json Viewer"
description="一个渲染 JSON 结构数据的组件,支持复制、展开等,简单易用"
>
<Card title="默认配置">
<JsonViewer :value="json1" />
</Card>
<Card title="可复制、默认展开3层、显示边框、事件处理" class="mt-4">
<JsonViewer
:value="json2"
:expand-depth="3"
copyable
:sort="false"
@key-click="handleKeyClick"
@value-click="handleValueClick"
@copied="handleCopied"
boxed
/>
</Card>
<Card title="预览模式" class="mt-4">
<JsonViewer
:value="json2"
copyable
preview-mode
:show-array-index="false"
/>
</Card>
</Page>
</template>

View File

@@ -0,0 +1,213 @@
<script lang="ts" setup>
import { reactive } from 'vue';
import { Page } from '@vben/common-ui';
import { Motion, MotionGroup, MotionPresets } from '@vben/plugins/motion';
import { refAutoReset, watchDebounced } from '@vueuse/core';
import {
Button,
Card,
Col,
Form,
FormItem,
InputNumber,
Row,
Select,
} from 'ant-design-vue';
// 本例子用不到visible类型的动画。带有VisibleOnce和Visible的类型会在组件进入视口被显示时执行动画
const presets = MotionPresets.filter((v) => !v.includes('Visible'));
const showCard1 = refAutoReset(true, 100);
const showCard2 = refAutoReset(true, 100);
const showCard3 = refAutoReset(true, 100);
const motionProps = reactive({
delay: 0,
duration: 300,
enter: { scale: 1 },
hovered: { scale: 1.1, transition: { delay: 0, duration: 50 } },
preset: 'fade',
tapped: { scale: 0.9, transition: { delay: 0, duration: 50 } },
});
const motionGroupProps = reactive({
delay: 0,
duration: 300,
enter: { scale: 1 },
hovered: { scale: 1.1, transition: { delay: 0, duration: 50 } },
preset: 'fade',
tapped: { scale: 0.9, transition: { delay: 0, duration: 50 } },
});
watchDebounced(
motionProps,
() => {
showCard2.value = false;
},
{ debounce: 200, deep: true },
);
watchDebounced(
motionGroupProps,
() => {
showCard3.value = false;
},
{ debounce: 200, deep: true },
);
function openDocPage() {
window.open('https://motion.vueuse.org/', '_blank');
}
</script>
<template>
<Page title="Motion">
<template #description>
<span>一个易于使用的为其它组件赋予动画效果的组件</span>
<Button type="link" @click="openDocPage">查看文档</Button>
</template>
<Card title="使用指令" :body-style="{ minHeight: '5rem' }">
<template #extra>
<Button type="primary" @click="showCard1 = false">重载</Button>
</template>
<div>
<div class="relative flex gap-2 overflow-hidden" v-if="showCard1">
<Button v-motion-fade-visible>fade</Button>
<Button v-motion-pop-visible :duration="500">pop</Button>
<Button v-motion-slide-left>slide-left</Button>
<Button v-motion-slide-right>slide-right</Button>
<Button v-motion-slide-bottom>slide-bottom</Button>
<Button v-motion-slide-top>slide-top</Button>
</div>
</div>
</Card>
<Card
class="mt-2"
title="使用组件(将内部作为一个整体添加动画)"
:body-style="{ padding: 0 }"
>
<div
class="relative flex min-h-32 items-center justify-center gap-2 overflow-hidden"
>
<Motion
v-bind="motionProps"
v-if="showCard2"
class="flex items-center gap-2"
>
<Button size="large">这个按钮在显示时会有动画效果</Button>
<span>附属组件会作为整体处理动画</span>
</Motion>
</div>
<div
class="relative flex min-h-32 items-center justify-center gap-2 overflow-hidden"
>
<div v-if="showCard2" class="flex items-center gap-2">
<span>顺序延迟</span>
<Motion
v-bind="{
...motionProps,
delay: motionProps.delay + 100 * i,
}"
v-for="i in 5"
:key="i"
>
<Button size="large">按钮{{ i }}</Button>
</Motion>
</div>
</div>
<div>
<Form :model="motionProps" :label-col="{ span: 10 }">
<Row>
<Col :span="8">
<FormItem prop="preset" label="动画效果">
<Select v-model:value="motionProps.preset">
<Select.Option
:value="preset"
v-for="preset in presets"
:key="preset"
>
{{ preset }}
</Select.Option>
</Select>
</FormItem>
</Col>
<Col :span="8">
<FormItem prop="duration" label="持续时间">
<InputNumber v-model:value="motionProps.duration" />
</FormItem>
</Col>
<Col :span="8">
<FormItem prop="delay" label="延迟动画">
<InputNumber v-model:value="motionProps.delay" />
</FormItem>
</Col>
<Col :span="8">
<FormItem prop="hovered.scale" label="Hover缩放">
<InputNumber v-model:value="motionProps.hovered.scale" />
</FormItem>
</Col>
<Col :span="8">
<FormItem prop="hovered.tapped" label="按下时缩放">
<InputNumber v-model:value="motionProps.tapped.scale" />
</FormItem>
</Col>
</Row>
</Form>
</div>
</Card>
<Card
class="mt-2"
title="分组动画(每个子元素都会应用相同的独立动画)"
:body-style="{ padding: 0 }"
>
<div
class="relative flex min-h-32 items-center justify-center gap-2 overflow-hidden"
>
<MotionGroup v-bind="motionGroupProps" v-if="showCard3">
<Button size="large">按钮1</Button>
<Button size="large">按钮2</Button>
<Button size="large">按钮3</Button>
<Button size="large">按钮4</Button>
<Button size="large">按钮5</Button>
</MotionGroup>
</div>
<div>
<Form :model="motionGroupProps" :label-col="{ span: 10 }">
<Row>
<Col :span="8">
<FormItem prop="preset" label="动画效果">
<Select v-model:value="motionGroupProps.preset">
<Select.Option
:value="preset"
v-for="preset in presets"
:key="preset"
>
{{ preset }}
</Select.Option>
</Select>
</FormItem>
</Col>
<Col :span="8">
<FormItem prop="duration" label="持续时间">
<InputNumber v-model:value="motionGroupProps.duration" />
</FormItem>
</Col>
<Col :span="8">
<FormItem prop="delay" label="延迟动画">
<InputNumber v-model:value="motionGroupProps.delay" />
</FormItem>
</Col>
<Col :span="8">
<FormItem prop="hovered.scale" label="Hover缩放">
<InputNumber v-model:value="motionGroupProps.hovered.scale" />
</FormItem>
</Col>
<Col :span="8">
<FormItem prop="hovered.tapped" label="按下时缩放">
<InputNumber v-model:value="motionGroupProps.tapped.scale" />
</FormItem>
</Col>
</Row>
</Form>
</div>
</Card>
</Page>
</template>