Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
This commit is contained in:
commit
379778412b
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/docs",
|
"name": "@vben/docs",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vitepress build",
|
"build": "vitepress build",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/commitlint-config",
|
"name": "@vben/commitlint-config",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/stylelint-config",
|
"name": "@vben/stylelint-config",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/node-utils",
|
"name": "@vben/node-utils",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/tailwind-config",
|
"name": "@vben/tailwind-config",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/tsconfig",
|
"name": "@vben/tsconfig",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/vite-config",
|
"name": "@vben/vite-config",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vben-admin-monorepo",
|
"name": "vben-admin-monorepo",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"monorepo",
|
"monorepo",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/design",
|
"name": "@vben-core/design",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/icons",
|
"name": "@vben-core/icons",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/shared",
|
"name": "@vben-core/shared",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/typings",
|
"name": "@vben-core/typings",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/composables",
|
"name": "@vben-core/composables",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/preferences",
|
"name": "@vben-core/preferences",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/form-ui",
|
"name": "@vben-core/form-ui",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/layout-ui",
|
"name": "@vben-core/layout-ui",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/menu-ui",
|
"name": "@vben-core/menu-ui",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/shadcn-ui",
|
"name": "@vben-core/shadcn-ui",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"#main": "./dist/index.mjs",
|
"#main": "./dist/index.mjs",
|
||||||
"#module": "./dist/index.mjs",
|
"#module": "./dist/index.mjs",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/tabs-ui",
|
"name": "@vben-core/tabs-ui",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/constants",
|
"name": "@vben/constants",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/access",
|
"name": "@vben/access",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/common-ui",
|
"name": "@vben/common-ui",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { VNode } from 'vue';
|
import type { VNode } from 'vue';
|
||||||
|
|
||||||
|
import { computed, ref, watch, watchEffect } from 'vue';
|
||||||
|
|
||||||
import { usePagination } from '@vben/hooks';
|
import { usePagination } from '@vben/hooks';
|
||||||
import { EmptyIcon, Grip, listIcons } from '@vben/icons';
|
import { EmptyIcon, Grip, listIcons } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Input,
|
||||||
Pagination,
|
Pagination,
|
||||||
PaginationEllipsis,
|
PaginationEllipsis,
|
||||||
PaginationFirst,
|
PaginationFirst,
|
||||||
@ -18,12 +22,17 @@ import {
|
|||||||
VbenIconButton,
|
VbenIconButton,
|
||||||
VbenPopover,
|
VbenPopover,
|
||||||
} from '@vben-core/shadcn-ui';
|
} from '@vben-core/shadcn-ui';
|
||||||
import { refDebounced } from '@vueuse/core';
|
|
||||||
import { computed, h, ref, watch, watchEffect } from 'vue';
|
import { refDebounced, watchDebounced } from '@vueuse/core';
|
||||||
|
|
||||||
|
import { fetchIconsData } from './icons';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
|
/** 图标集的名字 */
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
|
/** 是否自动请求API以获得图标集的数据.提供prefix时有效 */
|
||||||
|
autoFetchApi?: boolean;
|
||||||
/**
|
/**
|
||||||
* 图标列表
|
* 图标列表
|
||||||
*/
|
*/
|
||||||
@ -36,16 +45,19 @@ interface Props {
|
|||||||
modelValueProp?: string;
|
modelValueProp?: string;
|
||||||
/** 图标样式 */
|
/** 图标样式 */
|
||||||
iconClass?: string;
|
iconClass?: string;
|
||||||
|
type?: 'icon' | 'input';
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
prefix: 'ant-design',
|
prefix: 'ant-design',
|
||||||
pageSize: 36,
|
pageSize: 36,
|
||||||
icons: () => [],
|
icons: () => [],
|
||||||
inputComponent: () => h('div'),
|
|
||||||
iconSlot: 'default',
|
iconSlot: 'default',
|
||||||
iconClass: 'size-4',
|
iconClass: 'size-4',
|
||||||
modelValueProp: 'value',
|
autoFetchApi: true,
|
||||||
|
modelValueProp: 'modelValue',
|
||||||
|
inputComponent: undefined,
|
||||||
|
type: 'input',
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@ -59,9 +71,28 @@ const currentSelect = ref('');
|
|||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const keyword = ref('');
|
const keyword = ref('');
|
||||||
const keywordDebounce = refDebounced(keyword, 300);
|
const keywordDebounce = refDebounced(keyword, 300);
|
||||||
|
const innerIcons = ref<string[]>([]);
|
||||||
|
|
||||||
|
watchDebounced(
|
||||||
|
() => props.prefix,
|
||||||
|
async (prefix) => {
|
||||||
|
if (prefix && prefix !== 'svg' && props.autoFetchApi) {
|
||||||
|
innerIcons.value = await fetchIconsData(prefix);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, debounce: 500, maxWait: 1000 },
|
||||||
|
);
|
||||||
|
|
||||||
const currentList = computed(() => {
|
const currentList = computed(() => {
|
||||||
try {
|
try {
|
||||||
if (props.prefix) {
|
if (props.prefix) {
|
||||||
|
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) {
|
if (icons.length === 0) {
|
||||||
console.warn(`No icons found for prefix: ${props.prefix}`);
|
console.warn(`No icons found for prefix: ${props.prefix}`);
|
||||||
@ -143,18 +174,61 @@ defineExpose({ toggleOpenState, open, close });
|
|||||||
content-class="p-0 pt-3"
|
content-class="p-0 pt-3"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<component
|
<template v-if="props.type === 'input'">
|
||||||
:is="inputComponent"
|
<component
|
||||||
:[modelValueProp]="currentSelect"
|
v-if="props.inputComponent"
|
||||||
:placeholder="$t('ui.iconPicker.placeholder')"
|
:is="inputComponent"
|
||||||
>
|
:[modelValueProp]="currentSelect"
|
||||||
<template #[iconSlot]>
|
:placeholder="$t('ui.iconPicker.placeholder')"
|
||||||
<VbenIcon :icon="currentSelect || Grip" class="size-4" />
|
role="combobox"
|
||||||
</template>
|
:aria-label="$t('ui.iconPicker.placeholder')"
|
||||||
</component>
|
aria-expanded="visible"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<template #[iconSlot]>
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
<VbenIcon
|
||||||
|
:icon="currentSelect || Grip"
|
||||||
|
v-else
|
||||||
|
class="size-4"
|
||||||
|
v-bind="$attrs"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div class="mb-2 flex w-full">
|
<div class="mb-2 flex w-full">
|
||||||
<component :is="inputComponent" v-bind="searchInputProps" />
|
<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"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="paginationList.length > 0">
|
<template v-if="paginationList.length > 0">
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个缓存对象,在不刷新页面时,无需重复请求远程接口
|
||||||
|
*/
|
||||||
|
export const ICONS_MAP: Recordable<string[]> = {};
|
||||||
|
|
||||||
|
interface IconifyResponse {
|
||||||
|
prefix: string;
|
||||||
|
total: number;
|
||||||
|
title: string;
|
||||||
|
uncategorized?: string[];
|
||||||
|
categories?: Recordable<string[]>;
|
||||||
|
aliases?: Recordable<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PENDING_REQUESTS: Recordable<Promise<string[]>> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过Iconify接口获取图标集数据。
|
||||||
|
* 同一时间多个图标选择器同时请求同一个图标集时,实际上只会发起一次请求(所有请求共享同一份结果)。
|
||||||
|
* 请求结果会被缓存,刷新页面前同一个图标集不会再次请求
|
||||||
|
* @param prefix 图标集名称
|
||||||
|
* @returns 图标集中包含的所有图标名称
|
||||||
|
*/
|
||||||
|
export async function fetchIconsData(prefix: string): Promise<string[]> {
|
||||||
|
if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) {
|
||||||
|
return ICONS_MAP[prefix];
|
||||||
|
}
|
||||||
|
if (Reflect.has(PENDING_REQUESTS, prefix) && PENDING_REQUESTS[prefix]) {
|
||||||
|
return PENDING_REQUESTS[prefix];
|
||||||
|
}
|
||||||
|
PENDING_REQUESTS[prefix] = (async () => {
|
||||||
|
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());
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
const list = response.uncategorized || [];
|
||||||
|
if (response.categories) {
|
||||||
|
for (const category in response.categories) {
|
||||||
|
list.push(...(response.categories[category] || []));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ICONS_MAP[prefix] = list.map((v) => `${prefix}:${v}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch icons for prefix ${prefix}:`, error);
|
||||||
|
return [] as string[];
|
||||||
|
}
|
||||||
|
return ICONS_MAP[prefix];
|
||||||
|
})();
|
||||||
|
return PENDING_REQUESTS[prefix];
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/hooks",
|
"name": "@vben/hooks",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/layouts",
|
"name": "@vben/layouts",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/plugins",
|
"name": "@vben/plugins",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/request",
|
"name": "@vben/request",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -31,6 +31,7 @@ describe('fileDownloader', () => {
|
|||||||
expect(result).toEqual(mockBlob);
|
expect(result).toEqual(mockBlob);
|
||||||
expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, {
|
expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, {
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
|
responseReturn: 'body',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -51,6 +52,7 @@ describe('fileDownloader', () => {
|
|||||||
expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, {
|
expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, {
|
||||||
...customConfig,
|
...customConfig,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
|
responseReturn: 'body',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
import type { AxiosRequestConfig } from 'axios';
|
|
||||||
|
|
||||||
import type { RequestClient } from '../request-client';
|
import type { RequestClient } from '../request-client';
|
||||||
import type { RequestResponse } from '../types';
|
import type { RequestClientConfig } from '../types';
|
||||||
|
|
||||||
|
type DownloadRequestConfig = {
|
||||||
|
/**
|
||||||
|
* 定义期望获得的数据类型。
|
||||||
|
* raw: 原始的AxiosResponse,包括headers、status等。
|
||||||
|
* body: 只返回响应数据的BODY部分(Blob)
|
||||||
|
*/
|
||||||
|
responseReturn?: 'body' | 'raw';
|
||||||
|
} & Omit<RequestClientConfig, 'responseReturn'>;
|
||||||
|
|
||||||
class FileDownloader {
|
class FileDownloader {
|
||||||
private client: RequestClient;
|
private client: RequestClient;
|
||||||
@ -9,20 +16,23 @@ class FileDownloader {
|
|||||||
constructor(client: RequestClient) {
|
constructor(client: RequestClient) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
public async download(
|
* 下载文件
|
||||||
|
* @param url 文件的完整链接
|
||||||
|
* @param config 配置信息,可选。
|
||||||
|
* @returns 如果config.responseReturn为'body',则返回Blob(默认),否则返回RequestResponse<Blob>
|
||||||
|
*/
|
||||||
|
public async download<T = Blob>(
|
||||||
url: string,
|
url: string,
|
||||||
config?: AxiosRequestConfig,
|
config?: DownloadRequestConfig,
|
||||||
): Promise<RequestResponse<Blob>> {
|
): Promise<T> {
|
||||||
const finalConfig: AxiosRequestConfig = {
|
const finalConfig: DownloadRequestConfig = {
|
||||||
|
responseReturn: 'body',
|
||||||
...config,
|
...config,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await this.client.get<RequestResponse<Blob>>(
|
const response = await this.client.get<T>(url, finalConfig);
|
||||||
url,
|
|
||||||
finalConfig,
|
|
||||||
);
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
||||||
|
|
||||||
import type { RequestClient } from '../request-client';
|
import type { RequestClient } from '../request-client';
|
||||||
|
import type { RequestClientConfig } from '../types';
|
||||||
|
|
||||||
class FileUploader {
|
class FileUploader {
|
||||||
private client: RequestClient;
|
private client: RequestClient;
|
||||||
@ -9,18 +8,18 @@ class FileUploader {
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async upload(
|
public async upload<T = any>(
|
||||||
url: string,
|
url: string,
|
||||||
data: { file: Blob | File } & Record<string, any>,
|
data: Record<string, any> & { file: Blob | File },
|
||||||
config?: AxiosRequestConfig,
|
config?: RequestClientConfig,
|
||||||
): Promise<AxiosResponse> {
|
): Promise<T> {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
Object.entries(data).forEach(([key, value]) => {
|
Object.entries(data).forEach(([key, value]) => {
|
||||||
formData.append(key, value);
|
formData.append(key, value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const finalConfig: AxiosRequestConfig = {
|
const finalConfig: RequestClientConfig = {
|
||||||
...config,
|
...config,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
|
@ -25,15 +25,15 @@ export const defaultResponseInterceptor = ({
|
|||||||
if (config.responseReturn === 'raw') {
|
if (config.responseReturn === 'raw') {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
const code = responseData[codeField];
|
|
||||||
if (
|
if (status >= 200 && status < 400) {
|
||||||
status >= 200 && status < 400 && isFunction(successCode)
|
|
||||||
? successCode(code)
|
|
||||||
: code === successCode
|
|
||||||
) {
|
|
||||||
if (config.responseReturn === 'body') {
|
if (config.responseReturn === 'body') {
|
||||||
return responseData;
|
return responseData;
|
||||||
} else {
|
} else if (
|
||||||
|
isFunction(successCode)
|
||||||
|
? successCode(responseData[codeField])
|
||||||
|
: responseData[codeField] === successCode
|
||||||
|
) {
|
||||||
return isFunction(dataField)
|
return isFunction(dataField)
|
||||||
? dataField(responseData)
|
? dataField(responseData)
|
||||||
: responseData[dataField];
|
: responseData[dataField];
|
||||||
|
@ -7,9 +7,9 @@ import type {
|
|||||||
|
|
||||||
type ExtendOptions = {
|
type ExtendOptions = {
|
||||||
/** 响应数据的返回方式。
|
/** 响应数据的返回方式。
|
||||||
* raw: 原始的AxiosResponse,包括headers、status等。
|
* raw: 原始的AxiosResponse,包括headers、status等,不做是否成功请求的检查。
|
||||||
* body: 返回响应数据的BODY部分。
|
* body: 返回响应数据的BODY部分(只会根据status检查请求是否成功,忽略对code的判断,这种情况下应由调用方检查请求是否成功)。
|
||||||
* data: 解构响应的BODY数据,只返回其中的data节点数据。
|
* data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。
|
||||||
*/
|
*/
|
||||||
responseReturn?: 'body' | 'data' | 'raw';
|
responseReturn?: 'body' | 'data' | 'raw';
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/icons",
|
"name": "@vben/icons",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/locales",
|
"name": "@vben/locales",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/preferences",
|
"name": "@vben/preferences",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/stores",
|
"name": "@vben/stores",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/styles",
|
"name": "@vben/styles",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/types",
|
"name": "@vben/types",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/utils",
|
"name": "@vben/utils",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/playground",
|
"name": "@vben/playground",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
28
playground/src/api/examples/download.ts
Normal file
28
playground/src/api/examples/download.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import type { RequestResponse } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '../request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件,获取Blob
|
||||||
|
* @returns Blob
|
||||||
|
*/
|
||||||
|
async function downloadFile1() {
|
||||||
|
return requestClient.download<Blob>(
|
||||||
|
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件,获取完整的Response
|
||||||
|
* @returns RequestResponse<Blob>
|
||||||
|
*/
|
||||||
|
async function downloadFile2() {
|
||||||
|
return requestClient.download<RequestResponse<Blob>>(
|
||||||
|
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||||
|
{
|
||||||
|
responseReturn: 'raw',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { downloadFile1, downloadFile2 };
|
@ -1,4 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
import {
|
import {
|
||||||
downloadFileFromBase64,
|
downloadFileFromBase64,
|
||||||
@ -9,7 +11,23 @@ import {
|
|||||||
|
|
||||||
import { Button, Card } from 'ant-design-vue';
|
import { Button, Card } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { downloadFile1, downloadFile2 } from '#/api/examples/download';
|
||||||
|
|
||||||
import imageBase64 from './base64';
|
import imageBase64 from './base64';
|
||||||
|
|
||||||
|
const downloadResult = ref('');
|
||||||
|
|
||||||
|
function getBlob() {
|
||||||
|
downloadFile1().then((res) => {
|
||||||
|
downloadResult.value = `获取Blob成功,长度:${res.size}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResponse() {
|
||||||
|
downloadFile2().then((res) => {
|
||||||
|
downloadResult.value = `获取Response成功,headers:${JSON.stringify(res.headers)},长度:${res.data.size}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -70,5 +88,13 @@ import imageBase64 from './base64';
|
|||||||
Download TxT
|
Download TxT
|
||||||
</Button>
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<Card class="my-5" title="Request download">
|
||||||
|
<Button type="primary" @click="getBlob"> 获取Blob </Button>
|
||||||
|
<Button type="primary" class="ml-4" @click="getResponse">
|
||||||
|
获取Response
|
||||||
|
</Button>
|
||||||
|
<div class="mt-4">{{ downloadResult }}</div>
|
||||||
|
</Card>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
@ -20,7 +20,10 @@ import {
|
|||||||
|
|
||||||
import { Card, Input } from 'ant-design-vue';
|
import { Card, Input } from 'ant-design-vue';
|
||||||
|
|
||||||
const iconValue = ref('ant-design:trademark-outlined');
|
const iconValue1 = ref('ant-design:trademark-outlined');
|
||||||
|
const iconValue2 = ref('svg:avatar-1');
|
||||||
|
const iconValue3 = ref('mdi:alien-outline');
|
||||||
|
const iconValue4 = ref('mdi-light:book-multiple');
|
||||||
|
|
||||||
const inputComponent = h(Input);
|
const inputComponent = h(Input);
|
||||||
</script>
|
</script>
|
||||||
@ -78,26 +81,32 @@ const inputComponent = h(Input);
|
|||||||
<Card class="mb-5" title="图标选择器">
|
<Card class="mb-5" title="图标选择器">
|
||||||
<div class="mb-5 flex items-center gap-5">
|
<div class="mb-5 flex items-center gap-5">
|
||||||
<span>原始样式(Iconify):</span>
|
<span>原始样式(Iconify):</span>
|
||||||
<IconPicker class="w-[200px]" />
|
<IconPicker v-model="iconValue1" class="w-[200px]" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-5 flex items-center gap-5">
|
<div class="mb-5 flex items-center gap-5">
|
||||||
<span>原始样式(svg):</span>
|
<span>原始样式(svg):</span>
|
||||||
<IconPicker class="w-[200px]" prefix="svg" />
|
<IconPicker v-model="iconValue2" class="w-[200px]" prefix="svg" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-5 flex items-center gap-5">
|
<div class="mb-5 flex items-center gap-5">
|
||||||
<span>使用Input:</span>
|
<span>自定义Input:</span>
|
||||||
<IconPicker :input-component="inputComponent" icon-slot="addonAfter" />
|
<IconPicker
|
||||||
|
:input-component="inputComponent"
|
||||||
|
v-model="iconValue3"
|
||||||
|
icon-slot="addonAfter"
|
||||||
|
model-value-prop="value"
|
||||||
|
prefix="mdi"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-5">
|
<div class="flex items-center gap-5">
|
||||||
<span>可手动输入,只能点击图标打开弹窗:</span>
|
<span>显示为一个Icon:</span>
|
||||||
<Input
|
<Input
|
||||||
v-model:value="iconValue"
|
v-model:value="iconValue4"
|
||||||
allow-clear
|
allow-clear
|
||||||
placeholder="点击这里选择图标"
|
placeholder="点击这里选择图标"
|
||||||
style="width: 300px"
|
style="width: 300px"
|
||||||
>
|
>
|
||||||
<template #addonAfter>
|
<template #addonAfter>
|
||||||
<IconPicker v-model="iconValue" class="w-[200px]" />
|
<IconPicker v-model="iconValue4" prefix="mdi-light" type="icon" />
|
||||||
</template>
|
</template>
|
||||||
</Input>
|
</Input>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/turbo-run",
|
"name": "@vben/turbo-run",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/vsh",
|
"name": "@vben/vsh",
|
||||||
"version": "5.5.2",
|
"version": "5.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
Loading…
Reference in New Issue
Block a user