Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
This commit is contained in:
commit
c3edbec3f0
28
apps/backend-mock/api/demo/bigint.ts
Normal file
28
apps/backend-mock/api/demo/bigint.ts
Normal file
@ -0,0 +1,28 @@
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
const data = `
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": 123456789012345678901234567890123456789012345678901234567890,
|
||||
"name": "John Doe",
|
||||
"age": 30,
|
||||
"email": "john-doe@demo.com"
|
||||
},
|
||||
{
|
||||
"id": 987654321098765432109876543210987654321098765432109876543210,
|
||||
"name": "Jane Smith",
|
||||
"age": 25,
|
||||
"email": "jane@demo.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
setHeader(event, 'Content-Type', 'application/json');
|
||||
return data;
|
||||
});
|
@ -10,7 +10,8 @@ import { $t } from '@vben/locales';
|
||||
|
||||
import { isArray } from 'lodash-es';
|
||||
|
||||
setupVbenForm<ComponentType>({
|
||||
async function initSetupVbenForm() {
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
@ -43,11 +44,12 @@ setupVbenForm<ComponentType>({
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
|
@ -13,6 +13,7 @@ import { setupGlobalComponent } from '#/components/global';
|
||||
import { $t, setupI18n } from '#/locales';
|
||||
|
||||
import { initComponentAdapter } from './adapter/component';
|
||||
import { initSetupVbenForm } from './adapter/form';
|
||||
import App from './app.vue';
|
||||
import { router } from './router';
|
||||
|
||||
@ -20,6 +21,9 @@ async function bootstrap(namespace: string) {
|
||||
// 初始化组件适配器
|
||||
await initComponentAdapter();
|
||||
|
||||
// 初始化表单组件
|
||||
await initSetupVbenForm();
|
||||
|
||||
// // 设置弹窗的默认配置
|
||||
// setDefaultModalProps({
|
||||
// fullscreenButton: false,
|
||||
|
@ -26,6 +26,12 @@ outline: deep
|
||||
|
||||
<DemoPreview dir="demos/vben-ellipsis-text/tooltip" />
|
||||
|
||||
## 自动显示 tooltip
|
||||
|
||||
通过`tooltip-when-ellipsis`设置,仅在文本长度超出导致省略号出现时才触发 tooltip。
|
||||
|
||||
<DemoPreview dir="demos/vben-ellipsis-text/auto-display" />
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
@ -37,6 +43,8 @@ outline: deep
|
||||
| maxWidth | 文本区域最大宽度 | `number \| string` | `'100%'` |
|
||||
| placement | 提示浮层的位置 | `'bottom'\|'left'\|'right'\|'top'` | `'top'` |
|
||||
| tooltip | 启用文本提示 | `boolean` | `true` |
|
||||
| tooltipWhenEllipsis | 内容超出,自动启用文本提示 | `boolean` | `false` |
|
||||
| ellipsisThreshold | 设置 tooltipWhenEllipsis 后才生效,文本截断检测的像素差异阈值,越大则判断越严格,如果碰见异常情况可以自己设置阈值 | `number` | `3` |
|
||||
| tooltipBackgroundColor | 提示文本的背景颜色 | `string` | - |
|
||||
| tooltipColor | 提示文本的颜色 | `string` | - |
|
||||
| tooltipFontSize | 提示文本的大小 | `string` | - |
|
||||
|
16
docs/src/demos/vben-ellipsis-text/auto-display/index.vue
Normal file
16
docs/src/demos/vben-ellipsis-text/auto-display/index.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { EllipsisText } from '@vben/common-ui';
|
||||
|
||||
const text = `
|
||||
Vben Admin 是一个基于 Vue3.0、Vite、 TypeScript 的后台解决方案,目标是为开发中大型项目提供开箱即用的解决方案。
|
||||
`;
|
||||
</script>
|
||||
<template>
|
||||
<EllipsisText :line="2" :tooltip-when-ellipsis="true">
|
||||
{{ text }}
|
||||
</EllipsisText>
|
||||
|
||||
<EllipsisText :line="3" :tooltip-when-ellipsis="true">
|
||||
{{ text }}
|
||||
</EllipsisText>
|
||||
</template>
|
@ -238,6 +238,7 @@ const defaultPreferences: Preferences = {
|
||||
},
|
||||
logo: {
|
||||
enable: true,
|
||||
fit: 'contain',
|
||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
@ -431,6 +432,8 @@ interface HeaderPreferences {
|
||||
interface LogoPreferences {
|
||||
/** Whether the logo is visible */
|
||||
enable: boolean;
|
||||
/** Logo image fitting method */
|
||||
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
/** Logo URL */
|
||||
source: string;
|
||||
}
|
||||
|
@ -237,6 +237,7 @@ const defaultPreferences: Preferences = {
|
||||
},
|
||||
logo: {
|
||||
enable: true,
|
||||
fit: 'contain',
|
||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
@ -431,6 +432,8 @@ interface HeaderPreferences {
|
||||
interface LogoPreferences {
|
||||
/** logo是否可见 */
|
||||
enable: boolean;
|
||||
/** logo图片适应方式 */
|
||||
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
/** logo地址 */
|
||||
source: string;
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
||||
},
|
||||
"logo": {
|
||||
"enable": true,
|
||||
"fit": "contain",
|
||||
"source": "https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp",
|
||||
},
|
||||
"navigation": {
|
||||
|
@ -62,6 +62,7 @@ const defaultPreferences: Preferences = {
|
||||
|
||||
logo: {
|
||||
enable: true,
|
||||
fit: 'contain',
|
||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
|
@ -134,6 +134,8 @@ interface HeaderPreferences {
|
||||
interface LogoPreferences {
|
||||
/** logo是否可见 */
|
||||
enable: boolean;
|
||||
/** logo图片适应方式 */
|
||||
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
/** logo地址 */
|
||||
source: string;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import { createContext } from '@vben-core/shadcn-ui';
|
||||
import { isString, mergeWithArrayOverride, set } from '@vben-core/shared/utils';
|
||||
|
||||
import { useForm } from 'vee-validate';
|
||||
import { object } from 'zod';
|
||||
import { object, ZodIntersection, ZodNumber, ZodObject, ZodString } from 'zod';
|
||||
import { getDefaultsForSchema } from 'zod-defaults';
|
||||
|
||||
type ExtendFormProps = VbenFormProps & { formApi: ExtendedFormApi };
|
||||
@ -52,7 +52,12 @@ export function useFormInitial(
|
||||
if (Reflect.has(item, 'defaultValue')) {
|
||||
set(initialValues, item.fieldName, item.defaultValue);
|
||||
} else if (item.rules && !isString(item.rules)) {
|
||||
// 检查规则是否适合提取默认值
|
||||
const customDefaultValue = getCustomDefaultValue(item.rules);
|
||||
zodObject[item.fieldName] = item.rules;
|
||||
if (customDefaultValue !== undefined) {
|
||||
initialValues[item.fieldName] = customDefaultValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -64,6 +69,38 @@ export function useFormInitial(
|
||||
}
|
||||
return mergeWithArrayOverride(initialValues, zodDefaults);
|
||||
}
|
||||
// 自定义默认值提取逻辑
|
||||
function getCustomDefaultValue(rule: any): any {
|
||||
if (rule instanceof ZodString) {
|
||||
return ''; // 默认为空字符串
|
||||
} else if (rule instanceof ZodNumber) {
|
||||
return null; // 默认为 null(避免显示 0)
|
||||
} else if (rule instanceof ZodObject) {
|
||||
// 递归提取嵌套对象的默认值
|
||||
const defaultValues: Record<string, any> = {};
|
||||
for (const [key, valueSchema] of Object.entries(rule.shape)) {
|
||||
defaultValues[key] = getCustomDefaultValue(valueSchema);
|
||||
}
|
||||
return defaultValues;
|
||||
} else if (rule instanceof ZodIntersection) {
|
||||
// 对于交集类型,从schema 提取默认值
|
||||
const leftDefaultValue = getCustomDefaultValue(rule._def.left);
|
||||
const rightDefaultValue = getCustomDefaultValue(rule._def.right);
|
||||
|
||||
// 如果左右两边都能提取默认值,合并它们
|
||||
if (
|
||||
typeof leftDefaultValue === 'object' &&
|
||||
typeof rightDefaultValue === 'object'
|
||||
) {
|
||||
return { ...leftDefaultValue, ...rightDefaultValue };
|
||||
}
|
||||
|
||||
// 否则优先使用左边的默认值
|
||||
return leftDefaultValue ?? rightDefaultValue;
|
||||
} else {
|
||||
return undefined; // 其他类型不提供默认值
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
delegatedSlots,
|
||||
|
@ -5,6 +5,8 @@ import type {
|
||||
AvatarRootProps,
|
||||
} from 'radix-vue';
|
||||
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
|
||||
import { computed } from 'vue';
|
||||
@ -16,6 +18,7 @@ interface Props extends AvatarFallbackProps, AvatarImageProps, AvatarRootProps {
|
||||
class?: ClassType;
|
||||
dot?: boolean;
|
||||
dotClass?: ClassType;
|
||||
fit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
size?: number;
|
||||
}
|
||||
|
||||
@ -28,6 +31,15 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
as: 'button',
|
||||
dot: false,
|
||||
dotClass: 'bg-green-500',
|
||||
fit: 'cover',
|
||||
});
|
||||
|
||||
const imageStyle = computed<CSSProperties>(() => {
|
||||
const { fit } = props;
|
||||
if (fit) {
|
||||
return { objectFit: fit };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const text = computed(() => {
|
||||
@ -51,7 +63,7 @@ const rootStyle = computed(() => {
|
||||
class="relative flex flex-shrink-0 items-center"
|
||||
>
|
||||
<Avatar :class="props.class" class="size-full">
|
||||
<AvatarImage :alt="alt" :src="src" />
|
||||
<AvatarImage :alt="alt" :src="src" :style="imageStyle" />
|
||||
<AvatarFallback>{{ text }}</AvatarFallback>
|
||||
</Avatar>
|
||||
<span
|
||||
|
@ -6,6 +6,10 @@ interface Props {
|
||||
* @zh_CN 是否收起文本
|
||||
*/
|
||||
collapsed?: boolean;
|
||||
/**
|
||||
* @zh_CN Logo 图片适应方式
|
||||
*/
|
||||
fit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
/**
|
||||
* @zh_CN Logo 跳转地址
|
||||
*/
|
||||
@ -38,6 +42,7 @@ withDefaults(defineProps<Props>(), {
|
||||
logoSize: 32,
|
||||
src: '',
|
||||
theme: 'light',
|
||||
fit: 'cover',
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -53,6 +58,7 @@ withDefaults(defineProps<Props>(), {
|
||||
:alt="text"
|
||||
:src="src"
|
||||
:size="logoSize"
|
||||
:fit="fit"
|
||||
class="relative rounded-none bg-transparent"
|
||||
/>
|
||||
<template v-if="!collapsed">
|
||||
|
@ -1,7 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import { computed, ref, watchEffect } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
onUpdated,
|
||||
ref,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
|
||||
import { VbenTooltip } from '@vben-core/shadcn-ui';
|
||||
|
||||
@ -33,6 +40,16 @@ interface Props {
|
||||
* @default true
|
||||
*/
|
||||
tooltip?: boolean;
|
||||
/**
|
||||
* 是否只在文本被截断时显示提示框
|
||||
* @default false
|
||||
*/
|
||||
tooltipWhenEllipsis?: boolean;
|
||||
/**
|
||||
* 文本截断检测的像素差异阈值,越大则判断越严格
|
||||
* @default 3
|
||||
*/
|
||||
ellipsisThreshold?: number;
|
||||
/**
|
||||
* 提示框背景颜色,优先级高于 overlayStyle
|
||||
*/
|
||||
@ -62,12 +79,15 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
maxWidth: '100%',
|
||||
placement: 'top',
|
||||
tooltip: true,
|
||||
tooltipWhenEllipsis: false,
|
||||
ellipsisThreshold: 3,
|
||||
tooltipBackgroundColor: '',
|
||||
tooltipColor: '',
|
||||
tooltipFontSize: 14,
|
||||
tooltipMaxWidth: undefined,
|
||||
tooltipOverlayStyle: () => ({ textAlign: 'justify' }),
|
||||
});
|
||||
|
||||
const emit = defineEmits<{ expandChange: [boolean] }>();
|
||||
|
||||
const textMaxWidth = computed(() => {
|
||||
@ -79,9 +99,67 @@ const textMaxWidth = computed(() => {
|
||||
const ellipsis = ref();
|
||||
const isExpand = ref(false);
|
||||
const defaultTooltipMaxWidth = ref();
|
||||
const isEllipsis = ref(false);
|
||||
|
||||
const { width: eleWidth } = useElementSize(ellipsis);
|
||||
|
||||
// 检测文本是否被截断
|
||||
const checkEllipsis = () => {
|
||||
if (!ellipsis.value || !props.tooltipWhenEllipsis) return;
|
||||
|
||||
const element = ellipsis.value;
|
||||
|
||||
const originalText = element.textContent || '';
|
||||
const originalTrimmed = originalText.trim();
|
||||
|
||||
// 对于空文本直接返回 false
|
||||
if (!originalTrimmed) {
|
||||
isEllipsis.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const widthDiff = element.scrollWidth - element.clientWidth;
|
||||
const heightDiff = element.scrollHeight - element.clientHeight;
|
||||
|
||||
// 使用足够大的差异阈值确保只有真正被截断的文本才会显示 tooltip
|
||||
isEllipsis.value =
|
||||
props.line === 1
|
||||
? widthDiff > props.ellipsisThreshold
|
||||
: heightDiff > props.ellipsisThreshold;
|
||||
};
|
||||
|
||||
// 使用 ResizeObserver 监听尺寸变化
|
||||
let resizeObserver: null | ResizeObserver = null;
|
||||
|
||||
onMounted(() => {
|
||||
if (typeof ResizeObserver !== 'undefined' && props.tooltipWhenEllipsis) {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
checkEllipsis();
|
||||
});
|
||||
|
||||
if (ellipsis.value) {
|
||||
resizeObserver.observe(ellipsis.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始检测
|
||||
checkEllipsis();
|
||||
});
|
||||
|
||||
// 使用onUpdated钩子检测内容变化
|
||||
onUpdated(() => {
|
||||
if (props.tooltipWhenEllipsis) {
|
||||
checkEllipsis();
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
resizeObserver = null;
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(
|
||||
() => {
|
||||
if (props.tooltip && eleWidth.value) {
|
||||
@ -91,9 +169,13 @@ watchEffect(
|
||||
},
|
||||
{ flush: 'post' },
|
||||
);
|
||||
|
||||
function onExpand() {
|
||||
isExpand.value = !isExpand.value;
|
||||
emit('expandChange', isExpand.value);
|
||||
if (props.tooltipWhenEllipsis) {
|
||||
checkEllipsis();
|
||||
}
|
||||
}
|
||||
|
||||
function handleExpand() {
|
||||
@ -110,7 +192,9 @@ function handleExpand() {
|
||||
color: tooltipColor,
|
||||
backgroundColor: tooltipBackgroundColor,
|
||||
}"
|
||||
:disabled="!props.tooltip || isExpand"
|
||||
:disabled="
|
||||
!props.tooltip || isExpand || (props.tooltipWhenEllipsis && !isEllipsis)
|
||||
"
|
||||
:side="placement"
|
||||
>
|
||||
<slot name="tooltip">
|
||||
|
@ -234,6 +234,7 @@ const headerSlots = computed(() => {
|
||||
<template #logo>
|
||||
<VbenLogo
|
||||
v-if="preferences.logo.enable"
|
||||
:fit="preferences.logo.fit"
|
||||
:class="logoClass"
|
||||
:collapsed="logoCollapsed"
|
||||
:src="preferences.logo.source"
|
||||
@ -324,6 +325,7 @@ const headerSlots = computed(() => {
|
||||
<template #side-extra-title>
|
||||
<VbenLogo
|
||||
v-if="preferences.logo.enable"
|
||||
:fit="preferences.logo.fit"
|
||||
:text="preferences.app.name"
|
||||
:theme="theme"
|
||||
>
|
||||
|
@ -4,7 +4,7 @@ import { useVbenModal } from '@vben-core/popup-ui';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
interface Props {
|
||||
// 轮训时间,分钟
|
||||
// 轮询时间,分钟
|
||||
checkUpdatesInterval?: number;
|
||||
// 检查更新的地址
|
||||
checkUpdateUrl?: string;
|
||||
@ -44,6 +44,7 @@ async function getVersionTag() {
|
||||
const response = await fetch(props.checkUpdateUrl, {
|
||||
cache: 'no-cache',
|
||||
method: 'HEAD',
|
||||
redirect: 'manual',
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -48,8 +48,12 @@
|
||||
"@vueuse/core": "catalog:",
|
||||
"ant-design-vue": "catalog:",
|
||||
"dayjs": "catalog:",
|
||||
"json-bigint": "catalog:",
|
||||
"pinia": "catalog:",
|
||||
"vue": "catalog:",
|
||||
"vue-router": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/json-bigint": "catalog:"
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ import type { ComponentType } from './component';
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
setupVbenForm<ComponentType>({
|
||||
async function initSetupVbenForm() {
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
@ -36,10 +37,11 @@ setupVbenForm<ComponentType>({
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
|
10
playground/src/api/examples/json-bigint.ts
Normal file
10
playground/src/api/examples/json-bigint.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*/
|
||||
async function getBigIntData() {
|
||||
return requestClient.get('/demo/bigint');
|
||||
}
|
||||
|
||||
export { getBigIntData };
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 该文件可自行根据业务逻辑进行调整
|
||||
*/
|
||||
import type { RequestClientOptions } from '@vben/request';
|
||||
import type { AxiosResponseHeaders, RequestClientOptions } from '@vben/request';
|
||||
|
||||
import { useAppConfig } from '@vben/hooks';
|
||||
import { preferences } from '@vben/preferences';
|
||||
@ -12,8 +12,10 @@ import {
|
||||
RequestClient,
|
||||
} from '@vben/request';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import JSONBigInt from 'json-bigint';
|
||||
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
@ -25,6 +27,14 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
|
||||
const client = new RequestClient({
|
||||
...options,
|
||||
baseURL,
|
||||
transformResponse: (data: any, header: AxiosResponseHeaders) => {
|
||||
// storeAsString指示将BigInt存储为字符串,设为false则会存储为内置的BigInt类型
|
||||
return header.getContentType()?.toString().includes('application/json')
|
||||
? cloneDeep(
|
||||
JSONBigInt({ storeAsString: true, strict: true }).parse(data),
|
||||
)
|
||||
: data;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -13,12 +13,16 @@ import { $t, setupI18n } from '#/locales';
|
||||
import { router } from '#/router';
|
||||
|
||||
import { initComponentAdapter } from './adapter/component';
|
||||
import { initSetupVbenForm } from './adapter/form';
|
||||
import App from './app.vue';
|
||||
|
||||
async function bootstrap(namespace: string) {
|
||||
// 初始化组件适配器
|
||||
await initComponentAdapter();
|
||||
|
||||
// 初始化表单组件
|
||||
await initSetupVbenForm();
|
||||
|
||||
// 设置弹窗的默认配置
|
||||
// setDefaultModalProps({
|
||||
// fullscreenButton: false,
|
||||
|
@ -255,6 +255,16 @@ const routes: RouteRecordRaw[] = [
|
||||
title: $t('demos.features.requestParamsSerializer'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'BigIntDemo',
|
||||
path: '/demos/features/json-bigint',
|
||||
component: () =>
|
||||
import('#/views/demos/features/json-bigint/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:grape',
|
||||
title: 'JSON BigInt',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 面包屑导航
|
||||
|
39
playground/src/views/demos/features/json-bigint/index.vue
Normal file
39
playground/src/views/demos/features/json-bigint/index.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Alert, Button, Card } from 'ant-design-vue';
|
||||
|
||||
import { getBigIntData } from '#/api/examples/json-bigint';
|
||||
|
||||
const response = ref('');
|
||||
function fetchData() {
|
||||
getBigIntData().then((res) => {
|
||||
response.value = res;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Page
|
||||
title="JSON BigInt Support"
|
||||
description="解析后端返回的长整数(long/bigInt)。代码位置:playground/src/api/request.ts中的transformResponse"
|
||||
>
|
||||
<Card>
|
||||
<Alert>
|
||||
<template #message>
|
||||
有些后端接口返回的ID是长整数,但javascript原生的JSON解析是不支持超过2^53-1的长整数的。
|
||||
这种情况可以建议后端返回数据前将长整数转换为字符串类型。如果后端不接受我们的建议😡……
|
||||
<br />
|
||||
下面的按钮点击后会发起请求,接口返回的JSON数据中的id字段是超出整数范围的数字,已自动将其解析为字符串
|
||||
</template>
|
||||
</Alert>
|
||||
<Button class="mt-4" type="primary" @click="fetchData">发起请求</Button>
|
||||
<div>
|
||||
<pre>
|
||||
{{ response }}
|
||||
</pre>
|
||||
</div>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
@ -41,6 +41,7 @@ catalog:
|
||||
'@types/archiver': ^6.0.3
|
||||
'@types/eslint': ^9.6.1
|
||||
'@types/html-minifier-terser': ^7.0.2
|
||||
'@types/json-bigint': ^1.0.4
|
||||
'@types/jsonwebtoken': ^9.0.9
|
||||
'@types/lodash.clonedeep': ^4.5.9
|
||||
'@types/lodash.get': ^4.4.9
|
||||
@ -112,6 +113,7 @@ catalog:
|
||||
happy-dom: ^17.4.6
|
||||
html-minifier-terser: ^7.2.0
|
||||
is-ci: ^4.1.0
|
||||
json-bigint: ^1.0.0
|
||||
jsonc-eslint-parser: ^2.4.0
|
||||
jsonwebtoken: ^9.0.2
|
||||
lefthook: ^1.11.12
|
||||
|
Loading…
Reference in New Issue
Block a user