This commit is contained in:
dap
2024-12-05 08:06:55 +08:00
101 changed files with 878 additions and 1170 deletions

View File

@@ -62,7 +62,7 @@ body:
description: Before submitting the issue, please make sure you do the following description: Before submitting the issue, please make sure you do the following
# description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com). # description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com).
options: options:
- label: Read the [docs](https://anncwb.github.io/vue-vben-admin-doc/) - label: Read the [docs](https://doc.vben.pro/)
required: true required: true
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version) - label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
required: true required: true

View File

@@ -62,7 +62,7 @@ body:
label: Validations label: Validations
description: Before submitting the issue, please make sure you do the following description: Before submitting the issue, please make sure you do the following
options: options:
- label: Read the [docs](https://anncwb.github.io/vue-vben-admin-doc/) - label: Read the [docs](https://doc.vben.pro/)
required: true required: true
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version) - label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
required: true required: true

View File

@@ -1,3 +1,15 @@
# 1.1.3
**Bug Fixes**
- 节点树在编辑 & 空数组(不勾选)情况 勾选节点会造成watch延迟触发 导致会带上父节点id造成id重复
- 节点树在节点独立情况下的控制台warning: Invalid prop: type check failed for prop "value". Expected Array, got Object
**Others**
- 角色管理 优化Drawer布局
- unplugin-vue-components插件(默认未开启) 需要排除Button组件 全局已经默认导入了
# 1.1.2 # 1.1.2
**Features** **Features**

View File

@@ -8,7 +8,7 @@ import type { BaseFormComponentType } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue'; import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { globalShareState } from '@vben/common-ui'; import { ApiSelect, globalShareState, IconPicker } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { import {
@@ -51,6 +51,7 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType = export type ComponentType =
| 'ApiSelect'
| 'AutoComplete' | 'AutoComplete'
| 'Checkbox' | 'Checkbox'
| 'CheckboxGroup' | 'CheckboxGroup'
@@ -58,6 +59,7 @@ export type ComponentType =
| 'DefaultButton' | 'DefaultButton'
| 'Divider' | 'Divider'
| 'FileUpload' | 'FileUpload'
| 'IconPicker'
| 'ImageUpload' | 'ImageUpload'
| 'Input' | 'Input'
| 'InputNumber' | 'InputNumber'
@@ -83,7 +85,20 @@ async function initComponentAdapter() {
// 如果你的组件体积比较大,可以使用异步加载 // 如果你的组件体积比较大,可以使用异步加载
// Button: () => // Button: () =>
// import('xxx').then((res) => res.Button), // import('xxx').then((res) => res.Button),
ApiSelect: (props, { attrs, slots }) => {
return h(
ApiSelect,
{
...props,
...attrs,
component: Select,
loadingSlot: 'suffixIcon',
visibleEvent: 'onDropdownVisibleChange',
modelField: 'value',
},
slots,
);
},
AutoComplete, AutoComplete,
Checkbox, Checkbox,
CheckboxGroup, CheckboxGroup,
@@ -93,6 +108,7 @@ async function initComponentAdapter() {
return h(Button, { ...props, attrs, type: 'default' }, slots); return h(Button, { ...props, attrs, type: 'default' }, slots);
}, },
Divider, Divider,
IconPicker,
Input: withDefaultPlaceholder(Input, 'input'), Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'),

View File

@@ -8,6 +8,7 @@ import { computed, nextTick, onMounted, type PropType, ref, watch } from 'vue';
import { findGroupParentIds, treeToList } from '@vben/utils'; import { findGroupParentIds, treeToList } from '@vben/utils';
import { Checkbox, Tree } from 'ant-design-vue'; import { Checkbox, Tree } from 'ant-design-vue';
import { uniq } from 'lodash-es';
/** 需要禁止透传 */ /** 需要禁止透传 */
defineOptions({ inheritAttrs: false }); defineOptions({ inheritAttrs: false });
@@ -73,6 +74,8 @@ const checkedRealKeys = ref<(number | string)[]>([]);
/** /**
* 取第一次的menuTree id 设置到checkedMenuKeys * 取第一次的menuTree id 设置到checkedMenuKeys
* 主要为了解决没有任何修改 直接点击保存的情况 * 主要为了解决没有任何修改 直接点击保存的情况
*
* length为0情况(即新增时候没有勾选节点) 勾选这里会延迟触发 节点会拼接上父节点 导致ID重复
*/ */
const stop = watch([checkedKeys, () => props.treeData], () => { const stop = watch([checkedKeys, () => props.treeData], () => {
if ( if (
@@ -86,7 +89,10 @@ const stop = watch([checkedKeys, () => props.treeData], () => {
checkedKeys.value as any, checkedKeys.value as any,
{ id: props.fieldNames.key }, { id: props.fieldNames.key },
); );
checkedRealKeys.value = [...parentIds, ...checkedKeys.value]; /**
* uniq 解决上面的id重复问题
*/
checkedRealKeys.value = uniq([...parentIds, ...checkedKeys.value]);
stop(); stop();
} }
if (!props.checkStrictly && checkedKeys.value.length > 0) { if (!props.checkStrictly && checkedKeys.value.length > 0) {
@@ -98,19 +104,21 @@ const stop = watch([checkedKeys, () => props.treeData], () => {
/** /**
* *
* @param checkedKeys 已经选中的子节点的ID * @param checkedStateKeys 已经选中的子节点的ID
* @param info info.halfCheckedKeys为父节点的ID * @param info info.halfCheckedKeys为父节点的ID
*/ */
type CheckedState<T = number | string> = type CheckedState<T = number | string> =
| { checked: T[]; halfChecked: T[] } | { checked: T[]; halfChecked: T[] }
| T[]; | T[];
function handleChecked(checkedKeys: CheckedState, info: CheckInfo) { function handleChecked(checkedStateKeys: CheckedState, info: CheckInfo) {
// 数组的话为节点关联 // 数组的话为节点关联
if (Array.isArray(checkedKeys)) { if (Array.isArray(checkedStateKeys)) {
const halfCheckedKeys: number[] = (info.halfCheckedKeys || []) as number[]; const halfCheckedKeys: number[] = (info.halfCheckedKeys || []) as number[];
checkedRealKeys.value = [...halfCheckedKeys, ...checkedKeys]; checkedRealKeys.value = [...halfCheckedKeys, ...checkedStateKeys];
} else { } else {
checkedRealKeys.value = [...checkedKeys.checked]; checkedRealKeys.value = [...checkedStateKeys.checked];
// fix: Invalid prop: type check failed for prop "value". Expected Array, got Object
checkedKeys.value = [...checkedStateKeys.checked];
} }
} }
@@ -137,9 +145,10 @@ function handleCheckStrictlyChange(e: CheckboxChangeEvent) {
/** /**
* 暴露方法来获取用于提交的全部节点 * 暴露方法来获取用于提交的全部节点
* uniq去重(保险方案)
*/ */
defineExpose({ defineExpose({
getCheckedKeys: () => checkedRealKeys.value, getCheckedKeys: () => uniq(checkedRealKeys.value),
}); });
onMounted(async () => { onMounted(async () => {

View File

@@ -207,7 +207,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
const vbenMenuList = backMenuToVbenMenu(backMenuList); const vbenMenuList = backMenuToVbenMenu(backMenuList);
// 特别注意 这里要深拷贝 // 特别注意 这里要深拷贝
const menuList = [...cloneDeep(localMenuList), ...vbenMenuList]; const menuList = [...cloneDeep(localMenuList), ...vbenMenuList];
console.log('menuList', menuList); // console.log('menuList', menuList);
return menuList; return menuList;
}, },
// 可以指定没有权限跳转403页面 // 可以指定没有权限跳转403页面

View File

@@ -21,6 +21,9 @@ const title = computed(() => {
const [BasicForm, formApi] = useVbenForm({ const [BasicForm, formApi] = useVbenForm({
commonConfig: { commonConfig: {
formItemClass: 'col-span-2', formItemClass: 'col-span-2',
componentProps: {
class: 'w-full',
},
}, },
layout: 'vertical', layout: 'vertical',
schema: drawerSchema(), schema: drawerSchema(),

View File

@@ -134,7 +134,6 @@ export const drawerSchema: FormSchemaGetter = () => [
{ {
component: 'Select', component: 'Select',
componentProps: { componentProps: {
class: 'w-full',
getPopupContainer, getPopupContainer,
mode: 'multiple', mode: 'multiple',
optionFilterProp: 'label', optionFilterProp: 'label',
@@ -148,7 +147,6 @@ export const drawerSchema: FormSchemaGetter = () => [
component: 'Select', component: 'Select',
componentProps: { componentProps: {
allowClear: false, allowClear: false,
class: 'w-full',
getPopupContainer, getPopupContainer,
options: getDictOptions(DictEnum.SYS_DEVICE_TYPE), options: getDictOptions(DictEnum.SYS_DEVICE_TYPE),
}, },

View File

@@ -99,7 +99,7 @@ export const drawerSchema: FormSchemaGetter = () => [
orientation: 'center', orientation: 'center',
}, },
fieldName: 'divider1', fieldName: 'divider1',
labelClass: 'w-0', hideLabel: true,
renderComponentContent: () => ({ renderComponentContent: () => ({
default: () => '基本信息', default: () => '基本信息',
}), }),
@@ -134,7 +134,7 @@ export const drawerSchema: FormSchemaGetter = () => [
orientation: 'center', orientation: 'center',
}, },
fieldName: 'divider2', fieldName: 'divider2',
labelClass: 'w-0', hideLabel: true,
renderComponentContent: () => ({ renderComponentContent: () => ({
default: () => '认证信息', default: () => '认证信息',
}), }),
@@ -157,7 +157,7 @@ export const drawerSchema: FormSchemaGetter = () => [
orientation: 'center', orientation: 'center',
}, },
fieldName: 'divider3', fieldName: 'divider3',
labelClass: 'w-0', hideLabel: true,
renderComponentContent: () => ({ renderComponentContent: () => ({
default: () => '其他信息', default: () => '其他信息',
}), }),

View File

@@ -82,7 +82,6 @@ export const drawerSchema: FormSchemaGetter = () => [
{ {
component: 'TreeSelect', component: 'TreeSelect',
componentProps: { componentProps: {
class: 'w-full',
getPopupContainer, getPopupContainer,
}, },
fieldName: 'deptId', fieldName: 'deptId',

View File

@@ -154,12 +154,13 @@ export const drawerSchema: FormSchemaGetter = () => [
defaultValue: [], defaultValue: [],
fieldName: 'menuIds', fieldName: 'menuIds',
label: '菜单权限', label: '菜单权限',
formItemClass: 'col-span-2',
}, },
{ {
component: 'Textarea', component: 'Textarea',
defaultValue: '', defaultValue: '',
fieldName: 'remark', fieldName: 'remark',
formItemClass: 'items-baseline', formItemClass: 'items-baseline col-span-2',
label: '备注', label: '备注',
}, },
]; ];

View File

@@ -24,12 +24,12 @@ const [BasicForm, formApi] = useVbenForm({
componentProps: { componentProps: {
class: 'w-full', class: 'w-full',
}, },
formItemClass: 'col-span-2', formItemClass: 'col-span-1',
}, },
layout: 'vertical', layout: 'vertical',
schema: drawerSchema(), schema: drawerSchema(),
showDefaultActions: false, showDefaultActions: false,
wrapperClass: 'grid-cols-2', wrapperClass: 'grid-cols-2 gap-x-4',
}); });
const menuTree = ref<any[]>([]); const menuTree = ref<any[]>([]);

View File

@@ -101,7 +101,7 @@ export const drawerSchema: FormSchemaGetter = () => [
orientation: 'center', orientation: 'center',
}, },
fieldName: 'divider1', fieldName: 'divider1',
labelClass: 'w-0', hideLabel: true,
renderComponentContent: () => ({ renderComponentContent: () => ({
default: () => '基本信息', default: () => '基本信息',
}), }),
@@ -132,7 +132,7 @@ export const drawerSchema: FormSchemaGetter = () => [
orientation: 'center', orientation: 'center',
}, },
fieldName: 'divider2', fieldName: 'divider2',
labelClass: 'w-0', hideLabel: true,
renderComponentContent: () => ({ renderComponentContent: () => ({
default: () => '管理员信息', default: () => '管理员信息',
}), }),
@@ -167,7 +167,7 @@ export const drawerSchema: FormSchemaGetter = () => [
orientation: 'center', orientation: 'center',
}, },
fieldName: 'divider3', fieldName: 'divider3',
labelClass: 'w-0', hideLabel: true,
renderComponentContent: () => ({ renderComponentContent: () => ({
default: () => '租户设置', default: () => '租户设置',
}), }),
@@ -175,7 +175,6 @@ export const drawerSchema: FormSchemaGetter = () => [
{ {
component: 'Select', component: 'Select',
componentProps: { componentProps: {
class: 'w-full',
getPopupContainer, getPopupContainer,
}, },
fieldName: 'packageId', fieldName: 'packageId',
@@ -237,7 +236,7 @@ export const drawerSchema: FormSchemaGetter = () => [
orientation: 'center', orientation: 'center',
}, },
fieldName: 'divider4', fieldName: 'divider4',
labelClass: 'w-0', hideLabel: true,
renderComponentContent: () => ({ renderComponentContent: () => ({
default: () => '企业信息', default: () => '企业信息',
}), }),

View File

@@ -23,6 +23,9 @@ const [BasicForm, formApi] = useVbenForm({
commonConfig: { commonConfig: {
formItemClass: 'col-span-2', formItemClass: 'col-span-2',
labelWidth: 100, labelWidth: 100,
componentProps: {
class: 'w-full',
},
}, },
schema: drawerSchema(), schema: drawerSchema(),
showDefaultActions: false, showDefaultActions: false,

View File

@@ -71,7 +71,7 @@ onMounted(loadTree);
<div class="bg-background z-100 sticky left-0 top-0 p-[8px]"> <div class="bg-background z-100 sticky left-0 top-0 p-[8px]">
<InputSearch <InputSearch
v-model:value="searchValue" v-model:value="searchValue"
placeholder="Search" :placeholder="$t('pages.common.search')"
size="small" size="small"
> >
<template #enterButton> <template #enterButton>

View File

@@ -20,6 +20,8 @@ export default defineConfig(async () => {
// dts: './types/components.d.ts', // 输出类型文件 // dts: './types/components.d.ts', // 输出类型文件
// resolvers: [ // resolvers: [
// AntDesignVueResolver({ // AntDesignVueResolver({
// // 需要排除Button组件 全局已经默认导入了
// exclude: ['Button'],
// importStyle: false, // css in js // importStyle: false, // css in js
// }), // }),
// ], // ],

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/web-ele", "name": "@vben/web-ele",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -8,7 +8,7 @@ import type { BaseFormComponentType } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue'; import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { globalShareState } from '@vben/common-ui'; import { ApiSelect, globalShareState, IconPicker } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { import {
@@ -22,6 +22,7 @@ import {
ElNotification, ElNotification,
ElRadioGroup, ElRadioGroup,
ElSelect, ElSelect,
ElSelectV2,
ElSpace, ElSpace,
ElSwitch, ElSwitch,
ElTimePicker, ElTimePicker,
@@ -41,10 +42,12 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType = export type ComponentType =
| 'ApiSelect'
| 'Checkbox' | 'Checkbox'
| 'CheckboxGroup' | 'CheckboxGroup'
| 'DatePicker' | 'DatePicker'
| 'Divider' | 'Divider'
| 'IconPicker'
| 'Input' | 'Input'
| 'InputNumber' | 'InputNumber'
| 'RadioGroup' | 'RadioGroup'
@@ -61,7 +64,19 @@ async function initComponentAdapter() {
// 如果你的组件体积比较大,可以使用异步加载 // 如果你的组件体积比较大,可以使用异步加载
// Button: () => // Button: () =>
// import('xxx').then((res) => res.Button), // import('xxx').then((res) => res.Button),
ApiSelect: (props, { attrs, slots }) => {
return h(
ApiSelect,
{
...props,
...attrs,
component: ElSelectV2,
loadingSlot: 'loading',
visibleEvent: 'onDropdownVisibleChange',
},
slots,
);
},
Checkbox: ElCheckbox, Checkbox: ElCheckbox,
CheckboxGroup: ElCheckboxGroup, CheckboxGroup: ElCheckboxGroup,
// 自定义默认按钮 // 自定义默认按钮
@@ -73,6 +88,7 @@ async function initComponentAdapter() {
return h(ElButton, { ...props, attrs, type: 'primary' }, slots); return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
}, },
Divider: ElDivider, Divider: ElDivider,
IconPicker,
Input: withDefaultPlaceholder(ElInput, 'input'), Input: withDefaultPlaceholder(ElInput, 'input'),
InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'), InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
RadioGroup: ElRadioGroup, RadioGroup: ElRadioGroup,

View File

@@ -7,6 +7,7 @@ import '@vben/styles';
import '@vben/styles/ele'; import '@vben/styles/ele';
import { useTitle } from '@vueuse/core'; import { useTitle } from '@vueuse/core';
import { ElLoading } from 'element-plus';
import { $t, setupI18n } from '#/locales'; import { $t, setupI18n } from '#/locales';
@@ -19,6 +20,9 @@ async function bootstrap(namespace: string) {
await initComponentAdapter(); await initComponentAdapter();
const app = createApp(App); const app = createApp(App);
// 注册Element Plus提供的v-loading指令
app.directive('loading', ElLoading.directive);
// 国际化 i18n 配置 // 国际化 i18n 配置
await setupI18n(app); await setupI18n(app);

View File

@@ -1,4 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { import {
@@ -6,6 +8,7 @@ import {
ElCard, ElCard,
ElMessage, ElMessage,
ElNotification, ElNotification,
ElSegmented,
ElSpace, ElSpace,
ElTable, ElTable,
} from 'element-plus'; } from 'element-plus';
@@ -47,6 +50,10 @@ const tableData = [
{ prop1: '5', prop2: 'E' }, { prop1: '5', prop2: 'E' },
{ prop1: '6', prop2: 'F' }, { prop1: '6', prop2: 'F' },
]; ];
const segmentedValue = ref('Mon');
const segmentedOptions = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
</script> </script>
<template> <template>
@@ -54,7 +61,8 @@ const tableData = [
description="支持多语言,主题功能集成切换等" description="支持多语言,主题功能集成切换等"
title="Element Plus组件使用演示" title="Element Plus组件使用演示"
> >
<ElCard class="mb-5"> <div class="flex flex-wrap gap-5">
<ElCard class="mb-5 w-auto">
<template #header> 按钮 </template> <template #header> 按钮 </template>
<ElSpace> <ElSpace>
<ElButton text>Text</ElButton> <ElButton text>Text</ElButton>
@@ -66,7 +74,7 @@ const tableData = [
<ElButton type="danger"> Error </ElButton> <ElButton type="danger"> Error </ElButton>
</ElSpace> </ElSpace>
</ElCard> </ElCard>
<ElCard class="mb-5"> <ElCard class="mb-5 w-80">
<template #header> Message </template> <template #header> Message </template>
<ElSpace> <ElSpace>
<ElButton type="info" @click="info"> 信息 </ElButton> <ElButton type="info" @click="info"> 信息 </ElButton>
@@ -75,7 +83,7 @@ const tableData = [
<ElButton type="success" @click="success"> 成功 </ElButton> <ElButton type="success" @click="success"> 成功 </ElButton>
</ElSpace> </ElSpace>
</ElCard> </ElCard>
<ElCard class="mb-5"> <ElCard class="mb-5 w-80">
<template #header> Notification </template> <template #header> Notification </template>
<ElSpace> <ElSpace>
<ElButton type="info" @click="notify('info')"> 信息 </ElButton> <ElButton type="info" @click="notify('info')"> 信息 </ElButton>
@@ -84,11 +92,26 @@ const tableData = [
<ElButton type="success" @click="notify('success')"> 成功 </ElButton> <ElButton type="success" @click="notify('success')"> 成功 </ElButton>
</ElSpace> </ElSpace>
</ElCard> </ElCard>
<ElCard class="mb-5"> <ElCard class="mb-5 w-auto">
<template #header> Segmented </template>
<ElSegmented
v-model="segmentedValue"
:options="segmentedOptions"
size="large"
/>
</ElCard>
<ElCard class="mb-5 w-80">
<template #header> V-Loading </template>
<div class="flex size-72 items-center justify-center" v-loading="true">
一些演示的内容
</div>
</ElCard>
<ElCard class="mb-5 w-80">
<ElTable :data="tableData" stripe> <ElTable :data="tableData" stripe>
<ElTable.TableColumn label="测试列1" prop="prop1" /> <ElTable.TableColumn label="测试列1" prop="prop1" />
<ElTable.TableColumn label="测试列2" prop="prop2" /> <ElTable.TableColumn label="测试列2" prop="prop2" />
</ElTable> </ElTable>
</ElCard> </ElCard>
</div>
</Page> </Page>
</template> </template>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/web-naive", "name": "@vben/web-naive",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -8,7 +8,7 @@ import type { BaseFormComponentType } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue'; import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { globalShareState } from '@vben/common-ui'; import { ApiSelect, globalShareState, IconPicker } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { import {
@@ -42,10 +42,12 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType = export type ComponentType =
| 'ApiSelect'
| 'Checkbox' | 'Checkbox'
| 'CheckboxGroup' | 'CheckboxGroup'
| 'DatePicker' | 'DatePicker'
| 'Divider' | 'Divider'
| 'IconPicker'
| 'Input' | 'Input'
| 'InputNumber' | 'InputNumber'
| 'RadioGroup' | 'RadioGroup'
@@ -63,6 +65,18 @@ async function initComponentAdapter() {
// Button: () => // Button: () =>
// import('xxx').then((res) => res.Button), // import('xxx').then((res) => res.Button),
ApiSelect: (props, { attrs, slots }) => {
return h(
ApiSelect,
{
...props,
...attrs,
component: NSelect,
modelField: 'value',
},
slots,
);
},
Checkbox: NCheckbox, Checkbox: NCheckbox,
CheckboxGroup: NCheckboxGroup, CheckboxGroup: NCheckboxGroup,
DatePicker: NDatePicker, DatePicker: NDatePicker,
@@ -75,6 +89,7 @@ async function initComponentAdapter() {
return h(NButton, { ...props, attrs, type: 'primary' }, slots); return h(NButton, { ...props, attrs, type: 'primary' }, slots);
}, },
Divider: NDivider, Divider: NDivider,
IconPicker,
Input: withDefaultPlaceholder(NInput, 'input'), Input: withDefaultPlaceholder(NInput, 'input'),
InputNumber: withDefaultPlaceholder(NInputNumber, 'input'), InputNumber: withDefaultPlaceholder(NInputNumber, 'input'),
RadioGroup: NRadioGroup, RadioGroup: NRadioGroup,

View File

@@ -148,6 +148,16 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
}, },
], ],
}, },
{
collapsed: false,
text: '布局组件',
items: [
{
link: 'layout-ui/page',
text: 'Page 页面',
},
],
},
{ {
collapsed: false, collapsed: false,
text: '通用组件', text: '通用组件',

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/docs", "name": "@vben/docs",
"version": "5.4.8", "version": "5.5.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "vitepress build", "build": "vitepress build",

View File

@@ -88,6 +88,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
| closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` | | closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` |
| confirmText | 确认按钮文本 | `string\|slot` | `确认` | | confirmText | 确认按钮文本 | `string\|slot` | `确认` |
| cancelText | 取消按钮文本 | `string\|slot` | `取消` | | cancelText | 取消按钮文本 | `string\|slot` | `取消` |
| placement | 抽屉弹出位置 | `'left'\|'right'\|'top'\|'bottom'` | `right` |
| showCancelButton | 显示取消按钮 | `boolean` | `true` | | showCancelButton | 显示取消按钮 | `boolean` | `true` |
| showConfirmButton | 显示确认按钮文本 | `boolean` | `true` | | showConfirmButton | 显示确认按钮文本 | `boolean` | `true` |
| class | modal的class宽度通过这个配置 | `string` | - | | class | modal的class宽度通过这个配置 | `string` | - |

View File

@@ -87,7 +87,7 @@ import type { BaseFormComponentType } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue'; import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { globalShareState } from '@vben/common-ui'; import { globalShareState, IconPicker } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { import {
@@ -149,6 +149,7 @@ export type ComponentType =
| 'TimePicker' | 'TimePicker'
| 'TreeSelect' | 'TreeSelect'
| 'Upload' | 'Upload'
| 'IconPicker';
| BaseFormComponentType; | BaseFormComponentType;
async function initComponentAdapter() { async function initComponentAdapter() {
@@ -166,6 +167,7 @@ async function initComponentAdapter() {
return h(Button, { ...props, attrs, type: 'default' }, slots); return h(Button, { ...props, attrs, type: 'default' }, slots);
}, },
Divider, Divider,
IconPicker,
Input: withDefaultPlaceholder(Input, 'input'), Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
@@ -419,7 +421,7 @@ export interface FormSchema<
help?: string; help?: string;
/** 表单项 */ /** 表单项 */
label?: string; label?: string;
// 自定义组件内部渲染 /** 自定义组件内部渲染 */
renderComponentContent?: RenderComponentContentType; renderComponentContent?: RenderComponentContentType;
/** 字段规则 */ /** 字段规则 */
rules?: FormSchemaRuleType; rules?: FormSchemaRuleType;
@@ -500,3 +502,20 @@ import { z } from '#/adapter/form';
}); });
} }
``` ```
## Slots
可以使用以下插槽在表单中插入自定义的内容
| 插槽名 | 描述 |
| ------------- | ------------------ |
| reset-before | 重置按钮之前的位置 |
| submit-before | 提交按钮之前的位置 |
| expand-before | 展开按钮之前的位置 |
| expand-after | 展开按钮之后的位置 |
::: tip 字段插槽
除了以上内置插槽之外,`schema`属性中每个字段的`fieldName`都可以作为插槽名称,这些字段插槽的优先级高于`component`定义的组件。也就是说,当提供了与`fieldName`同名的插槽时,这些插槽的内容将会作为这些字段的组件,此时`component`的值将会被忽略。
:::

View File

@@ -93,13 +93,14 @@ const [Modal, modalApi] = useVbenModal({
| modal | 显示遮罩 | `boolean` | `true` | | modal | 显示遮罩 | `boolean` | `true` |
| header | 显示header | `boolean` | `true` | | header | 显示header | `boolean` | `true` |
| footer | 显示footer | `boolean\|slot` | `true` | | footer | 显示footer | `boolean\|slot` | `true` |
| confirmDisabled | 禁用确认按钮 | `boolean` | `false` |
| confirmLoading | 确认按钮loading状态 | `boolean` | `false` | | confirmLoading | 确认按钮loading状态 | `boolean` | `false` |
| closeOnClickModal | 点击遮罩关闭弹窗 | `boolean` | `true` | | closeOnClickModal | 点击遮罩关闭弹窗 | `boolean` | `true` |
| closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` | | closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` |
| confirmText | 确认按钮文本 | `string\|slot` | `确认` | | confirmText | 确认按钮文本 | `string\|slot` | `确认` |
| cancelText | 取消按钮文本 | `string\|slot` | `取消` | | cancelText | 取消按钮文本 | `string\|slot` | `取消` |
| showCancelButton | 显示取消按钮 | `boolean` | `true` | | showCancelButton | 显示取消按钮 | `boolean` | `true` |
| showConfirmButton | 显示确认按钮文本 | `boolean` | `true` | | showConfirmButton | 显示确认按钮 | `boolean` | `true` |
| class | modal的class宽度通过这个配置 | `string` | - | | class | modal的class宽度通过这个配置 | `string` | - |
| contentClass | modal内容区域的class | `string` | - | | contentClass | modal内容区域的class | `string` | - |
| footerClass | modal底部区域的class | `string` | - | | footerClass | modal底部区域的class | `string` | - |

View File

@@ -6,6 +6,10 @@
::: :::
## 布局组件
布局组件一般在页面内容区域用作顶层容器组件,提供一些统一的布局样式和基本功能。
## 通用组件 ## 通用组件
通用组件是一些常用的组件,比如弹窗、抽屉、表单等。大部分基于 `Tailwind CSS` 实现,可适用于不同 UI 组件库的应用。 通用组件是一些常用的组件,比如弹窗、抽屉、表单等。大部分基于 `Tailwind CSS` 实现,可适用于不同 UI 组件库的应用。

View File

@@ -0,0 +1,45 @@
---
outline: deep
---
# Page 常规页面组件
提供一个常规页面布局的组件,包括头部、内容区域、底部三个部分。
::: info 写在前面
本组件是一个基本布局组件。如果有更多的通用页面布局需求(比如双列布局等),可以根据实际需求自行封装。
:::
## 基础用法
`Page`作为你的业务页面的根组件即可。
### Props
| 属性名 | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- |
| title | 页面标题 | `string\|slot` | - |
| description | 页面描述(标题下的内容) | `string\|slot` | - |
| contentClass | 内容区域的class | `string` | - |
| headerClass | 头部区域的class | `string` | - |
| footerClass | 底部区域的class | `string` | - |
| autoContentHeight | 自动调整内容区域的高度 | `boolean` | `false` |
| fixedHeader | 固定头部在页面内容区域顶部,在滚动时保持可见 | `boolean` | `false` |
::: tip 注意
如果`title``description``extra`三者均未提供有效内容(通过`props`或者`slots`均可),则页面头部区域不会渲染。
:::
### Slots
| 插槽名称 | 描述 |
| ----------- | ------------ |
| default | 页面内容 |
| title | 页面标题 |
| description | 页面描述 |
| extra | 页面头部右侧 |
| footer | 页面底部 |

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/commitlint-config", "name": "@vben/commitlint-config",
"version": "5.4.8", "version": "5.5.0",
"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",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/stylelint-config", "name": "@vben/stylelint-config",
"version": "5.4.8", "version": "5.5.0",
"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",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/node-utils", "name": "@vben/node-utils",
"version": "5.4.8", "version": "5.5.0",
"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",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/tailwind-config", "name": "@vben/tailwind-config",
"version": "5.4.8", "version": "5.5.0",
"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",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/tsconfig", "name": "@vben/tsconfig",
"version": "5.4.8", "version": "5.5.0",
"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",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/vite-config", "name": "@vben/vite-config",
"version": "5.4.8", "version": "5.5.0",
"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",

View File

@@ -1,5 +1,6 @@
import type { ApplicationPluginOptions } from '../typing'; import type { ApplicationPluginOptions } from '../typing';
import { existsSync } from 'node:fs';
import { join } from 'node:path'; import { join } from 'node:path';
import { fs } from '@vben/node-utils'; import { fs } from '@vben/node-utils';
@@ -21,12 +22,11 @@ function getConfFiles() {
const script = process.env.npm_lifecycle_script as string; const script = process.env.npm_lifecycle_script as string;
const reg = /--mode ([\d_a-z]+)/; const reg = /--mode ([\d_a-z]+)/;
const result = reg.exec(script); const result = reg.exec(script);
let mode = 'production';
if (result) { if (result) {
const mode = result[1]; mode = result[1] as string;
return ['.env', `.env.${mode}`];
} }
return ['.env', '.env.production']; return ['.env', '.env.local', `.env.${mode}`, `.env.${mode}.local`];
} }
/** /**
@@ -42,11 +42,14 @@ async function loadEnv<T = Record<string, string>>(
for (const confFile of confFiles) { for (const confFile of confFiles) {
try { try {
const envPath = await fs.readFile(join(process.cwd(), confFile), { const confFilePath = join(process.cwd(), confFile);
if (existsSync(confFilePath)) {
const envPath = await fs.readFile(confFilePath, {
encoding: 'utf8', encoding: 'utf8',
}); });
const env = dotenv.parse(envPath); const env = dotenv.parse(envPath);
envConfig = { ...envConfig, ...env }; envConfig = { ...envConfig, ...env };
}
} catch (error) { } catch (error) {
console.error(`Error while parsing ${confFile}`, error); console.error(`Error while parsing ${confFile}`, error);
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "vben-admin-monorepo", "name": "vben-admin-monorepo",
"version": "5.4.8", "version": "5.5.0",
"private": true, "private": true,
"keywords": [ "keywords": [
"monorepo", "monorepo",
@@ -99,7 +99,7 @@
"node": ">=20.10.0", "node": ">=20.10.0",
"pnpm": ">=9.12.0" "pnpm": ">=9.12.0"
}, },
"packageManager": "pnpm@9.14.2", "packageManager": "pnpm@9.14.4",
"pnpm": { "pnpm": {
"peerDependencyRules": { "peerDependencyRules": {
"allowedVersions": { "allowedVersions": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/design", "name": "@vben-core/design",
"version": "5.4.8", "version": "5.5.0",
"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": {
@@ -28,7 +28,7 @@
".": { ".": {
"types": "./src/index.ts", "types": "./src/index.ts",
"development": "./src/index.ts", "development": "./src/index.ts",
"default": "./dist/style.css" "default": "./dist/design.css"
} }
}, },
"publishConfig": { "publishConfig": {

View File

@@ -58,6 +58,8 @@
/* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */ /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
--accent: 216 5% 19%; --accent: 216 5% 19%;
--accent-dark: 240 0% 22%;
--accent-darker: 240 0% 26%;
--accent-lighter: 216 5% 12%; --accent-lighter: 216 5% 12%;
--accent-hover: 216 5% 24%; --accent-hover: 216 5% 24%;
--accent-foreground: 0 0% 98%; --accent-foreground: 0 0% 98%;

View File

@@ -58,6 +58,8 @@
/* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */ /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
--accent: 240 5% 96%; --accent: 240 5% 96%;
--accent-dark: 216 14% 93%;
--accent-darker: 216 11% 91%;
--accent-lighter: 240 0% 98%; --accent-lighter: 240 0% 98%;
--accent-hover: 200deg 10% 90%; --accent-hover: 200deg 10% 90%;
--accent-foreground: 240 6% 10%; --accent-foreground: 240 6% 10%;

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/icons", "name": "@vben-core/icons",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/shared", "name": "@vben-core/shared",
"version": "5.4.8", "version": "5.5.0",
"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": {
@@ -86,12 +86,16 @@
"dayjs": "catalog:", "dayjs": "catalog:",
"defu": "catalog:", "defu": "catalog:",
"lodash.clonedeep": "catalog:", "lodash.clonedeep": "catalog:",
"lodash.get": "catalog:",
"lodash.isequal": "catalog:",
"nprogress": "catalog:", "nprogress": "catalog:",
"tailwind-merge": "catalog:", "tailwind-merge": "catalog:",
"theme-colors": "catalog:" "theme-colors": "catalog:"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash.clonedeep": "catalog:", "@types/lodash.clonedeep": "catalog:",
"@types/lodash.get": "catalog:",
"@types/lodash.isequal": "catalog:",
"@types/nprogress": "catalog:" "@types/nprogress": "catalog:"
} }
} }

View File

@@ -15,3 +15,5 @@ export * from './update-css-variables';
export * from './util'; export * from './util';
export * from './window'; export * from './window';
export { default as cloneDeep } from 'lodash.clonedeep'; export { default as cloneDeep } from 'lodash.clonedeep';
export { default as get } from 'lodash.get';
export { default as isEqual } from 'lodash.isequal';

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/typings", "name": "@vben-core/typings",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/composables", "name": "@vben-core/composables",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/preferences", "name": "@vben-core/preferences",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/form-ui", "name": "@vben-core/form-ui",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -185,7 +185,7 @@ export class FormApi {
const fieldSet = new Set(fields); const fieldSet = new Set(fields);
const schema = this.state?.schema ?? []; 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({ this.setState({
schema: filterSchema, schema: filterSchema,

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/layout-ui", "name": "@vben-core/layout-ui",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -63,7 +63,7 @@ const logoStyle = computed((): CSSProperties => {
<header <header
:class="theme" :class="theme"
:style="style" :style="style"
class="border-border bg-header top-0 flex w-full flex-[0_0_auto] items-center border-b transition-[margin-top] duration-200" class="border-border bg-header top-0 flex w-full flex-[0_0_auto] items-center border-b pl-2 transition-[margin-top] duration-200"
> >
<div v-if="slots.logo" :style="logoStyle"> <div v-if="slots.logo" :style="logoStyle">
<slot name="logo"></slot> <slot name="logo"></slot>

View File

@@ -191,7 +191,10 @@ watchEffect(() => {
function calcMenuWidthStyle(isHiddenDom: boolean): CSSProperties { function calcMenuWidthStyle(isHiddenDom: boolean): CSSProperties {
const { extraWidth, fixedExtra, isSidebarMixed, show, width } = props; const { extraWidth, fixedExtra, isSidebarMixed, show, width } = props;
let widthValue = `${width + (isSidebarMixed && fixedExtra && extraVisible.value ? extraWidth : 0)}px`; let widthValue =
width === 0
? '0px'
: `${width + (isSidebarMixed && fixedExtra && extraVisible.value ? extraWidth : 0)}px`;
const { collapseWidth } = props; const { collapseWidth } = props;

View File

@@ -192,7 +192,7 @@ const headerFixed = computed(() => {
}); });
const showSidebar = computed(() => { const showSidebar = computed(() => {
return isSideMode.value && sidebarEnable.value; return isSideMode.value && sidebarEnable.value && !props.sidebarHidden;
}); });
/** /**
@@ -503,7 +503,7 @@ function handleHeaderToggle() {
<div <div
ref="contentRef" ref="contentRef"
class="flex flex-1 flex-col overflow-hidden transition-all duration-300 ease-in" class="flex flex-1 flex-col transition-all duration-300 ease-in"
> >
<div <div
:class="[ :class="[
@@ -533,7 +533,7 @@ function handleHeaderToggle() {
<template #toggle-button> <template #toggle-button>
<VbenIconButton <VbenIconButton
v-if="showHeaderToggleButton" v-if="showHeaderToggleButton"
class="my-0 ml-2 mr-1 rounded-md" class="my-0 mr-1 rounded-md"
@click="handleHeaderToggle" @click="handleHeaderToggle"
> >
<Menu class="size-4" /> <Menu class="size-4" />

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/menu-ui", "name": "@vben-core/menu-ui",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -41,6 +41,7 @@ export class DrawerApi {
loading: false, loading: false,
modal: true, modal: true,
openAutoFocus: false, openAutoFocus: false,
placement: 'right',
showCancelButton: true, showCancelButton: true,
showConfirmButton: true, showConfirmButton: true,
title: '', title: '',

View File

@@ -4,6 +4,8 @@ import type { DrawerApi } from './drawer-api';
import type { Component, Ref } from 'vue'; import type { Component, Ref } from 'vue';
export type DrawerPlacement = 'bottom' | 'left' | 'right' | 'top';
export interface DrawerProps { export interface DrawerProps {
/** /**
* 取消按钮文字 * 取消按钮文字
@@ -72,6 +74,12 @@ export interface DrawerProps {
* 是否自动聚焦 * 是否自动聚焦
*/ */
openAutoFocus?: boolean; openAutoFocus?: boolean;
/**
* 抽屉位置
* @default right
*/
placement?: DrawerPlacement;
/** /**
* 是否显示取消按钮 * 是否显示取消按钮
* @default true * @default true

View File

@@ -62,6 +62,7 @@ const {
loading: showLoading, loading: showLoading,
modal, modal,
openAutoFocus, openAutoFocus,
placement,
showCancelButton, showCancelButton,
showConfirmButton, showConfirmButton,
title, title,
@@ -119,11 +120,13 @@ function handleFocusOutside(e: Event) {
<SheetContent <SheetContent
:class=" :class="
cn('flex w-[520px] flex-col', drawerClass, { cn('flex w-[520px] flex-col', drawerClass, {
'!w-full': isMobile, '!w-full': isMobile || placement === 'bottom' || placement === 'top',
'max-h-[100vh]': placement === 'bottom' || placement === 'top',
}) })
" "
:modal="modal" :modal="modal"
:open="state?.isOpen" :open="state?.isOpen"
:side="placement"
@close-auto-focus="handleFocusOutside" @close-auto-focus="handleFocusOutside"
@escape-key-down="escapeKeyDown" @escape-key-down="escapeKeyDown"
@focus-outside="handleFocusOutside" @focus-outside="handleFocusOutside"

View File

@@ -41,6 +41,7 @@ export class ModalApi {
class: '', class: '',
closeOnClickModal: true, closeOnClickModal: true,
closeOnPressEscape: true, closeOnPressEscape: true,
confirmDisabled: false,
confirmLoading: false, confirmLoading: false,
contentClass: '', contentClass: '',
draggable: false, draggable: false,

View File

@@ -35,6 +35,10 @@ export interface ModalProps {
* @default true * @default true
*/ */
closeOnPressEscape?: boolean; closeOnPressEscape?: boolean;
/**
* 禁用确认按钮
*/
confirmDisabled?: boolean;
/** /**
* 确定按钮 loading * 确定按钮 loading
* @default false * @default false

View File

@@ -59,6 +59,7 @@ const {
closable, closable,
closeOnClickModal, closeOnClickModal,
closeOnPressEscape, closeOnPressEscape,
confirmDisabled,
confirmLoading, confirmLoading,
confirmText, confirmText,
contentClass, contentClass,
@@ -235,7 +236,7 @@ function handleFocusOutside(e: Event) {
ref="wrapperRef" ref="wrapperRef"
:class=" :class="
cn('relative min-h-40 flex-1 overflow-y-auto p-3', contentClass, { cn('relative min-h-40 flex-1 overflow-y-auto p-3', contentClass, {
'overflow-hidden': showLoading, 'pointer-events-none overflow-hidden': showLoading,
}) })
" "
> >
@@ -285,6 +286,7 @@ function handleFocusOutside(e: Event) {
<component <component
:is="components.PrimaryButton || VbenButton" :is="components.PrimaryButton || VbenButton"
v-if="showConfirmButton" v-if="showConfirmButton"
:disabled="confirmDisabled"
:loading="confirmLoading" :loading="confirmLoading"
@click="() => modalApi?.onConfirm()" @click="() => modalApi?.onConfirm()"
> >

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/shadcn-ui", "name": "@vben-core/shadcn-ui",
"version": "5.4.8", "version": "5.5.0",
"#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",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/tabs-ui", "name": "@vben-core/tabs-ui",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/constants", "name": "@vben/constants",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/access", "name": "@vben/access",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/common-ui", "name": "@vben/common-ui",
"version": "5.4.8", "version": "5.5.0",
"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": {
@@ -29,6 +29,7 @@
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@vben-core/form-ui": "workspace:*", "@vben-core/form-ui": "workspace:*",
"@vben-core/popup-ui": "workspace:*", "@vben-core/popup-ui": "workspace:*",
"@vben-core/preferences": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*", "@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*", "@vben-core/shared": "workspace:*",
"@vben/constants": "workspace:*", "@vben/constants": "workspace:*",

View File

@@ -0,0 +1,182 @@
<script lang="ts" setup>
import type { AnyPromiseFunction } from '@vben/types';
import { computed, ref, unref, useAttrs, type VNode, watch } from 'vue';
import { LoaderCircle } from '@vben/icons';
import { get, isEqual, isFunction } from '@vben-core/shared/utils';
import { objectOmit } from '@vueuse/core';
type OptionsItem = {
[name: string]: any;
disabled?: boolean;
label?: string;
value?: string;
};
interface Props {
// 组件
component: VNode;
numberToString?: boolean;
api?: (arg?: any) => Promise<OptionsItem[] | Record<string, any>>;
params?: Record<string, any>;
resultField?: string;
labelField?: string;
valueField?: string;
immediate?: boolean;
alwaysLoad?: boolean;
beforeFetch?: AnyPromiseFunction<any, any>;
afterFetch?: AnyPromiseFunction<any, any>;
options?: OptionsItem[];
// 尾部插槽
loadingSlot?: string;
// 可见时触发的事件名
visibleEvent?: string;
modelField?: string;
}
defineOptions({ name: 'ApiSelect', inheritAttrs: false });
const props = withDefaults(defineProps<Props>(), {
labelField: 'label',
valueField: 'value',
resultField: '',
visibleEvent: '',
numberToString: false,
params: () => ({}),
immediate: true,
alwaysLoad: false,
loadingSlot: '',
beforeFetch: undefined,
afterFetch: undefined,
modelField: 'modelValue',
api: undefined,
options: () => [],
});
const emit = defineEmits<{
optionsChange: [OptionsItem[]];
}>();
const modelValue = defineModel({ default: '' });
const attrs = useAttrs();
const refOptions = ref<OptionsItem[]>([]);
const loading = ref(false);
// 首次是否加载过了
const isFirstLoaded = ref(false);
const getOptions = computed(() => {
const { labelField, valueField, numberToString } = props;
const data: OptionsItem[] = [];
const refOptionsData = unref(refOptions);
for (const next of refOptionsData) {
if (next) {
const value = get(next, valueField);
data.push({
...objectOmit(next, [labelField, valueField]),
label: get(next, labelField),
value: numberToString ? `${value}` : value,
});
}
}
return data.length > 0 ? data : props.options;
});
const bindProps = computed(() => {
return {
[props.modelField]: unref(modelValue),
[`onUpdate:${props.modelField}`]: (val: string) => {
modelValue.value = val;
},
...objectOmit(attrs, ['onUpdate:value']),
...(props.visibleEvent
? {
[props.visibleEvent]: handleFetchForVisible,
}
: {}),
};
});
async function fetchApi() {
let { api, beforeFetch, afterFetch, params, resultField } = props;
if (!api || !isFunction(api) || loading.value) {
return;
}
refOptions.value = [];
try {
loading.value = true;
if (beforeFetch && isFunction(beforeFetch)) {
params = (await beforeFetch(params)) || params;
}
let res = await api(params);
if (afterFetch && isFunction(afterFetch)) {
res = (await afterFetch(res)) || res;
}
isFirstLoaded.value = true;
if (Array.isArray(res)) {
refOptions.value = res;
emitChange();
return;
}
if (resultField) {
refOptions.value = get(res, resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
// reset status
isFirstLoaded.value = false;
} finally {
loading.value = false;
}
}
async function handleFetchForVisible(visible: boolean) {
if (visible) {
if (props.alwaysLoad) {
await fetchApi();
} else if (!props.immediate && !unref(isFirstLoaded)) {
await fetchApi();
}
}
}
watch(
() => props.params,
(value, oldValue) => {
if (isEqual(value, oldValue)) {
return;
}
fetchApi();
},
{ deep: true, immediate: props.immediate },
);
function emitChange() {
emit('optionsChange', unref(getOptions));
}
</script>
<template>
<div v-bind="{ ...$attrs }">
<component
:is="component"
v-bind="bindProps"
:options="getOptions"
:placeholder="$attrs.placeholder"
>
<template v-for="item in Object.keys($slots)" #[item]="data">
<slot :name="item" v-bind="data || {}"></slot>
</template>
<template v-if="loadingSlot && loading" #[loadingSlot]>
<LoaderCircle class="animate-spin" />
</template>
</component>
</div>
</template>

View File

@@ -0,0 +1 @@
export { default as ApiSelect } from './api-select.vue';

View File

@@ -1,46 +0,0 @@
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import { EllipsisText } from '..';
describe('ellipsis-text.vue', () => {
it('renders the correct content and truncates text', async () => {
const wrapper = mount(EllipsisText, {
props: {
line: 1,
title: 'Test Title',
},
slots: {
default: 'This is a very long text that should be truncated.',
},
});
expect(wrapper.text()).toContain('This is a very long text');
// 检查 ellipsis 是否应用了正确的 class
const ellipsis = wrapper.find('.truncate');
expect(ellipsis.exists()).toBe(true);
});
it('expands text on click if expand is true', async () => {
const wrapper = mount(EllipsisText, {
props: {
expand: true,
line: 1,
},
slots: {
default: 'This is a very long text that should be truncated.',
},
});
const ellipsis = wrapper.find('.truncate');
// 点击 ellipsis应该触发 expandChange参数为 false
await ellipsis.trigger('click');
expect(wrapper.emitted('expandChange')).toBeTruthy();
expect(wrapper.emitted('expandChange')?.[0]).toEqual([true]);
// 再次点击,应该触发 expandChange参数为 false
await ellipsis.trigger('click');
expect(wrapper.emitted('expandChange')?.length).toBe(2);
expect(wrapper.emitted('expandChange')?.[1]).toEqual([false]);
});
});

View File

@@ -1,10 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, useTemplateRef, watch, watchEffect } from 'vue'; import { computed, ref, watch, watchEffect } from 'vue';
import { usePagination } from '@vben/hooks'; import { usePagination } from '@vben/hooks';
import { EmptyIcon, Grip } from '@vben/icons'; import { EmptyIcon, Grip, listIcons } from '@vben/icons';
import { $t } from '@vben/locales';
import { import {
Button, Button,
Input,
Pagination, Pagination,
PaginationEllipsis, PaginationEllipsis,
PaginationFirst, PaginationFirst,
@@ -18,9 +20,11 @@ import {
VbenPopover, VbenPopover,
} from '@vben-core/shadcn-ui'; } from '@vben-core/shadcn-ui';
import { refDebounced } from '@vueuse/core';
interface Props { interface Props {
value?: string;
pageSize?: number; pageSize?: number;
prefix?: string;
/** /**
* 图标列表 * 图标列表
*/ */
@@ -28,48 +32,65 @@ interface Props {
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
value: '', prefix: 'ant-design',
pageSize: 36, pageSize: 36,
icons: () => [], icons: () => [],
}); });
const emit = defineEmits<{ const emit = defineEmits<{
change: [string]; change: [string];
'update:value': [string];
}>(); }>();
const refTrigger = useTemplateRef<HTMLElement>('refTrigger'); const modelValue = defineModel({ default: '', type: String });
const currentSelect = ref('');
const currentList = ref(props.icons);
const currentPage = ref(1);
watch( const visible = ref(false);
() => props.icons, const currentSelect = ref('');
(newIcons) => { const currentPage = ref(1);
currentList.value = newIcons; const keyword = ref('');
}, const keywordDebounce = refDebounced(keyword, 300);
{ immediate: true }, const currentList = computed(() => {
); try {
if (props.prefix) {
const icons = listIcons('', props.prefix);
if (icons.length === 0) {
console.warn(`No icons found for prefix: ${props.prefix}`);
}
return icons;
} else {
return props.icons;
}
} catch (error) {
console.error('Failed to load icons:', error);
return [];
}
});
const showList = computed(() => {
return currentList.value.filter((item) =>
item.includes(keywordDebounce.value),
);
});
const { paginationList, total, setCurrentPage } = usePagination( const { paginationList, total, setCurrentPage } = usePagination(
currentList, showList,
props.pageSize, props.pageSize,
); );
watchEffect(() => { watchEffect(() => {
currentSelect.value = props.value; currentSelect.value = modelValue.value;
}); });
watch( watch(
() => currentSelect.value, () => currentSelect.value,
(v) => { (v) => {
emit('update:value', v);
emit('change', v); emit('change', v);
}, },
); );
const handleClick = (icon: string) => { const handleClick = (icon: string) => {
currentSelect.value = icon; currentSelect.value = icon;
modelValue.value = icon;
close();
}; };
const handlePageChange = (page: number) => { const handlePageChange = (page: number) => {
@@ -77,22 +98,46 @@ const handlePageChange = (page: number) => {
setCurrentPage(page); setCurrentPage(page);
}; };
function changeOpenState() { function toggleOpenState() {
refTrigger.value?.click?.(); visible.value = !visible.value;
} }
defineExpose({ changeOpenState }); function open() {
visible.value = true;
}
function close() {
visible.value = false;
}
defineExpose({ toggleOpenState, open, close });
</script> </script>
<template> <template>
<VbenPopover <VbenPopover
v-model:open="visible"
:content-props="{ align: 'end', alignOffset: -11, sideOffset: 8 }" :content-props="{ align: 'end', alignOffset: -11, sideOffset: 8 }"
content-class="p-0 pt-3" content-class="p-0 pt-3"
> >
<template #trigger> <template #trigger>
<div ref="refTrigger"> <slot :close="close" :icon="currentSelect" :open="open" name="trigger">
<VbenIcon :icon="currentSelect || Grip" class="size-5" /> <div class="flex items-center gap-2">
<Input
:value="currentSelect"
class="flex-1 cursor-pointer"
v-bind="$attrs"
:placeholder="$t('ui.iconPicker.placeholder')"
/>
<VbenIcon :icon="currentSelect || Grip" class="size-8" />
</div> </div>
</slot>
</template> </template>
<div class="mb-2 flex w-full">
<Input
v-model="keyword"
:placeholder="$t('ui.iconPicker.search')"
class="mx-2"
/>
</div>
<template v-if="paginationList.length > 0"> <template v-if="paginationList.length > 0">
<div class="grid max-h-[360px] w-full grid-cols-6 justify-items-center"> <div class="grid max-h-[360px] w-full grid-cols-6 justify-items-center">

View File

@@ -1,3 +1,4 @@
export * from './api-select';
export * from './captcha'; export * from './captcha';
export * from './code-mirror'; export * from './code-mirror';
export * from './ellipsis-text'; export * from './ellipsis-text';

View File

@@ -1,5 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'; import {
computed,
nextTick,
onMounted,
ref,
type StyleValue,
useTemplateRef,
} from 'vue';
import { preferences } from '@vben-core/preferences';
import { cn } from '@vben-core/shared/utils';
interface Props { interface Props {
title?: string; title?: string;
@@ -9,6 +19,10 @@ interface Props {
* 根据content可见高度自适应 * 根据content可见高度自适应
*/ */
autoContentHeight?: boolean; autoContentHeight?: boolean;
/** 头部固定 */
fixedHeader?: boolean;
headerClass?: string;
footerClass?: string;
} }
defineOptions({ defineOptions({
@@ -20,6 +34,7 @@ const {
description = '', description = '',
autoContentHeight = false, autoContentHeight = false,
title = '', title = '',
fixedHeader = false,
} = defineProps<Props>(); } = defineProps<Props>();
const headerHeight = ref(0); const headerHeight = ref(0);
@@ -29,6 +44,17 @@ const shouldAutoHeight = ref(false);
const headerRef = useTemplateRef<HTMLDivElement>('headerRef'); const headerRef = useTemplateRef<HTMLDivElement>('headerRef');
const footerRef = useTemplateRef<HTMLDivElement>('footerRef'); const footerRef = useTemplateRef<HTMLDivElement>('footerRef');
const headerStyle = computed<StyleValue>(() => {
return fixedHeader
? {
position: 'sticky',
zIndex: 200,
top:
preferences.header.mode === 'fixed' ? 'var(--vben-header-height)' : 0,
}
: undefined;
});
const contentStyle = computed(() => { const contentStyle = computed(() => {
if (autoContentHeight) { if (autoContentHeight) {
return { return {
@@ -69,7 +95,16 @@ onMounted(() => {
$slots.extra $slots.extra
" "
ref="headerRef" ref="headerRef"
class="bg-card relative px-6 py-4" :class="
cn(
'bg-card relative px-6 py-4',
headerClass,
fixedHeader
? 'border-border border-b transition-all duration-200'
: '',
)
"
:style="headerStyle"
> >
<slot name="title"> <slot name="title">
<div v-if="title" class="mb-2 flex text-lg font-semibold"> <div v-if="title" class="mb-2 flex text-lg font-semibold">
@@ -95,7 +130,12 @@ onMounted(() => {
<div <div
v-if="$slots.footer" v-if="$slots.footer"
ref="footerRef" ref="footerRef"
class="bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4" :class="
cn(
footerClass,
'bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4',
)
"
> >
<slot name="footer"></slot> <slot name="footer"></slot>
</div> </div>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/hooks", "name": "@vben/hooks",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -260,6 +260,9 @@ export function useElementPlusDesignTokens() {
'--el-fill-color-light': getCssVariableValue('--accent'), '--el-fill-color-light': getCssVariableValue('--accent'),
'--el-fill-color-lighter': getCssVariableValue('--accent-lighter'), '--el-fill-color-lighter': getCssVariableValue('--accent-lighter'),
'--el-fill-color-dark': getCssVariableValue('--accent-dark'),
'--el-fill-color-darker': getCssVariableValue('--accent-darker'),
// 解决ElLoading背景色问题 // 解决ElLoading背景色问题
'--el-mask-color': isDark.value '--el-mask-color': isDark.value
? 'rgba(0,0,0,.8)' ? 'rgba(0,0,0,.8)'

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/layouts", "name": "@vben/layouts",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -110,8 +110,17 @@ const {
sidebarVisible, sidebarVisible,
} = useMixedMenu(); } = useMixedMenu();
function wrapperMenus(menus: MenuRecordRaw[]) { /**
return mapTree(menus, (item) => { * 包装菜单,翻译菜单名称
* @param menus 原始菜单数据
* @param deep 是否深度包装。对于双列布局,只需要包装第一层,因为更深层的数据会在扩展菜单中重新包装
*/
function wrapperMenus(menus: MenuRecordRaw[], deep: boolean = true) {
return deep
? mapTree(menus, (item) => {
return { ...cloneDeep(item), name: $t(item.name) };
})
: menus.map((item) => {
return { ...cloneDeep(item), name: $t(item.name) }; return { ...cloneDeep(item), name: $t(item.name) };
}); });
} }
@@ -257,7 +266,7 @@ const headerSlots = computed(() => {
<template #mixed-menu> <template #mixed-menu>
<LayoutMixedMenu <LayoutMixedMenu
:active-path="extraActiveMenu" :active-path="extraActiveMenu"
:menus="wrapperMenus(headerMenus)" :menus="wrapperMenus(headerMenus, false)"
:rounded="isMenuRounded" :rounded="isMenuRounded"
:theme="sidebarTheme" :theme="sidebarTheme"
@default-select="handleDefaultSelect" @default-select="handleDefaultSelect"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/plugins", "name": "@vben/plugins",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -43,7 +43,11 @@ function extendProxyOption(
const data = await configFn( const data = await configFn(
params, params,
{ {
...customValues, /**
* 开启toolbarConfig.refresh功能
* 点击刷新按钮 这里的值为PointerEvent 会携带错误参数
*/
...(customValues instanceof PointerEvent ? {} : customValues),
...formValues, ...formValues,
}, },
...args, ...args,

View File

@@ -30,7 +30,7 @@
--vxe-ui-loading-background-color: hsl(var(--overlay-content)); --vxe-ui-loading-background-color: hsl(var(--overlay-content));
/* table */ /* table */
--vxe-ui-table-header-background-color: hsl(var(--accent)); --vxe-ui-table-header-background-color: hsl(0deg 0% 98%);
--vxe-ui-table-border-color: hsl(var(--border)); --vxe-ui-table-border-color: hsl(var(--border));
--vxe-ui-table-row-hover-background-color: hsl(var(--accent-hover)); --vxe-ui-table-row-hover-background-color: hsl(var(--accent-hover));
--vxe-ui-table-row-striped-background-color: hsl(var(--accent) / 60%); --vxe-ui-table-row-striped-background-color: hsl(var(--accent) / 60%);
@@ -49,6 +49,10 @@
/* --vxe-ui-table-fixed-scrolling-box-shadow-color: rgb(0 0 0 / 80%); */ /* --vxe-ui-table-fixed-scrolling-box-shadow-color: rgb(0 0 0 / 80%); */
} }
html[data-vxe-ui-theme='dark'] .vxe-grid {
--vxe-ui-table-header-background-color: hsl(var(--accent) / 50%);
}
.vxe-pager { .vxe-pager {
.vxe-pager--prev-btn:not(.is--disabled):active, .vxe-pager--prev-btn:not(.is--disabled):active,
.vxe-pager--next-btn:not(.is--disabled):active, .vxe-pager--next-btn:not(.is--disabled):active,

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/request", "name": "@vben/request",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/icons", "name": "@vben/icons",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/locales", "name": "@vben/locales",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -21,6 +21,10 @@
"pointAriaLabel": "Click point", "pointAriaLabel": "Click point",
"clickInOrder": "Please click in order" "clickInOrder": "Please click in order"
}, },
"iconPicker": {
"placeholder": "Select an icon",
"search": "Search icon..."
},
"fallback": { "fallback": {
"pageNotFound": "Oops! Page Not Found", "pageNotFound": "Oops! Page Not Found",
"pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.", "pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.",

View File

@@ -21,6 +21,10 @@
"pointAriaLabel": "点击点", "pointAriaLabel": "点击点",
"clickInOrder": "请依次点击" "clickInOrder": "请依次点击"
}, },
"iconPicker": {
"placeholder": "选择一个图标",
"search": "搜索图标..."
},
"fallback": { "fallback": {
"pageNotFound": "哎呀!未找到页面", "pageNotFound": "哎呀!未找到页面",
"pageNotFoundDesc": "抱歉,我们无法找到您要找的页面。", "pageNotFoundDesc": "抱歉,我们无法找到您要找的页面。",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/preferences", "name": "@vben/preferences",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/stores", "name": "@vben/stores",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/styles", "name": "@vben/styles",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -78,14 +78,6 @@ html {
--vxe-ui-border-radius: 8px !important; --vxe-ui-border-radius: 8px !important;
} }
/**
vxe表格头部背景色 与antd保持一致 只需要处理light模式 夜间模式用vxe默认的
*/
html[data-vxe-ui-theme='light'] {
/** 只支持hsl格式 */
--vxe-ui-table-header-background-color: hsl(0deg 0% 98%) !important;
}
/** /**
vxe表格loading 只加载表格 不加载上面的表单 vxe表格loading 只加载表格 不加载上面的表单
*/ */

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/types", "name": "@vben/types",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/utils", "name": "@vben/utils",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/playground", "name": "@vben/playground",
"version": "5.4.8", "version": "5.5.0",
"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": {

View File

@@ -8,7 +8,7 @@ import type { BaseFormComponentType } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue'; import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { globalShareState } from '@vben/common-ui'; import { ApiSelect, globalShareState, IconPicker } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { import {
@@ -48,12 +48,14 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType = export type ComponentType =
| 'ApiSelect'
| 'AutoComplete' | 'AutoComplete'
| 'Checkbox' | 'Checkbox'
| 'CheckboxGroup' | 'CheckboxGroup'
| 'DatePicker' | 'DatePicker'
| 'DefaultButton' | 'DefaultButton'
| 'Divider' | 'Divider'
| 'IconPicker'
| 'Input' | 'Input'
| 'InputNumber' | 'InputNumber'
| 'InputPassword' | 'InputPassword'
@@ -78,6 +80,20 @@ async function initComponentAdapter() {
// Button: () => // Button: () =>
// import('xxx').then((res) => res.Button), // import('xxx').then((res) => res.Button),
ApiSelect: (props, { attrs, slots }) => {
return h(
ApiSelect,
{
...props,
...attrs,
component: Select,
loadingSlot: 'suffixIcon',
modelField: 'value',
visibleEvent: 'onVisibleChange',
},
slots,
);
},
AutoComplete, AutoComplete,
Checkbox, Checkbox,
CheckboxGroup, CheckboxGroup,
@@ -87,6 +103,7 @@ async function initComponentAdapter() {
return h(Button, { ...props, attrs, type: 'default' }, slots); return h(Button, { ...props, attrs, type: 'default' }, slots);
}, },
Divider, Divider,
IconPicker,
Input: withDefaultPlaceholder(Input, 'input'), Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'),

View File

@@ -79,17 +79,17 @@ async function changeAccount(role: string) {
<Card class="mb-5" title="组件形式控制 - 权限码"> <Card class="mb-5" title="组件形式控制 - 权限码">
<AccessControl :codes="['AC_100100']" type="code"> <AccessControl :codes="['AC_100100']" type="code">
<Button class="mr-4"> Super 账号可见 ["AC_1000001"] </Button> <Button class="mr-4"> Super 账号可见 ["AC_100100"] </Button>
</AccessControl> </AccessControl>
<AccessControl :codes="['AC_100030']" type="code"> <AccessControl :codes="['AC_100030']" type="code">
<Button class="mr-4"> Admin 账号可见 ["AC_100010"] </Button> <Button class="mr-4"> Admin 账号可见 ["AC_100030"] </Button>
</AccessControl> </AccessControl>
<AccessControl :codes="['AC_1000001']" type="code"> <AccessControl :codes="['AC_1000001']" type="code">
<Button class="mr-4"> User 账号可见 ["AC_1000001"] </Button> <Button class="mr-4"> User 账号可见 ["AC_1000001"] </Button>
</AccessControl> </AccessControl>
<AccessControl :codes="['AC_100100', 'AC_100010']" type="code"> <AccessControl :codes="['AC_100100', 'AC_100030']" type="code">
<Button class="mr-4"> <Button class="mr-4">
Super & Admin 账号可见 ["AC_100100","AC_1000001"] Super & Admin 账号可见 ["AC_100100","AC_100030"]
</Button> </Button>
</AccessControl> </AccessControl>
</Card> </Card>
@@ -115,35 +115,35 @@ async function changeAccount(role: string) {
<Card class="mb-5" title="函数形式控制"> <Card class="mb-5" title="函数形式控制">
<Button v-if="hasAccessByCodes(['AC_100100'])" class="mr-4"> <Button v-if="hasAccessByCodes(['AC_100100'])" class="mr-4">
Super 账号可见 ["AC_1000001"] Super 账号可见 ["AC_100100"]
</Button> </Button>
<Button v-if="hasAccessByCodes(['AC_100030'])" class="mr-4"> <Button v-if="hasAccessByCodes(['AC_100030'])" class="mr-4">
Admin 账号可见 ["AC_100010"] Admin 账号可见 ["AC_100030"]
</Button> </Button>
<Button v-if="hasAccessByCodes(['AC_1000001'])" class="mr-4"> <Button v-if="hasAccessByCodes(['AC_1000001'])" class="mr-4">
User 账号可见 ["AC_1000001"] User 账号可见 ["AC_1000001"]
</Button> </Button>
<Button v-if="hasAccessByCodes(['AC_100100', 'AC_1000001'])" class="mr-4"> <Button v-if="hasAccessByCodes(['AC_100100', 'AC_100030'])" class="mr-4">
Super & Admin 账号可见 ["AC_100100","AC_1000001"] Super & Admin 账号可见 ["AC_100100","AC_100030"]
</Button> </Button>
</Card> </Card>
<Card class="mb-5" title="指令方式 - 权限码"> <Card class="mb-5" title="指令方式 - 权限码">
<Button class="mr-4" v-access:code="['AC_100100']"> <Button class="mr-4" v-access:code="['AC_100100']">
Super 账号可见 ["AC_1000001"] Super 账号可见 ["AC_100100"]
</Button> </Button>
<Button class="mr-4" v-access:code="['AC_100030']"> <Button class="mr-4" v-access:code="['AC_100030']">
Admin 账号可见 ["AC_100010"] Admin 账号可见 ["AC_100030"]
</Button> </Button>
<Button class="mr-4" v-access:code="['AC_1000001']"> <Button class="mr-4" v-access:code="['AC_1000001']">
User 账号可见 ["AC_1000001"] User 账号可见 ["AC_1000001"]
</Button> </Button>
<Button class="mr-4" v-access:code="['AC_100100', 'AC_1000001']"> <Button class="mr-4" v-access:code="['AC_100100', 'AC_100030']">
Super & Admin 账号可见 ["AC_100100","AC_1000001"] Super & Admin 账号可见 ["AC_100100","AC_100030"]
</Button> </Button>
</Card> </Card>
<Card class="mb-5" title="指令方式 - 角色"> <Card v-if="accessMode === 'frontend'" class="mb-5" title="指令方式 - 角色">
<Button class="mr-4" v-access:role="['super']"> Super 角色可见 </Button> <Button class="mr-4" v-access:role="['super']"> Super 角色可见 </Button>
<Button class="mr-4" v-access:role="['admin']"> Admin 角色可见 </Button> <Button class="mr-4" v-access:role="['admin']"> Admin 角色可见 </Button>
<Button class="mr-4" v-access:role="['user']"> User 角色可见 </Button> <Button class="mr-4" v-access:role="['user']"> User 角色可见 </Button>

View File

@@ -1,87 +0,0 @@
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { IconPicker } from '@vben/common-ui';
import { listIcons } from '@vben/icons';
import { Input } from 'ant-design-vue';
import iconsData from './icons.data';
export interface Props {
allowClear?: boolean;
pageSize?: number;
/**
* 可以通过prefix获取系统中使用的图标集
*/
prefix?: string;
readonly?: boolean;
value?: string;
width?: string;
}
// Don't inherit FormItem disabled、placeholder...
defineOptions({
inheritAttrs: false,
});
const props = withDefaults(defineProps<Props>(), {
allowClear: true,
pageSize: 36,
prefix: '',
readonly: false,
value: '',
width: '100%',
});
const refIconPicker = ref();
const currentSelect = ref('');
const currentList = computed(() => {
try {
if (props.prefix) {
const icons = listIcons('', props.prefix);
if (icons.length === 0) {
console.warn(`No icons found for prefix: ${props.prefix}`);
}
return icons;
} else {
const prefix = iconsData.prefix;
return iconsData.icons.map((icon) => `${prefix}:${icon}`);
}
} catch (error) {
console.error('Failed to load icons:', error);
return [];
}
});
const triggerPopover = () => {
refIconPicker.value?.changeOpenState?.();
};
const handleChange = (icon: string) => {
currentSelect.value = icon;
};
</script>
<template>
<Input
v-model:value="currentSelect"
:allow-clear="props.allowClear"
:readonly="props.readonly"
:style="{ width }"
class="cursor-pointer"
placeholder="点击选中图标"
@click="triggerPopover"
>
<template #addonAfter>
<IconPicker
ref="refIconPicker"
:icons="currentList"
:page-size="pageSize"
:value="currentSelect"
@change="handleChange"
/>
</template>
</Input>
</template>

View File

@@ -1,793 +0,0 @@
export default {
icons: [
'account-book-filled',
'account-book-outlined',
'account-book-twotone',
'aim-outlined',
'alert-filled',
'alert-outlined',
'alert-twotone',
'alibaba-outlined',
'align-center-outlined',
'align-left-outlined',
'align-right-outlined',
'alipay-circle-filled',
'alipay-circle-outlined',
'alipay-outlined',
'alipay-square-filled',
'aliwangwang-filled',
'aliwangwang-outlined',
'aliyun-outlined',
'amazon-circle-filled',
'amazon-outlined',
'amazon-square-filled',
'android-filled',
'android-outlined',
'ant-cloud-outlined',
'ant-design-outlined',
'apartment-outlined',
'api-filled',
'api-outlined',
'api-twotone',
'apple-filled',
'apple-outlined',
'appstore-add-outlined',
'appstore-filled',
'appstore-outlined',
'appstore-twotone',
'area-chart-outlined',
'arrow-down-outlined',
'arrow-left-outlined',
'arrow-right-outlined',
'arrow-up-outlined',
'arrows-alt-outlined',
'audio-filled',
'audio-muted-outlined',
'audio-outlined',
'audio-twotone',
'audit-outlined',
'backward-filled',
'backward-outlined',
'bank-filled',
'bank-outlined',
'bank-twotone',
'bar-chart-outlined',
'barcode-outlined',
'bars-outlined',
'behance-circle-filled',
'behance-outlined',
'behance-square-filled',
'behance-square-outlined',
'bell-filled',
'bell-outlined',
'bell-twotone',
'bg-colors-outlined',
'block-outlined',
'bold-outlined',
'book-filled',
'book-outlined',
'book-twotone',
'border-bottom-outlined',
'border-horizontal-outlined',
'border-inner-outlined',
'border-left-outlined',
'border-outer-outlined',
'border-outlined',
'border-right-outlined',
'border-top-outlined',
'border-verticle-outlined',
'borderless-table-outlined',
'box-plot-filled',
'box-plot-outlined',
'box-plot-twotone',
'branches-outlined',
'bug-filled',
'bug-outlined',
'bug-twotone',
'build-filled',
'build-outlined',
'build-twotone',
'bulb-filled',
'bulb-outlined',
'bulb-twotone',
'calculator-filled',
'calculator-outlined',
'calculator-twotone',
'calendar-filled',
'calendar-outlined',
'calendar-twotone',
'camera-filled',
'camera-outlined',
'camera-twotone',
'car-filled',
'car-outlined',
'car-twotone',
'caret-down-filled',
'caret-down-outlined',
'caret-left-filled',
'caret-left-outlined',
'caret-right-filled',
'caret-right-outlined',
'caret-up-filled',
'caret-up-outlined',
'carry-out-filled',
'carry-out-outlined',
'carry-out-twotone',
'check-circle-filled',
'check-circle-outlined',
'check-circle-twotone',
'check-outlined',
'check-square-filled',
'check-square-outlined',
'check-square-twotone',
'chrome-filled',
'chrome-outlined',
'ci-circle-filled',
'ci-circle-outlined',
'ci-circle-twotone',
'ci-outlined',
'ci-twotone',
'clear-outlined',
'clock-circle-filled',
'clock-circle-outlined',
'clock-circle-twotone',
'close-circle-filled',
'close-circle-outlined',
'close-circle-twotone',
'close-outlined',
'close-square-filled',
'close-square-outlined',
'close-square-twotone',
'cloud-download-outlined',
'cloud-filled',
'cloud-outlined',
'cloud-server-outlined',
'cloud-sync-outlined',
'cloud-twotone',
'cloud-upload-outlined',
'cluster-outlined',
'code-filled',
'code-outlined',
'code-sandbox-circle-filled',
'code-sandbox-outlined',
'code-sandbox-square-filled',
'code-twotone',
'codepen-circle-filled',
'codepen-circle-outlined',
'codepen-outlined',
'codepen-square-filled',
'coffee-outlined',
'column-height-outlined',
'column-width-outlined',
'comment-outlined',
'compass-filled',
'compass-outlined',
'compass-twotone',
'compress-outlined',
'console-sql-outlined',
'contacts-filled',
'contacts-outlined',
'contacts-twotone',
'container-filled',
'container-outlined',
'container-twotone',
'control-filled',
'control-outlined',
'control-twotone',
'copy-filled',
'copy-outlined',
'copy-twotone',
'copyright-circle-filled',
'copyright-circle-outlined',
'copyright-circle-twotone',
'copyright-outlined',
'copyright-twotone',
'credit-card-filled',
'credit-card-outlined',
'credit-card-twotone',
'crown-filled',
'crown-outlined',
'crown-twotone',
'customer-service-filled',
'customer-service-outlined',
'customer-service-twotone',
'dash-outlined',
'dashboard-filled',
'dashboard-outlined',
'dashboard-twotone',
'database-filled',
'database-outlined',
'database-twotone',
'delete-column-outlined',
'delete-filled',
'delete-outlined',
'delete-row-outlined',
'delete-twotone',
'delivered-procedure-outlined',
'deployment-unit-outlined',
'desktop-outlined',
'diff-filled',
'diff-outlined',
'diff-twotone',
'dingding-outlined',
'dingtalk-circle-filled',
'dingtalk-outlined',
'dingtalk-square-filled',
'disconnect-outlined',
'dislike-filled',
'dislike-outlined',
'dislike-twotone',
'dollar-circle-filled',
'dollar-circle-outlined',
'dollar-circle-twotone',
'dollar-outlined',
'dollar-twotone',
'dot-chart-outlined',
'double-left-outlined',
'double-right-outlined',
'down-circle-filled',
'down-circle-outlined',
'down-circle-twotone',
'down-outlined',
'down-square-filled',
'down-square-outlined',
'down-square-twotone',
'download-outlined',
'drag-outlined',
'dribbble-circle-filled',
'dribbble-outlined',
'dribbble-square-filled',
'dribbble-square-outlined',
'dropbox-circle-filled',
'dropbox-outlined',
'dropbox-square-filled',
'edit-filled',
'edit-outlined',
'edit-twotone',
'ellipsis-outlined',
'enter-outlined',
'environment-filled',
'environment-outlined',
'environment-twotone',
'euro-circle-filled',
'euro-circle-outlined',
'euro-circle-twotone',
'euro-outlined',
'euro-twotone',
'exception-outlined',
'exclamation-circle-filled',
'exclamation-circle-outlined',
'exclamation-circle-twotone',
'exclamation-outlined',
'expand-alt-outlined',
'expand-outlined',
'experiment-filled',
'experiment-outlined',
'experiment-twotone',
'export-outlined',
'eye-filled',
'eye-invisible-filled',
'eye-invisible-outlined',
'eye-invisible-twotone',
'eye-outlined',
'eye-twotone',
'facebook-filled',
'facebook-outlined',
'fall-outlined',
'fast-backward-filled',
'fast-backward-outlined',
'fast-forward-filled',
'fast-forward-outlined',
'field-binary-outlined',
'field-number-outlined',
'field-string-outlined',
'field-time-outlined',
'file-add-filled',
'file-add-outlined',
'file-add-twotone',
'file-done-outlined',
'file-excel-filled',
'file-excel-outlined',
'file-excel-twotone',
'file-exclamation-filled',
'file-exclamation-outlined',
'file-exclamation-twotone',
'file-filled',
'file-gif-outlined',
'file-image-filled',
'file-image-outlined',
'file-image-twotone',
'file-jpg-outlined',
'file-markdown-filled',
'file-markdown-outlined',
'file-markdown-twotone',
'file-outlined',
'file-pdf-filled',
'file-pdf-outlined',
'file-pdf-twotone',
'file-ppt-filled',
'file-ppt-outlined',
'file-ppt-twotone',
'file-protect-outlined',
'file-search-outlined',
'file-sync-outlined',
'file-text-filled',
'file-text-outlined',
'file-text-twotone',
'file-twotone',
'file-unknown-filled',
'file-unknown-outlined',
'file-unknown-twotone',
'file-word-filled',
'file-word-outlined',
'file-word-twotone',
'file-zip-filled',
'file-zip-outlined',
'file-zip-twotone',
'filter-filled',
'filter-outlined',
'filter-twotone',
'fire-filled',
'fire-outlined',
'fire-twotone',
'flag-filled',
'flag-outlined',
'flag-twotone',
'folder-add-filled',
'folder-add-outlined',
'folder-add-twotone',
'folder-filled',
'folder-open-filled',
'folder-open-outlined',
'folder-open-twotone',
'folder-outlined',
'folder-twotone',
'folder-view-outlined',
'font-colors-outlined',
'font-size-outlined',
'fork-outlined',
'form-outlined',
'format-painter-filled',
'format-painter-outlined',
'forward-filled',
'forward-outlined',
'frown-filled',
'frown-outlined',
'frown-twotone',
'fullscreen-exit-outlined',
'fullscreen-outlined',
'function-outlined',
'fund-filled',
'fund-outlined',
'fund-projection-screen-outlined',
'fund-twotone',
'fund-view-outlined',
'funnel-plot-filled',
'funnel-plot-outlined',
'funnel-plot-twotone',
'gateway-outlined',
'gif-outlined',
'gift-filled',
'gift-outlined',
'gift-twotone',
'github-filled',
'github-outlined',
'gitlab-filled',
'gitlab-outlined',
'global-outlined',
'gold-filled',
'gold-outlined',
'gold-twotone',
'golden-filled',
'google-circle-filled',
'google-outlined',
'google-plus-circle-filled',
'google-plus-outlined',
'google-plus-square-filled',
'google-square-filled',
'group-outlined',
'hdd-filled',
'hdd-outlined',
'hdd-twotone',
'heart-filled',
'heart-outlined',
'heart-twotone',
'heat-map-outlined',
'highlight-filled',
'highlight-outlined',
'highlight-twotone',
'history-outlined',
'home-filled',
'home-outlined',
'home-twotone',
'hourglass-filled',
'hourglass-outlined',
'hourglass-twotone',
'html5-filled',
'html5-outlined',
'html5-twotone',
'idcard-filled',
'idcard-outlined',
'idcard-twotone',
'ie-circle-filled',
'ie-outlined',
'ie-square-filled',
'import-outlined',
'inbox-outlined',
'info-circle-filled',
'info-circle-outlined',
'info-circle-twotone',
'info-outlined',
'insert-row-above-outlined',
'insert-row-below-outlined',
'insert-row-left-outlined',
'insert-row-right-outlined',
'instagram-filled',
'instagram-outlined',
'insurance-filled',
'insurance-outlined',
'insurance-twotone',
'interaction-filled',
'interaction-outlined',
'interaction-twotone',
'issues-close-outlined',
'italic-outlined',
'key-outlined',
'laptop-outlined',
'layout-filled',
'layout-outlined',
'layout-twotone',
'left-circle-filled',
'left-circle-outlined',
'left-circle-twotone',
'left-outlined',
'left-square-filled',
'left-square-outlined',
'left-square-twotone',
'like-filled',
'like-outlined',
'like-twotone',
'line-chart-outlined',
'line-height-outlined',
'line-outlined',
'link-outlined',
'linkedin-filled',
'linkedin-outlined',
'loading-3-quarters-outlined',
'loading-outlined',
'lock-filled',
'lock-outlined',
'lock-twotone',
'login-outlined',
'logout-outlined',
'mac-command-filled',
'mac-command-outlined',
'mail-filled',
'mail-outlined',
'mail-twotone',
'man-outlined',
'medicine-box-filled',
'medicine-box-outlined',
'medicine-box-twotone',
'medium-circle-filled',
'medium-outlined',
'medium-square-filled',
'medium-workmark-outlined',
'meh-filled',
'meh-outlined',
'meh-twotone',
'menu-fold-outlined',
'menu-outlined',
'menu-unfold-outlined',
'merge-cells-outlined',
'message-filled',
'message-outlined',
'message-twotone',
'minus-circle-filled',
'minus-circle-outlined',
'minus-circle-twotone',
'minus-outlined',
'minus-square-filled',
'minus-square-outlined',
'minus-square-twotone',
'mobile-filled',
'mobile-outlined',
'mobile-twotone',
'money-collect-filled',
'money-collect-outlined',
'money-collect-twotone',
'monitor-outlined',
'more-outlined',
'node-collapse-outlined',
'node-expand-outlined',
'node-index-outlined',
'notification-filled',
'notification-outlined',
'notification-twotone',
'number-outlined',
'one-to-one-outlined',
'ordered-list-outlined',
'paper-clip-outlined',
'partition-outlined',
'pause-circle-filled',
'pause-circle-outlined',
'pause-circle-twotone',
'pause-outlined',
'pay-circle-filled',
'pay-circle-outlined',
'percentage-outlined',
'phone-filled',
'phone-outlined',
'phone-twotone',
'pic-center-outlined',
'pic-left-outlined',
'pic-right-outlined',
'picture-filled',
'picture-outlined',
'picture-twotone',
'pie-chart-filled',
'pie-chart-outlined',
'pie-chart-twotone',
'play-circle-filled',
'play-circle-outlined',
'play-circle-twotone',
'play-square-filled',
'play-square-outlined',
'play-square-twotone',
'plus-circle-filled',
'plus-circle-outlined',
'plus-circle-twotone',
'plus-outlined',
'plus-square-filled',
'plus-square-outlined',
'plus-square-twotone',
'pound-circle-filled',
'pound-circle-outlined',
'pound-circle-twotone',
'pound-outlined',
'poweroff-outlined',
'printer-filled',
'printer-outlined',
'printer-twotone',
'profile-filled',
'profile-outlined',
'profile-twotone',
'project-filled',
'project-outlined',
'project-twotone',
'property-safety-filled',
'property-safety-outlined',
'property-safety-twotone',
'pull-request-outlined',
'pushpin-filled',
'pushpin-outlined',
'pushpin-twotone',
'qq-circle-filled',
'qq-outlined',
'qq-square-filled',
'qrcode-outlined',
'question-circle-filled',
'question-circle-outlined',
'question-circle-twotone',
'question-outlined',
'radar-chart-outlined',
'radius-bottomleft-outlined',
'radius-bottomright-outlined',
'radius-setting-outlined',
'radius-upleft-outlined',
'radius-upright-outlined',
'read-filled',
'read-outlined',
'reconciliation-filled',
'reconciliation-outlined',
'reconciliation-twotone',
'red-envelope-filled',
'red-envelope-outlined',
'red-envelope-twotone',
'reddit-circle-filled',
'reddit-outlined',
'reddit-square-filled',
'redo-outlined',
'reload-outlined',
'rest-filled',
'rest-outlined',
'rest-twotone',
'retweet-outlined',
'right-circle-filled',
'right-circle-outlined',
'right-circle-twotone',
'right-outlined',
'right-square-filled',
'right-square-outlined',
'right-square-twotone',
'rise-outlined',
'robot-filled',
'robot-outlined',
'rocket-filled',
'rocket-outlined',
'rocket-twotone',
'rollback-outlined',
'rotate-left-outlined',
'rotate-right-outlined',
'safety-certificate-filled',
'safety-certificate-outlined',
'safety-certificate-twotone',
'safety-outlined',
'save-filled',
'save-outlined',
'save-twotone',
'scan-outlined',
'schedule-filled',
'schedule-outlined',
'schedule-twotone',
'scissor-outlined',
'search-outlined',
'security-scan-filled',
'security-scan-outlined',
'security-scan-twotone',
'select-outlined',
'send-outlined',
'setting-filled',
'setting-outlined',
'setting-twotone',
'shake-outlined',
'share-alt-outlined',
'shop-filled',
'shop-outlined',
'shop-twotone',
'shopping-cart-outlined',
'shopping-filled',
'shopping-outlined',
'shopping-twotone',
'shrink-outlined',
'signal-filled',
'sisternode-outlined',
'sketch-circle-filled',
'sketch-outlined',
'sketch-square-filled',
'skin-filled',
'skin-outlined',
'skin-twotone',
'skype-filled',
'skype-outlined',
'slack-circle-filled',
'slack-outlined',
'slack-square-filled',
'slack-square-outlined',
'sliders-filled',
'sliders-outlined',
'sliders-twotone',
'small-dash-outlined',
'smile-filled',
'smile-outlined',
'smile-twotone',
'snippets-filled',
'snippets-outlined',
'snippets-twotone',
'solution-outlined',
'sort-ascending-outlined',
'sort-descending-outlined',
'sound-filled',
'sound-outlined',
'sound-twotone',
'split-cells-outlined',
'star-filled',
'star-outlined',
'star-twotone',
'step-backward-filled',
'step-backward-outlined',
'step-forward-filled',
'step-forward-outlined',
'stock-outlined',
'stop-filled',
'stop-outlined',
'stop-twotone',
'strikethrough-outlined',
'subnode-outlined',
'swap-left-outlined',
'swap-outlined',
'swap-right-outlined',
'switcher-filled',
'switcher-outlined',
'switcher-twotone',
'sync-outlined',
'table-outlined',
'tablet-filled',
'tablet-outlined',
'tablet-twotone',
'tag-filled',
'tag-outlined',
'tag-twotone',
'tags-filled',
'tags-outlined',
'tags-twotone',
'taobao-circle-filled',
'taobao-circle-outlined',
'taobao-outlined',
'taobao-square-filled',
'team-outlined',
'thunderbolt-filled',
'thunderbolt-outlined',
'thunderbolt-twotone',
'to-top-outlined',
'tool-filled',
'tool-outlined',
'tool-twotone',
'trademark-circle-filled',
'trademark-circle-outlined',
'trademark-circle-twotone',
'trademark-outlined',
'transaction-outlined',
'translation-outlined',
'trophy-filled',
'trophy-outlined',
'trophy-twotone',
'twitter-circle-filled',
'twitter-outlined',
'twitter-square-filled',
'underline-outlined',
'undo-outlined',
'ungroup-outlined',
'unlock-filled',
'unlock-outlined',
'unlock-twotone',
'unordered-list-outlined',
'up-circle-filled',
'up-circle-outlined',
'up-circle-twotone',
'up-outlined',
'up-square-filled',
'up-square-outlined',
'up-square-twotone',
'upload-outlined',
'usb-filled',
'usb-outlined',
'usb-twotone',
'user-add-outlined',
'user-delete-outlined',
'user-outlined',
'user-switch-outlined',
'usergroup-add-outlined',
'usergroup-delete-outlined',
'verified-outlined',
'vertical-align-bottom-outlined',
'vertical-align-middle-outlined',
'vertical-align-top-outlined',
'vertical-left-outlined',
'vertical-right-outlined',
'video-camera-add-outlined',
'video-camera-filled',
'video-camera-outlined',
'video-camera-twotone',
'wallet-filled',
'wallet-outlined',
'wallet-twotone',
'warning-filled',
'warning-outlined',
'warning-twotone',
'wechat-filled',
'wechat-outlined',
'weibo-circle-filled',
'weibo-circle-outlined',
'weibo-outlined',
'weibo-square-filled',
'weibo-square-outlined',
'whats-app-outlined',
'wifi-outlined',
'windows-filled',
'windows-outlined',
'woman-outlined',
'yahoo-filled',
'yahoo-outlined',
'youtube-filled',
'youtube-outlined',
'yuque-filled',
'yuque-outlined',
'zhihu-circle-filled',
'zhihu-outlined',
'zhihu-square-filled',
'zoom-in-outlined',
'zoom-out-outlined',
],
prefix: 'ant-design',
};

View File

@@ -1,6 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Page } from '@vben/common-ui'; import { ref } from 'vue';
import { IconPicker, Page } from '@vben/common-ui';
import { import {
IconifyIcon,
MdiGithub, MdiGithub,
MdiGoogle, MdiGoogle,
MdiKeyboardEsc, MdiKeyboardEsc,
@@ -16,9 +19,9 @@ import {
SvgDownloadIcon, SvgDownloadIcon,
} from '@vben/icons'; } from '@vben/icons';
import { Card } from 'ant-design-vue'; import { Card, Input } from 'ant-design-vue';
import IconPicker from './icon-picker.vue'; const iconValue = ref('ant-design:trademark-outlined');
</script> </script>
<template> <template>
@@ -60,15 +63,64 @@ import IconPicker from './icon-picker.vue';
</div> </div>
</Card> </Card>
<Card class="mb-5" title="图标选择器(Iconify)"> <Card class="mb-5" title="Tailwind CSS">
<div class="flex items-center gap-5"> <div class="flex items-center gap-5 text-3xl">
<IconPicker width="300px" /> <span class="icon-[ant-design--alipay-circle-outlined]"></span>
<span class="icon-[ant-design--account-book-filled]"></span>
<span class="icon-[ant-design--container-outlined]"></span>
<span class="icon-[svg-spinners--wind-toy]"></span>
<span class="icon-[svg-spinners--blocks-wave]"></span>
<span class="icon-[line-md--compass-filled-loop]"></span>
</div> </div>
</Card> </Card>
<Card title="图标选择器(Svg)"> <Card class="mb-5" title="图标选择器">
<div class="mb-5 flex items-center gap-5">
<span>原始样式(Iconify):</span>
<IconPicker class="w-[200px]" />
</div>
<div class="mb-5 flex items-center gap-5">
<span>原始样式(svg):</span>
<IconPicker class="w-[200px]" prefix="svg" />
</div>
<div class="mb-5 flex items-center gap-5">
<span>完整替换触发组件:</span>
<IconPicker class="w-[200px]">
<template #trigger="{ icon }">
<Input
:value="icon"
placeholder="点击这里选择图标"
style="width: 300px"
>
<template #addonAfter>
<IconifyIcon
:icon="icon || 'ant-design:appstore-filled'"
class="text-2xl"
/>
</template>
</Input>
</template>
</IconPicker>
</div>
<div class="flex items-center gap-5"> <div class="flex items-center gap-5">
<IconPicker prefix="svg" width="300px" /> <span>可手动输入只能点击图标打开弹窗:</span>
<Input
v-model:value="iconValue"
allow-clear
placeholder="点击这里选择图标"
style="width: 300px"
>
<template #addonAfter>
<IconPicker v-model="iconValue" class="w-[200px]">
<template #trigger="{ icon }">
<IconifyIcon
:icon="icon || 'ant-design:appstore-filled'"
class="text-2xl"
/>
</template>
</IconPicker>
</template>
</Input>
</div> </div>
</Card> </Card>
</Page> </Page>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Page, useVbenDrawer } from '@vben/common-ui'; import { type DrawerPlacement, Page, useVbenDrawer } from '@vben/common-ui';
import { Button, Card } from 'ant-design-vue'; import { Button, Card } from 'ant-design-vue';
@@ -13,6 +13,7 @@ import SharedDataDemo from './shared-data-demo.vue';
const [BaseDrawer, baseDrawerApi] = useVbenDrawer({ const [BaseDrawer, baseDrawerApi] = useVbenDrawer({
// 连接抽离的组件 // 连接抽离的组件
connectedComponent: BaseDemo, connectedComponent: BaseDemo,
// placement: 'left',
}); });
const [AutoHeightDrawer, autoHeightDrawerApi] = useVbenDrawer({ const [AutoHeightDrawer, autoHeightDrawerApi] = useVbenDrawer({
@@ -31,7 +32,8 @@ const [FormDrawer, formDrawerApi] = useVbenDrawer({
connectedComponent: FormDrawerDemo, connectedComponent: FormDrawerDemo,
}); });
function openBaseDrawer() { function openBaseDrawer(placement: DrawerPlacement = 'right') {
baseDrawerApi.setState({ placement });
baseDrawerApi.open(); baseDrawerApi.open();
} }
@@ -81,7 +83,16 @@ function openFormDrawer() {
<Card class="mb-4" title="基本使用"> <Card class="mb-4" title="基本使用">
<p class="mb-3">一个基础的抽屉示例</p> <p class="mb-3">一个基础的抽屉示例</p>
<Button type="primary" @click="openBaseDrawer">打开抽屉</Button> <Button type="primary" @click="openBaseDrawer('right')">右侧打开</Button>
<Button class="ml-2" type="primary" @click="openBaseDrawer('bottom')">
底部打开
</Button>
<Button class="ml-2" type="primary" @click="openBaseDrawer('left')">
左侧打开
</Button>
<Button class="ml-2" type="primary" @click="openBaseDrawer('top')">
顶部打开
</Button>
</Card> </Card>
<Card class="mb-4" title="内容高度自适应滚动"> <Card class="mb-4" title="内容高度自适应滚动">

View File

@@ -1,13 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { Button, Card, message } from 'ant-design-vue'; import { Button, Card, message, TabPane, Tabs } from 'ant-design-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';
import { getAllMenusApi } from '#/api';
import DocButton from '../doc-button.vue'; import DocButton from '../doc-button.vue';
const activeTab = ref('basic');
const [BaseForm, baseFormApi] = useVbenForm({ const [BaseForm, baseFormApi] = useVbenForm({
// 所有表单项共用,可单独在表单内覆盖 // 所有表单项共用,可单独在表单内覆盖
commonConfig: { commonConfig: {
@@ -36,6 +41,27 @@ const [BaseForm, baseFormApi] = useVbenForm({
// 界面显示的label // 界面显示的label
label: '字符串', label: '字符串',
}, },
{
// 组件需要在 #/adapter.ts内注册并加上类型
component: 'ApiSelect',
// 对应组件的参数
componentProps: {
// 菜单接口转options格式
afterFetch: (data: { name: string; path: string }[]) => {
return data.map((item: any) => ({
label: item.name,
value: item.path,
}));
},
// 菜单接口
api: getAllMenusApi,
placeholder: '请选择',
},
// 字段名
fieldName: 'api',
// 界面显示的label
label: 'ApiSelect',
},
{ {
component: 'InputPassword', component: 'InputPassword',
componentProps: { componentProps: {
@@ -53,6 +79,11 @@ const [BaseForm, baseFormApi] = useVbenForm({
label: '数字(带后缀)', label: '数字(带后缀)',
suffix: () => '¥', suffix: () => '¥',
}, },
{
component: 'IconPicker',
fieldName: 'icon',
label: '图标',
},
{ {
component: 'Select', component: 'Select',
componentProps: { componentProps: {
@@ -331,18 +362,31 @@ function handleSetFormValue() {
<Page <Page
content-class="flex flex-col gap-4" content-class="flex flex-col gap-4"
description="表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。" description="表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。"
fixed-header
header-class="pb-0"
title="表单组件" title="表单组件"
> >
<template #description>
<div class="text-muted-foreground">
<p>
表单组件基础示例请注意该页面用到的参数代码会添加一些简单注释方便理解请仔细查看
</p>
</div>
<Tabs v-model:active-key="activeTab" :tab-bar-style="{ marginBottom: 0 }">
<TabPane key="basic" tab="基础示例" />
<TabPane key="layout" tab="自定义布局" />
</Tabs>
</template>
<template #extra> <template #extra>
<DocButton path="/components/common-ui/vben-form" /> <DocButton path="/components/common-ui/vben-form" />
</template> </template>
<Card title="基础示例"> <Card v-show="activeTab === 'basic'" title="基础示例">
<template #extra> <template #extra>
<Button type="primary" @click="handleSetFormValue">设置表单值</Button> <Button type="primary" @click="handleSetFormValue">设置表单值</Button>
</template> </template>
<BaseForm /> <BaseForm />
</Card> </Card>
<Card title="使用tailwind自定义布局"> <Card v-show="activeTab === 'layout'" title="使用tailwind自定义布局">
<CustomLayoutForm /> <CustomLayoutForm />
</Card> </Card>
</Page> </Page>

View File

@@ -22,10 +22,10 @@ const [Modal, modalApi] = useVbenModal({
}); });
function handleUpdate(len: number) { function handleUpdate(len: number) {
modalApi.setState({ loading: true }); modalApi.setState({ confirmDisabled: true, loading: true });
setTimeout(() => { setTimeout(() => {
list.value = Array.from({ length: len }, (_v, k) => k + 1); list.value = Array.from({ length: len }, (_v, k) => k + 1);
modalApi.setState({ loading: false }); modalApi.setState({ confirmDisabled: false, loading: false });
}, 2000); }, 2000);
} }
</script> </script>

View File

@@ -77,6 +77,7 @@ function openFormModal() {
<template> <template>
<Page <Page
description="弹窗组件常用于在不离开当前页面的情况下显示额外的信息、表单或操作提示更多api请查看组件文档。" description="弹窗组件常用于在不离开当前页面的情况下显示额外的信息、表单或操作提示更多api请查看组件文档。"
fixed-header
title="弹窗组件示例" title="弹窗组件示例"
> >
<template #extra> <template #extra>

View File

@@ -13,7 +13,7 @@ packages:
- docs - docs
- playground - playground
catalog: catalog:
'@ast-grep/napi': ^0.30.0 '@ast-grep/napi': ^0.31.0
'@changesets/changelog-github': ^0.5.0 '@changesets/changelog-github': ^0.5.0
'@changesets/cli': ^2.27.10 '@changesets/cli': ^2.27.10
'@changesets/git': ^3.0.2 '@changesets/git': ^3.0.2
@@ -21,74 +21,76 @@ catalog:
'@commitlint/cli': ^19.6.0 '@commitlint/cli': ^19.6.0
'@commitlint/config-conventional': ^19.6.0 '@commitlint/config-conventional': ^19.6.0
'@ctrl/tinycolor': ^4.1.0 '@ctrl/tinycolor': ^4.1.0
'@eslint/js': ^9.15.0 '@eslint/js': ^9.16.0
'@faker-js/faker': ^9.2.0 '@faker-js/faker': ^9.3.0
'@iconify/json': ^2.2.275 '@iconify/json': ^2.2.279
'@iconify/tailwind': ^1.1.3 '@iconify/tailwind': ^1.1.3
'@iconify/vue': ^4.1.2 '@iconify/vue': ^4.1.2
'@intlify/core-base': ^10.0.4 '@intlify/core-base': ^10.0.5
'@intlify/unplugin-vue-i18n': ^6.0.0 '@intlify/unplugin-vue-i18n': ^6.0.0
'@jspm/generator': ^2.4.1 '@jspm/generator': ^2.4.1
'@manypkg/get-packages': ^2.2.2 '@manypkg/get-packages': ^2.2.2
'@nolebase/vitepress-plugin-git-changelog': ^2.10.0 '@nolebase/vitepress-plugin-git-changelog': ^2.11.1
'@playwright/test': ^1.49.0 '@playwright/test': ^1.49.0
'@pnpm/workspace.read-manifest': ^2.2.1 '@pnpm/workspace.read-manifest': ^1000.0.0
'@stylistic/stylelint-plugin': ^3.1.1 '@stylistic/stylelint-plugin': ^3.1.1
'@tailwindcss/nesting': 0.0.0-insiders.565cd3e '@tailwindcss/nesting': 0.0.0-insiders.565cd3e
'@tailwindcss/typography': ^0.5.15 '@tailwindcss/typography': ^0.5.15
'@tanstack/vue-query': ^5.61.3 '@tanstack/vue-query': ^5.62.2
'@tanstack/vue-store': ^0.5.7 '@tanstack/vue-store': ^0.6.0
'@types/archiver': ^6.0.3 '@types/archiver': ^6.0.3
'@types/eslint': ^9.6.1 '@types/eslint': ^9.6.1
'@types/html-minifier-terser': ^7.0.2 '@types/html-minifier-terser': ^7.0.2
'@types/jsonwebtoken': ^9.0.7 '@types/jsonwebtoken': ^9.0.7
'@types/lodash.clonedeep': ^4.5.9 '@types/lodash.clonedeep': ^4.5.9
'@types/node': ^22.9.3 '@types/lodash.get': ^4.4.9
'@types/lodash.isequal': ^4.5.8
'@types/node': ^22.10.1
'@types/nprogress': ^0.2.3 '@types/nprogress': ^0.2.3
'@types/postcss-import': ^14.0.3 '@types/postcss-import': ^14.0.3
'@types/qrcode': ^1.5.5 '@types/qrcode': ^1.5.5
'@types/sortablejs': ^1.15.8 '@types/sortablejs': ^1.15.8
'@typescript-eslint/eslint-plugin': ^8.15.0 '@typescript-eslint/eslint-plugin': ^8.17.0
'@typescript-eslint/parser': ^8.15.0 '@typescript-eslint/parser': ^8.17.0
'@vee-validate/zod': ^4.14.7 '@vee-validate/zod': ^4.14.7
'@vite-pwa/vitepress': ^0.5.3 '@vite-pwa/vitepress': ^0.5.3
'@vitejs/plugin-vue': ^5.2.0 '@vitejs/plugin-vue': ^5.2.1
'@vitejs/plugin-vue-jsx': ^4.1.0 '@vitejs/plugin-vue-jsx': ^4.1.1
'@vue/reactivity': ^3.5.13 '@vue/reactivity': ^3.5.13
'@vue/shared': ^3.5.13 '@vue/shared': ^3.5.13
'@vue/test-utils': ^2.4.6 '@vue/test-utils': ^2.4.6
'@vueuse/core': ^11.3.0 '@vueuse/core': ^12.0.0
'@vueuse/integrations': ^11.3.0 '@vueuse/integrations': ^12.0.0
ant-design-vue: ^4.2.6 ant-design-vue: ^4.2.6
archiver: ^7.0.1 archiver: ^7.0.1
autoprefixer: ^10.4.20 autoprefixer: ^10.4.20
axios: ^1.7.7 axios: ^1.7.9
axios-mock-adapter: ^2.1.0 axios-mock-adapter: ^2.1.0
cac: ^6.7.14 cac: ^6.7.14
chalk: ^5.3.0 chalk: ^5.3.0
cheerio: 1.0.0 cheerio: 1.0.0
circular-dependency-scanner: ^2.3.0 circular-dependency-scanner: ^2.3.0
class-variance-authority: ^0.7.0 class-variance-authority: ^0.7.1
clsx: ^2.1.1 clsx: ^2.1.1
commitlint-plugin-function-rules: ^4.0.1 commitlint-plugin-function-rules: ^4.0.1
consola: ^3.2.3 consola: ^3.2.3
cross-env: ^7.0.3 cross-env: ^7.0.3
cspell: 8.16.0 cspell: ^8.16.1
cssnano: ^7.0.6 cssnano: ^7.0.6
cz-git: ^1.11.0 cz-git: ^1.11.0
czg: ^1.11.0 czg: ^1.11.0
dayjs: ^1.11.13 dayjs: ^1.11.13
defu: ^6.1.4 defu: ^6.1.4
depcheck: ^1.4.7 depcheck: ^1.4.7
dotenv: ^16.4.5 dotenv: ^16.4.7
echarts: ^5.5.1 echarts: ^5.5.1
element-plus: ^2.8.8 element-plus: ^2.9.0
eslint: ^9.15.0 eslint: ^9.16.0
eslint-config-turbo: ^2.3.1 eslint-config-turbo: ^2.3.3
eslint-plugin-command: ^0.2.6 eslint-plugin-command: ^0.2.6
eslint-plugin-eslint-comments: ^3.2.0 eslint-plugin-eslint-comments: ^3.2.0
eslint-plugin-import-x: ^4.4.3 eslint-plugin-import-x: ^4.5.0
eslint-plugin-jsdoc: ^50.5.0 eslint-plugin-jsdoc: ^50.6.0
eslint-plugin-jsonc: ^2.18.2 eslint-plugin-jsonc: ^2.18.2
eslint-plugin-n: ^17.14.0 eslint-plugin-n: ^17.14.0
eslint-plugin-no-only-tests: ^3.3.0 eslint-plugin-no-only-tests: ^3.3.0
@@ -98,13 +100,13 @@ catalog:
eslint-plugin-unicorn: ^56.0.1 eslint-plugin-unicorn: ^56.0.1
eslint-plugin-unused-imports: ^4.1.4 eslint-plugin-unused-imports: ^4.1.4
eslint-plugin-vitest: ^0.5.4 eslint-plugin-vitest: ^0.5.4
eslint-plugin-vue: ^9.31.0 eslint-plugin-vue: ^9.32.0
execa: ^9.5.1 execa: ^9.5.1
find-up: ^7.0.0 find-up: ^7.0.0
get-port: ^7.1.0 get-port: ^7.1.0
globals: ^15.12.0 globals: ^15.13.0
h3: ^1.13.0 h3: ^1.13.0
happy-dom: ^15.11.6 happy-dom: ^15.11.7
html-minifier-terser: ^7.2.0 html-minifier-terser: ^7.2.0
husky: ^9.1.7 husky: ^9.1.7
is-ci: ^3.0.1 is-ci: ^3.0.1
@@ -112,9 +114,11 @@ catalog:
jsonwebtoken: ^9.0.2 jsonwebtoken: ^9.0.2
lint-staged: ^15.2.10 lint-staged: ^15.2.10
lodash.clonedeep: ^4.5.0 lodash.clonedeep: ^4.5.0
lucide-vue-next: ^0.460.0 lodash.get: ^4.4.2
lodash.isequal: ^4.5.0
lucide-vue-next: ^0.465.0
medium-zoom: ^1.1.0 medium-zoom: ^1.1.0
naive-ui: ^2.40.1 naive-ui: ^2.40.3
nitropack: ^2.10.4 nitropack: ^2.10.4
nprogress: ^0.2.0 nprogress: ^0.2.0
ora: ^8.1.1 ora: ^8.1.1
@@ -128,18 +132,18 @@ catalog:
postcss-import: ^16.1.0 postcss-import: ^16.1.0
postcss-preset-env: ^10.1.1 postcss-preset-env: ^10.1.1
postcss-scss: ^4.0.9 postcss-scss: ^4.0.9
prettier: ^3.3.3 prettier: ^3.4.2
prettier-plugin-tailwindcss: ^0.6.9 prettier-plugin-tailwindcss: ^0.6.9
publint: ^0.2.12 publint: ^0.2.12
qrcode: ^1.5.4 qrcode: ^1.5.4
radix-vue: ^1.9.10 radix-vue: ^1.9.10
resolve.exports: ^2.0.2 resolve.exports: ^2.0.3
rimraf: ^6.0.1 rimraf: ^6.0.1
rollup: ^4.27.4 rollup: ^4.28.0
rollup-plugin-visualizer: ^5.12.0 rollup-plugin-visualizer: ^5.12.0
sass: 1.80.6 sass: 1.80.6
sortablejs: ^1.15.4 sortablejs: ^1.15.6
stylelint: ^16.10.0 stylelint: ^16.11.0
stylelint-config-recess-order: ^5.1.1 stylelint-config-recess-order: ^5.1.1
stylelint-config-recommended: ^14.0.1 stylelint-config-recommended: ^14.0.1
stylelint-config-recommended-scss: ^14.1.0 stylelint-config-recommended-scss: ^14.1.0
@@ -148,29 +152,29 @@ catalog:
stylelint-order: ^6.0.4 stylelint-order: ^6.0.4
stylelint-prettier: ^5.0.2 stylelint-prettier: ^5.0.2
stylelint-scss: ^6.10.0 stylelint-scss: ^6.10.0
tailwind-merge: ^2.5.4 tailwind-merge: ^2.5.5
tailwindcss: ^3.4.15 tailwindcss: ^3.4.16
tailwindcss-animate: ^1.0.7 tailwindcss-animate: ^1.0.7
theme-colors: ^0.1.0 theme-colors: ^0.1.0
turbo: ^2.3.1 turbo: ^2.3.3
typescript: 5.6.3 typescript: 5.6.3
unbuild: ^3.0.0-rc.11 unbuild: ^3.0.0-rc.11
unplugin-element-plus: ^0.8.0 unplugin-element-plus: ^0.8.0
vee-validate: ^4.14.7 vee-validate: ^4.14.7
vite: ^5.4.11 vite: ^6.0.2
vite-plugin-compression: ^0.5.1 vite-plugin-compression: ^0.5.1
vite-plugin-dts: 4.2.1 vite-plugin-dts: 4.2.1
vite-plugin-html: ^3.2.2 vite-plugin-html: ^3.2.2
vite-plugin-lazy-import: ^1.0.7 vite-plugin-lazy-import: ^1.0.7
vite-plugin-pwa: ^0.21.0 vite-plugin-pwa: ^0.21.1
vite-plugin-vue-devtools: ^7.6.4 vite-plugin-vue-devtools: ^7.6.7
vitepress: ^1.5.0 vitepress: ^1.5.0
vitepress-plugin-group-icons: ^1.3.0 vitepress-plugin-group-icons: ^1.3.1
vitest: ^2.1.5 vitest: ^2.1.8
vue: ^3.5.13 vue: ^3.5.13
vue-eslint-parser: ^9.4.3 vue-eslint-parser: ^9.4.3
vue-i18n: ^10.0.4 vue-i18n: ^10.0.5
vue-router: ^4.4.5 vue-router: ^4.5.0
vue-tsc: ^2.1.10 vue-tsc: ^2.1.10
vxe-pc-ui: ^4.3.4 vxe-pc-ui: ^4.3.4
vxe-table: 4.9.5 vxe-table: 4.9.5

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/turbo-run", "name": "@vben/turbo-run",
"version": "5.4.8", "version": "5.5.0",
"private": true, "private": true,
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",

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