Compare commits
66 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b29b41fc73 | ||
![]() |
a7d9697397 | ||
![]() |
7ec3cfb3fd | ||
![]() |
3014b62086 | ||
![]() |
e0b4bf0956 | ||
![]() |
686e09a1f8 | ||
![]() |
4778ead7c2 | ||
![]() |
131310efef | ||
![]() |
a13f808aa2 | ||
![]() |
41b415362c | ||
![]() |
1178f7a0bf | ||
![]() |
359d837dee | ||
![]() |
373766691f | ||
![]() |
bac0275624 | ||
![]() |
0fc0f13064 | ||
![]() |
b75a8e6a2b | ||
![]() |
68ab73bdb5 | ||
![]() |
4c1fc4a11e | ||
![]() |
03f166f8a4 | ||
![]() |
d42daf9ce0 | ||
![]() |
d1862fba27 | ||
![]() |
f0db3d6b79 | ||
![]() |
0efbf57152 | ||
![]() |
1d842b1b87 | ||
![]() |
48ca7aca8c | ||
![]() |
827ef2e403 | ||
![]() |
4df62b563e | ||
![]() |
21d37a1be0 | ||
![]() |
fe236ea929 | ||
![]() |
05b4b61c6e | ||
![]() |
e2b91f88ab | ||
![]() |
eeba7b50f9 | ||
![]() |
7ab00250bf | ||
![]() |
9896a67c21 | ||
![]() |
db38ef522f | ||
![]() |
845f2a2abd | ||
![]() |
9b73792dc9 | ||
![]() |
fccbe44cf7 | ||
![]() |
e23486dbc6 | ||
![]() |
935df713f3 | ||
![]() |
17c7ce8f21 | ||
![]() |
24b9aa44d2 | ||
![]() |
014e6d38a0 | ||
![]() |
12f216c0e7 | ||
![]() |
ae3f7cb909 | ||
![]() |
32117b73aa | ||
![]() |
e1414b2b10 | ||
![]() |
839e2e4321 | ||
![]() |
e8992a1d16 | ||
![]() |
3c4af23edf | ||
![]() |
e3a93970f4 | ||
![]() |
7b9866158b | ||
![]() |
3fb286b552 | ||
![]() |
7461693aa7 | ||
![]() |
3afb8395a7 | ||
![]() |
0950022f09 | ||
![]() |
227cf1e72b | ||
![]() |
a3e98aedf9 | ||
![]() |
ac3ec4746f | ||
![]() |
36939f36ee | ||
![]() |
07325d4c5e | ||
![]() |
253abc5ef1 | ||
![]() |
5f55799572 | ||
![]() |
54a9ff088f | ||
![]() |
73502677ff | ||
![]() |
dedba18553 |
18
.github/CODEOWNERS
vendored
18
.github/CODEOWNERS
vendored
@@ -1,14 +1,14 @@
|
||||
# default onwer
|
||||
* anncwb@126.com vince292007@gmail.com
|
||||
* anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
|
||||
# vben core onwer
|
||||
/.github/ anncwb@126.com vince292007@gmail.com
|
||||
/.vscode/ anncwb@126.com vince292007@gmail.com
|
||||
/packages/ anncwb@126.com vince292007@gmail.com
|
||||
/packages/@core/ anncwb@126.com vince292007@gmail.com
|
||||
/internal/ anncwb@126.com vince292007@gmail.com
|
||||
/scripts/ anncwb@126.com vince292007@gmail.com
|
||||
/.github/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/.vscode/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/packages/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/packages/@core/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/internal/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/scripts/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
|
||||
# vben team onwer
|
||||
apps/ anncwb@126.com vince292007@gmail.com @vbenjs/team-v5
|
||||
docs/ anncwb@126.com vince292007@gmail.com @vbenjs/team-v5
|
||||
apps/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com @vbenjs/team-v5
|
||||
docs/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com @vbenjs/team-v5
|
||||
|
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -62,7 +62,7 @@ body:
|
||||
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).
|
||||
options:
|
||||
- label: Read the [docs](https://anncwb.github.io/vue-vben-admin-doc/)
|
||||
- label: Read the [docs](https://doc.vben.pro/)
|
||||
required: true
|
||||
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
|
||||
required: true
|
||||
|
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -62,7 +62,7 @@ body:
|
||||
label: Validations
|
||||
description: Before submitting the issue, please make sure you do the following
|
||||
options:
|
||||
- label: Read the [docs](https://anncwb.github.io/vue-vben-admin-doc/)
|
||||
- label: Read the [docs](https://doc.vben.pro/)
|
||||
required: true
|
||||
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
|
||||
required: true
|
||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -99,7 +99,8 @@
|
||||
"**/.stylelintcache": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/vite.config.mts.*": true,
|
||||
"**/tea.yaml": true
|
||||
"**/tea.yaml": true,
|
||||
"**/public/tinymce/**": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
||||
# 1.1.3
|
||||
|
||||
**REFACTOR**
|
||||
|
||||
- 重构: 判断vxe-table的复选框是否选中
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 节点树在编辑 & 空数组(不勾选)情况 勾选节点会造成watch延迟触发 导致会带上父节点id造成id重复
|
||||
- 节点树在节点独立情况下的控制台warning: Invalid prop: type check failed for prop "value". Expected Array, got Object
|
||||
|
||||
**Others**
|
||||
|
||||
- 角色管理 优化Drawer布局
|
||||
- unplugin-vue-components插件(默认未开启) 需要排除Button组件 全局已经默认导入了
|
||||
|
||||
**BUG FIXES**
|
||||
|
||||
- 操作日志详情 在description组件中json预览样式异常
|
||||
- 微服务版本 区间查询和中文搜索条件一起使用 无法正确查询
|
||||
|
||||
# 1.1.2
|
||||
|
||||
**Features**
|
||||
|
9
apps/web-antd/.vscode/settings.json
vendored
9
apps/web-antd/.vscode/settings.json
vendored
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit"
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/web-antd",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -8,7 +8,7 @@ import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
import type { Component, SetupContext } 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 {
|
||||
@@ -51,6 +51,7 @@ const withDefaultPlaceholder = <T extends Component>(
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type ComponentType =
|
||||
| 'ApiSelect'
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
@@ -58,6 +59,7 @@ export type ComponentType =
|
||||
| 'DefaultButton'
|
||||
| 'Divider'
|
||||
| 'FileUpload'
|
||||
| 'IconPicker'
|
||||
| 'ImageUpload'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
@@ -83,7 +85,20 @@ async function initComponentAdapter() {
|
||||
// 如果你的组件体积比较大,可以使用异步加载
|
||||
// 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,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
@@ -93,6 +108,13 @@ async function initComponentAdapter() {
|
||||
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||
},
|
||||
Divider,
|
||||
IconPicker: (props, { attrs, slots }) => {
|
||||
return h(
|
||||
IconPicker,
|
||||
{ iconSlot: 'addonAfter', inputComponent: Input, ...props, ...attrs },
|
||||
slots,
|
||||
);
|
||||
},
|
||||
Input: withDefaultPlaceholder(Input, 'input'),
|
||||
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
|
||||
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
|
||||
|
@@ -107,6 +107,7 @@ export type * from '@vben/plugins/vxe-table';
|
||||
|
||||
/**
|
||||
* 通用的表格复选框是否选中事件
|
||||
* @deprecated 使用vxeCheckboxChecked代替
|
||||
* @param checked 是否选中
|
||||
* @returns function
|
||||
*/
|
||||
@@ -118,3 +119,14 @@ export function tableCheckboxEvent(checked: Ref<boolean>) {
|
||||
};
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断vxe-table的复选框是否选中
|
||||
* @param tableApi api
|
||||
* @returns boolean
|
||||
*/
|
||||
export function vxeCheckboxChecked(
|
||||
tableApi: ReturnType<typeof useVbenVxeGrid>[1],
|
||||
) {
|
||||
return tableApi?.grid?.getCheckboxRecords?.()?.length > 0;
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import {
|
||||
authenticateResponseInterceptor,
|
||||
errorMessageResponseInterceptor,
|
||||
RequestClient,
|
||||
stringify,
|
||||
} from '@vben/request';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
@@ -94,6 +95,23 @@ function createRequestClient(baseURL: string) {
|
||||
// 添加全局clientId
|
||||
config.headers.clientId = clientId;
|
||||
|
||||
/**
|
||||
* 格式化get/delete参数
|
||||
* 如果包含自定义的paramsSerializer则不走此逻辑
|
||||
*/
|
||||
if (
|
||||
['DELETE', 'GET'].includes(config.method?.toUpperCase() || '') &&
|
||||
config.params &&
|
||||
!config.paramsSerializer
|
||||
) {
|
||||
/**
|
||||
* 1. 格式化参数 微服务在传递区间时间选择(后端的params Map类型参数)需要格式化key 否则接收不到
|
||||
* 2. 数组参数需要格式化 后端才能正常接收 会变成arr=1&arr=2&arr=3的格式来接收
|
||||
*/
|
||||
config.paramsSerializer = (params) =>
|
||||
stringify(params, { arrayFormat: 'repeat' });
|
||||
}
|
||||
|
||||
const { encrypt } = config;
|
||||
// 全局开启请求加密功能 && 该请求开启 && 是post/put请求
|
||||
if (
|
||||
|
@@ -8,6 +8,7 @@ import { computed, nextTick, onMounted, type PropType, ref, watch } from 'vue';
|
||||
import { findGroupParentIds, treeToList } from '@vben/utils';
|
||||
|
||||
import { Checkbox, Tree } from 'ant-design-vue';
|
||||
import { uniq } from 'lodash-es';
|
||||
|
||||
/** 需要禁止透传 */
|
||||
defineOptions({ inheritAttrs: false });
|
||||
@@ -73,6 +74,8 @@ const checkedRealKeys = ref<(number | string)[]>([]);
|
||||
/**
|
||||
* 取第一次的menuTree id 设置到checkedMenuKeys
|
||||
* 主要为了解决没有任何修改 直接点击保存的情况
|
||||
*
|
||||
* length为0情况(即新增时候没有勾选节点) 勾选这里会延迟触发 节点会拼接上父节点 导致ID重复
|
||||
*/
|
||||
const stop = watch([checkedKeys, () => props.treeData], () => {
|
||||
if (
|
||||
@@ -86,7 +89,10 @@ const stop = watch([checkedKeys, () => props.treeData], () => {
|
||||
checkedKeys.value as any,
|
||||
{ id: props.fieldNames.key },
|
||||
);
|
||||
checkedRealKeys.value = [...parentIds, ...checkedKeys.value];
|
||||
/**
|
||||
* uniq 解决上面的id重复问题
|
||||
*/
|
||||
checkedRealKeys.value = uniq([...parentIds, ...checkedKeys.value]);
|
||||
stop();
|
||||
}
|
||||
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
|
||||
*/
|
||||
type CheckedState<T = number | string> =
|
||||
| { checked: T[]; halfChecked: 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[];
|
||||
checkedRealKeys.value = [...halfCheckedKeys, ...checkedKeys];
|
||||
checkedRealKeys.value = [...halfCheckedKeys, ...checkedStateKeys];
|
||||
} 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({
|
||||
getCheckedKeys: () => checkedRealKeys.value,
|
||||
getCheckedKeys: () => uniq(checkedRealKeys.value),
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, watch } from 'vue';
|
||||
import { computed, h, onMounted, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||
@@ -8,6 +8,7 @@ import { useWatermark } from '@vben/hooks';
|
||||
import {
|
||||
BookOpenText,
|
||||
CircleHelp,
|
||||
GiteeIcon,
|
||||
GitHubOutlined,
|
||||
UserOutlined,
|
||||
} from '@vben/icons';
|
||||
@@ -55,6 +56,15 @@ const menus = computed(() => {
|
||||
icon: UserOutlined,
|
||||
text: $t('ui.widgets.profile'),
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
openWindow('https://gitee.com/dapppp/ruoyi-plus-vben5', {
|
||||
target: '_blank',
|
||||
});
|
||||
},
|
||||
icon: () => h(GiteeIcon, { class: 'text-red-800' }),
|
||||
text: 'Gitee项目地址',
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
openWindow(VBEN_GITHUB_URL, {
|
||||
@@ -62,7 +72,7 @@ const menus = computed(() => {
|
||||
});
|
||||
},
|
||||
icon: GitHubOutlined,
|
||||
text: 'GitHub',
|
||||
text: 'Vben官方地址',
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
|
@@ -207,7 +207,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
const vbenMenuList = backMenuToVbenMenu(backMenuList);
|
||||
// 特别注意 这里要深拷贝
|
||||
const menuList = [...cloneDeep(localMenuList), ...vbenMenuList];
|
||||
console.log('menuList', menuList);
|
||||
// console.log('menuList', menuList);
|
||||
return menuList;
|
||||
},
|
||||
// 可以指定没有权限跳转403页面
|
||||
|
@@ -68,7 +68,7 @@ export function renderJsonPreview(json: any) {
|
||||
return <span>{json}</span>;
|
||||
}
|
||||
if (typeof json === 'object') {
|
||||
return <JsonPreview data={json} />;
|
||||
return <JsonPreview class="break-normal" data={json} />;
|
||||
}
|
||||
try {
|
||||
const obj = JSON.parse(json);
|
||||
@@ -76,7 +76,7 @@ export function renderJsonPreview(json: any) {
|
||||
if (typeof obj !== 'object') {
|
||||
return <span>{obj}</span>;
|
||||
}
|
||||
return <JsonPreview data={obj} />;
|
||||
return <JsonPreview class="break-normal" data={obj} />;
|
||||
} catch {
|
||||
return <span>{json}</span>;
|
||||
}
|
||||
|
@@ -9,8 +9,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridDefines,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
@@ -77,18 +77,15 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'monitor-logininfo-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const canUnlock = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: (e: VxeGridDefines.CheckboxChangeEventParams) => {
|
||||
const records = e.$table.getCheckboxRecords();
|
||||
checked.value = records.length > 0;
|
||||
const records = e.$grid.getCheckboxRecords();
|
||||
canUnlock.value = records.length === 1 && records[0]?.status === '1';
|
||||
},
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -125,7 +122,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await loginInfoRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -138,7 +134,6 @@ async function handleUnlock() {
|
||||
const { userName } = records[0];
|
||||
await userUnlock(userName);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
canUnlock.value = false;
|
||||
tableApi.grid.clearCheckboxRow();
|
||||
}
|
||||
@@ -173,7 +168,7 @@ function handleDownloadExcel() {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['monitor:logininfor:remove']"
|
||||
|
@@ -157,12 +157,15 @@ export const descSchema: DescItem[] = [
|
||||
field: 'method',
|
||||
label: '方法',
|
||||
},
|
||||
/**
|
||||
* 默认word-break: break-word;会导致json预览样式异常
|
||||
*/
|
||||
{
|
||||
field: 'operParam',
|
||||
label: '请求参数',
|
||||
render(value) {
|
||||
return (
|
||||
<div class="max-h-[300px] overflow-y-auto">
|
||||
<div class="max-h-[300px] w-full overflow-y-auto">
|
||||
{renderJsonPreview(value)}
|
||||
</div>
|
||||
);
|
||||
@@ -173,7 +176,7 @@ export const descSchema: DescItem[] = [
|
||||
label: '响应参数',
|
||||
render(value) {
|
||||
return (
|
||||
<div class="max-h-[300px] overflow-y-auto">
|
||||
<div class="max-h-[300px] w-full overflow-y-auto">
|
||||
{renderJsonPreview(value)}
|
||||
</div>
|
||||
);
|
||||
|
@@ -3,8 +3,6 @@ import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { OperationLog } from '#/api/monitor/operlog/model';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
@@ -12,8 +10,8 @@ import { Modal, Space } from 'ant-design-vue';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -88,7 +86,6 @@ const gridOptions: VxeGridProps<OperationLog> = {
|
||||
id: 'monitor-operlog-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
@@ -96,8 +93,6 @@ const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
sortChange: () => {
|
||||
tableApi.query();
|
||||
},
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -138,7 +133,6 @@ async function handleDelete() {
|
||||
onOk: async () => {
|
||||
await operLogDelete(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -168,7 +162,7 @@ function handleDownloadExcel() {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['monitor:operlog:remove']"
|
||||
|
@@ -21,11 +21,14 @@ const title = computed(() => {
|
||||
const [BasicForm, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
layout: 'vertical',
|
||||
schema: drawerSchema(),
|
||||
showDefaultActions: false,
|
||||
wrapperClass: 'grid-cols-2',
|
||||
wrapperClass: 'grid-cols-2 gap-x-4',
|
||||
});
|
||||
|
||||
function setupForm(update: boolean) {
|
||||
|
@@ -134,7 +134,6 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
getPopupContainer,
|
||||
mode: 'multiple',
|
||||
optionFilterProp: 'label',
|
||||
@@ -148,7 +147,6 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: false,
|
||||
class: 'w-full',
|
||||
getPopupContainer,
|
||||
options: getDictOptions(DictEnum.SYS_DEVICE_TYPE),
|
||||
},
|
||||
|
@@ -1,8 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
@@ -10,8 +8,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -70,14 +68,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-client-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [ClientDrawer, drawerApi] = useVbenDrawer({
|
||||
@@ -109,7 +102,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await clientRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -133,7 +125,7 @@ const { hasAccessByCodes } = useAccess();
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:client:remove']"
|
||||
|
@@ -1,16 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -72,14 +70,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-config-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
const [ConfigModal, modalApi] = useVbenModal({
|
||||
connectedComponent: configModal,
|
||||
@@ -110,7 +103,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await configRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -140,7 +132,7 @@ async function handleRefreshCache() {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:config:remove']"
|
||||
|
@@ -9,8 +9,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -73,14 +73,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-dict-data-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [DictDataDrawer, drawerApi] = useVbenDrawer({
|
||||
@@ -115,7 +110,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await dictDataRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -142,7 +136,7 @@ emitter.on('rowClick', async (value) => {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:dict:remove']"
|
||||
|
@@ -9,8 +9,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -67,7 +67,6 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-dict-type-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const lastDictType = ref('');
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
@@ -82,8 +81,6 @@ const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
emitter.emit('rowClick', row.dictType);
|
||||
lastDictType.value = row.dictType;
|
||||
},
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
const [DictTypeModal, modalApi] = useVbenModal({
|
||||
@@ -115,7 +112,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await dictTypeRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -152,7 +148,7 @@ function handleDownloadExcel() {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:dict:remove']"
|
||||
|
@@ -1,16 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import { noticeList, noticeRemove } from '#/api/system/notice';
|
||||
@@ -60,14 +58,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-notice-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [NoticeModal, modalApi] = useVbenModal({
|
||||
@@ -99,7 +92,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await noticeRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -111,7 +103,7 @@ function handleMultiDelete() {
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:notice:remove']"
|
||||
|
@@ -99,7 +99,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
orientation: 'center',
|
||||
},
|
||||
fieldName: 'divider1',
|
||||
labelClass: 'w-0',
|
||||
hideLabel: true,
|
||||
renderComponentContent: () => ({
|
||||
default: () => '基本信息',
|
||||
}),
|
||||
@@ -134,7 +134,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
orientation: 'center',
|
||||
},
|
||||
fieldName: 'divider2',
|
||||
labelClass: 'w-0',
|
||||
hideLabel: true,
|
||||
renderComponentContent: () => ({
|
||||
default: () => '认证信息',
|
||||
}),
|
||||
@@ -157,7 +157,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
orientation: 'center',
|
||||
},
|
||||
fieldName: 'divider3',
|
||||
labelClass: 'w-0',
|
||||
hideLabel: true,
|
||||
renderComponentContent: () => ({
|
||||
default: () => '其他信息',
|
||||
}),
|
||||
|
@@ -1,8 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
@@ -10,8 +8,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -66,14 +64,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-oss-config-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [OssConfigDrawer, drawerApi] = useVbenDrawer({
|
||||
@@ -105,7 +98,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await ossConfigRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -119,7 +111,7 @@ const { hasAccessByCodes } = useAccess();
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:ossConfig:remove']"
|
||||
|
@@ -20,8 +20,8 @@ import {
|
||||
import { isEmpty } from 'lodash-es';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import { configInfoByKey } from '#/api/system/config';
|
||||
@@ -91,7 +91,6 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-oss-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
@@ -99,8 +98,6 @@ const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
sortChange: () => {
|
||||
tableApi.query();
|
||||
},
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -172,7 +169,7 @@ const [FileUploadModal, fileUploadApi] = useVbenModal({
|
||||
配置管理
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:oss:remove']"
|
||||
|
@@ -82,7 +82,6 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
getPopupContainer,
|
||||
},
|
||||
fieldName: 'deptId',
|
||||
|
@@ -9,8 +9,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import { postExport, postList, postRemove } from '#/api/system/post';
|
||||
@@ -79,14 +79,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-post-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [PostDrawer, drawerApi] = useVbenDrawer({
|
||||
@@ -118,7 +113,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await postRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -146,7 +140,7 @@ function handleDownloadExcel() {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:post:remove']"
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
|
||||
@@ -10,8 +9,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -69,14 +68,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-role-assign-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [RoleAssignDrawer, drawerApi] = useVbenDrawer({
|
||||
@@ -109,7 +103,6 @@ function handleMultipleAuthCancel() {
|
||||
onOk: async () => {
|
||||
await roleAuthCancelAll(roleId, ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
tableApi.grid.clearCheckboxRow();
|
||||
},
|
||||
});
|
||||
@@ -122,7 +115,7 @@ function handleMultipleAuthCancel() {
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:role:remove']"
|
||||
|
@@ -1,14 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { roleSelectAll, roleUnallocatedList } from '#/api/system/role';
|
||||
|
||||
import { columns, querySchema } from './data';
|
||||
@@ -62,14 +57,9 @@ const gridOptions: VxeGridProps = {
|
||||
},
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
|
@@ -153,12 +153,13 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
defaultValue: [],
|
||||
fieldName: 'menuIds',
|
||||
label: '菜单权限',
|
||||
formItemClass: 'col-span-2',
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
defaultValue: '',
|
||||
fieldName: 'remark',
|
||||
formItemClass: 'items-baseline',
|
||||
formItemClass: 'items-baseline col-span-2',
|
||||
label: '备注',
|
||||
},
|
||||
];
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
@@ -23,8 +23,8 @@ import {
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -91,14 +91,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-role-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
const [RoleDrawer, drawerApi] = useVbenDrawer({
|
||||
connectedComponent: roleDrawer,
|
||||
@@ -129,7 +124,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await roleRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -171,7 +165,7 @@ function handleAssignRole(record: Recordable<any>) {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:role:remove']"
|
||||
|
@@ -24,12 +24,12 @@ const [BasicForm, formApi] = useVbenForm({
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
formItemClass: 'col-span-1',
|
||||
},
|
||||
layout: 'vertical',
|
||||
schema: drawerSchema(),
|
||||
showDefaultActions: false,
|
||||
wrapperClass: 'grid-cols-2',
|
||||
wrapperClass: 'grid-cols-2 gap-x-4',
|
||||
});
|
||||
|
||||
const menuTree = ref<any[]>([]);
|
||||
|
@@ -101,7 +101,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
orientation: 'center',
|
||||
},
|
||||
fieldName: 'divider1',
|
||||
labelClass: 'w-0',
|
||||
hideLabel: true,
|
||||
renderComponentContent: () => ({
|
||||
default: () => '基本信息',
|
||||
}),
|
||||
@@ -132,7 +132,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
orientation: 'center',
|
||||
},
|
||||
fieldName: 'divider2',
|
||||
labelClass: 'w-0',
|
||||
hideLabel: true,
|
||||
renderComponentContent: () => ({
|
||||
default: () => '管理员信息',
|
||||
}),
|
||||
@@ -167,7 +167,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
orientation: 'center',
|
||||
},
|
||||
fieldName: 'divider3',
|
||||
labelClass: 'w-0',
|
||||
hideLabel: true,
|
||||
renderComponentContent: () => ({
|
||||
default: () => '租户设置',
|
||||
}),
|
||||
@@ -175,7 +175,6 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
getPopupContainer,
|
||||
},
|
||||
fieldName: 'packageId',
|
||||
@@ -237,7 +236,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
orientation: 'center',
|
||||
},
|
||||
fieldName: 'divider4',
|
||||
labelClass: 'w-0',
|
||||
hideLabel: true,
|
||||
renderComponentContent: () => ({
|
||||
default: () => '企业信息',
|
||||
}),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
|
||||
@@ -11,8 +11,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -73,14 +73,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-tenant-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [TenantDrawer, drawerApi] = useVbenDrawer({
|
||||
@@ -121,7 +116,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await tenantRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
// 重新加载租户信息
|
||||
tenantStore.initTenant();
|
||||
},
|
||||
@@ -173,7 +167,7 @@ function handleSyncTenantDict() {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:tenant:remove']"
|
||||
|
@@ -23,6 +23,9 @@ const [BasicForm, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 100,
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
schema: drawerSchema(),
|
||||
showDefaultActions: false,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
|
||||
@@ -11,8 +11,8 @@ import { getVxePopupContainer } from '@vben/utils';
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -69,14 +69,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'system-tenant-package-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [TenantPackageDrawer, drawerApi] = useVbenDrawer({
|
||||
@@ -108,7 +103,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await packageRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -144,7 +138,7 @@ const isSuperAdmin = computed(() => {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:tenantPackage:remove']"
|
||||
|
@@ -71,7 +71,7 @@ onMounted(loadTree);
|
||||
<div class="bg-background z-100 sticky left-0 top-0 p-[8px]">
|
||||
<InputSearch
|
||||
v-model:value="searchValue"
|
||||
placeholder="Search"
|
||||
:placeholder="$t('pages.common.search')"
|
||||
size="small"
|
||||
>
|
||||
<template #enterButton>
|
||||
|
@@ -24,11 +24,8 @@ import {
|
||||
Space,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { vxeCheckboxChecked } from '#/adapter/vxe-table';
|
||||
import {
|
||||
userExport,
|
||||
userList,
|
||||
@@ -126,14 +123,9 @@ const gridOptions: VxeGridProps = {
|
||||
},
|
||||
id: 'system-user-index',
|
||||
};
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [UserDrawer, userDrawerApi] = useVbenDrawer({
|
||||
@@ -165,7 +157,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await userRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -201,7 +192,7 @@ const { hasAccessByCodes } = useAccess();
|
||||
<div class="flex h-full gap-[8px]">
|
||||
<DeptTree
|
||||
v-model:select-dept-id="selectDeptId"
|
||||
class="w-[260px]"
|
||||
:width="260"
|
||||
@reload="() => tableApi.reload()"
|
||||
@select="() => tableApi.reload()"
|
||||
/>
|
||||
@@ -221,7 +212,7 @@ const { hasAccessByCodes } = useAccess();
|
||||
{{ $t('pages.common.import') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:user:remove']"
|
||||
|
@@ -2,7 +2,7 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { Key } from 'ant-design-vue/es/vc-tree/interface';
|
||||
|
||||
import { type Component, ref } from 'vue';
|
||||
import { type Component, markRaw, ref } from 'vue';
|
||||
|
||||
import {
|
||||
CodeMirror,
|
||||
@@ -98,12 +98,12 @@ function convertToTree(paths: string[]): TreeNode[] {
|
||||
}
|
||||
|
||||
const iconMap = [
|
||||
{ key: 'java', value: JavaIcon },
|
||||
{ key: 'xml', value: XmlIcon },
|
||||
{ key: 'sql', value: SqlIcon },
|
||||
{ key: 'ts', value: TsIcon },
|
||||
{ key: 'vue', value: VueIcon },
|
||||
{ key: 'folder', value: FolderIcon },
|
||||
{ key: 'java', value: markRaw(JavaIcon) },
|
||||
{ key: 'xml', value: markRaw(XmlIcon) },
|
||||
{ key: 'sql', value: markRaw(SqlIcon) },
|
||||
{ key: 'ts', value: markRaw(TsIcon) },
|
||||
{ key: 'vue', value: markRaw(VueIcon) },
|
||||
{ key: 'folder', value: markRaw(FolderIcon) },
|
||||
];
|
||||
function findIcon(path: string) {
|
||||
const defaultFileIcon = DefaultFileIcon;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
@@ -11,8 +11,8 @@ import { message, Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -78,14 +78,9 @@ const gridOptions: VxeGridProps = {
|
||||
id: 'tool-gen-index',
|
||||
};
|
||||
|
||||
const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -177,7 +172,6 @@ function handleMultiDelete() {
|
||||
onOk: async () => {
|
||||
await genRemove(ids);
|
||||
await tableApi.query();
|
||||
checked.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -203,7 +197,7 @@ function handleImport() {
|
||||
</a>
|
||||
<Space>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['tool:gen:remove']"
|
||||
@@ -212,7 +206,7 @@ function handleImport() {
|
||||
{{ $t('pages.common.delete') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
v-access:code="['tool:gen:code']"
|
||||
@click="handleBatchGen"
|
||||
>
|
||||
|
@@ -20,6 +20,8 @@ export default defineConfig(async () => {
|
||||
// dts: './types/components.d.ts', // 输出类型文件
|
||||
// resolvers: [
|
||||
// AntDesignVueResolver({
|
||||
// // 需要排除Button组件 全局已经默认导入了
|
||||
// exclude: ['Button'],
|
||||
// importStyle: false, // css in js
|
||||
// }),
|
||||
// ],
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/web-ele",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -4,11 +4,12 @@
|
||||
*/
|
||||
|
||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { Component, SetupContext } 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 {
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
ElNotification,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
ElSelectV2,
|
||||
ElSpace,
|
||||
ElSwitch,
|
||||
ElTimePicker,
|
||||
@@ -41,10 +43,12 @@ const withDefaultPlaceholder = <T extends Component>(
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type ComponentType =
|
||||
| 'ApiSelect'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
| 'DatePicker'
|
||||
| 'Divider'
|
||||
| 'IconPicker'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
| 'RadioGroup'
|
||||
@@ -61,7 +65,19 @@ async function initComponentAdapter() {
|
||||
// 如果你的组件体积比较大,可以使用异步加载
|
||||
// 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,
|
||||
CheckboxGroup: ElCheckboxGroup,
|
||||
// 自定义默认按钮
|
||||
@@ -73,14 +89,67 @@ async function initComponentAdapter() {
|
||||
return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
|
||||
},
|
||||
Divider: ElDivider,
|
||||
IconPicker: (props, { attrs, slots }) => {
|
||||
return h(
|
||||
IconPicker,
|
||||
{
|
||||
iconSlot: 'append',
|
||||
modelValueProp: 'model-value',
|
||||
inputComponent: ElInput,
|
||||
...props,
|
||||
...attrs,
|
||||
},
|
||||
slots,
|
||||
);
|
||||
},
|
||||
Input: withDefaultPlaceholder(ElInput, 'input'),
|
||||
InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
|
||||
RadioGroup: ElRadioGroup,
|
||||
Select: withDefaultPlaceholder(ElSelect, 'select'),
|
||||
Space: ElSpace,
|
||||
Switch: ElSwitch,
|
||||
TimePicker: ElTimePicker,
|
||||
DatePicker: ElDatePicker,
|
||||
TimePicker: (props, { attrs, slots }) => {
|
||||
const { name, id, isRange } = props;
|
||||
const extraProps: Recordable<any> = {};
|
||||
if (isRange) {
|
||||
if (name && !Array.isArray(name)) {
|
||||
extraProps.name = [name, `${name}_end`];
|
||||
}
|
||||
if (id && !Array.isArray(id)) {
|
||||
extraProps.id = [id, `${id}_end`];
|
||||
}
|
||||
}
|
||||
return h(
|
||||
ElTimePicker,
|
||||
{
|
||||
...props,
|
||||
...attrs,
|
||||
...extraProps,
|
||||
},
|
||||
slots,
|
||||
);
|
||||
},
|
||||
DatePicker: (props, { attrs, slots }) => {
|
||||
const { name, id, type } = props;
|
||||
const extraProps: Recordable<any> = {};
|
||||
if (type && type.includes('range')) {
|
||||
if (name && !Array.isArray(name)) {
|
||||
extraProps.name = [name, `${name}_end`];
|
||||
}
|
||||
if (id && !Array.isArray(id)) {
|
||||
extraProps.id = [id, `${id}_end`];
|
||||
}
|
||||
}
|
||||
return h(
|
||||
ElDatePicker,
|
||||
{
|
||||
...props,
|
||||
...attrs,
|
||||
...extraProps,
|
||||
},
|
||||
slots,
|
||||
);
|
||||
},
|
||||
TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'),
|
||||
Upload: ElUpload,
|
||||
};
|
||||
|
@@ -7,6 +7,7 @@ import '@vben/styles';
|
||||
import '@vben/styles/ele';
|
||||
|
||||
import { useTitle } from '@vueuse/core';
|
||||
import { ElLoading } from 'element-plus';
|
||||
|
||||
import { $t, setupI18n } from '#/locales';
|
||||
|
||||
@@ -19,6 +20,9 @@ async function bootstrap(namespace: string) {
|
||||
await initComponentAdapter();
|
||||
const app = createApp(App);
|
||||
|
||||
// 注册Element Plus提供的v-loading指令
|
||||
app.directive('loading', ElLoading.directive);
|
||||
|
||||
// 国际化 i18n 配置
|
||||
await setupI18n(app);
|
||||
|
||||
|
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import {
|
||||
@@ -6,6 +8,7 @@ import {
|
||||
ElCard,
|
||||
ElMessage,
|
||||
ElNotification,
|
||||
ElSegmented,
|
||||
ElSpace,
|
||||
ElTable,
|
||||
} from 'element-plus';
|
||||
@@ -47,6 +50,10 @@ const tableData = [
|
||||
{ prop1: '5', prop2: 'E' },
|
||||
{ prop1: '6', prop2: 'F' },
|
||||
];
|
||||
|
||||
const segmentedValue = ref('Mon');
|
||||
|
||||
const segmentedOptions = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -54,41 +61,57 @@ const tableData = [
|
||||
description="支持多语言,主题功能集成切换等"
|
||||
title="Element Plus组件使用演示"
|
||||
>
|
||||
<ElCard class="mb-5">
|
||||
<template #header> 按钮 </template>
|
||||
<ElSpace>
|
||||
<ElButton text>Text</ElButton>
|
||||
<ElButton>Default</ElButton>
|
||||
<ElButton type="primary"> Primary </ElButton>
|
||||
<ElButton type="info"> Info </ElButton>
|
||||
<ElButton type="success"> Success </ElButton>
|
||||
<ElButton type="warning"> Warning </ElButton>
|
||||
<ElButton type="danger"> Error </ElButton>
|
||||
</ElSpace>
|
||||
</ElCard>
|
||||
<ElCard class="mb-5">
|
||||
<template #header> Message </template>
|
||||
<ElSpace>
|
||||
<ElButton type="info" @click="info"> 信息 </ElButton>
|
||||
<ElButton type="danger" @click="error"> 错误 </ElButton>
|
||||
<ElButton type="warning" @click="warning"> 警告 </ElButton>
|
||||
<ElButton type="success" @click="success"> 成功 </ElButton>
|
||||
</ElSpace>
|
||||
</ElCard>
|
||||
<ElCard class="mb-5">
|
||||
<template #header> Notification </template>
|
||||
<ElSpace>
|
||||
<ElButton type="info" @click="notify('info')"> 信息 </ElButton>
|
||||
<ElButton type="danger" @click="notify('error')"> 错误 </ElButton>
|
||||
<ElButton type="warning" @click="notify('warning')"> 警告 </ElButton>
|
||||
<ElButton type="success" @click="notify('success')"> 成功 </ElButton>
|
||||
</ElSpace>
|
||||
</ElCard>
|
||||
<ElCard class="mb-5">
|
||||
<ElTable :data="tableData" stripe>
|
||||
<ElTable.TableColumn label="测试列1" prop="prop1" />
|
||||
<ElTable.TableColumn label="测试列2" prop="prop2" />
|
||||
</ElTable>
|
||||
</ElCard>
|
||||
<div class="flex flex-wrap gap-5">
|
||||
<ElCard class="mb-5 w-auto">
|
||||
<template #header> 按钮 </template>
|
||||
<ElSpace>
|
||||
<ElButton text>Text</ElButton>
|
||||
<ElButton>Default</ElButton>
|
||||
<ElButton type="primary"> Primary </ElButton>
|
||||
<ElButton type="info"> Info </ElButton>
|
||||
<ElButton type="success"> Success </ElButton>
|
||||
<ElButton type="warning"> Warning </ElButton>
|
||||
<ElButton type="danger"> Error </ElButton>
|
||||
</ElSpace>
|
||||
</ElCard>
|
||||
<ElCard class="mb-5 w-80">
|
||||
<template #header> Message </template>
|
||||
<ElSpace>
|
||||
<ElButton type="info" @click="info"> 信息 </ElButton>
|
||||
<ElButton type="danger" @click="error"> 错误 </ElButton>
|
||||
<ElButton type="warning" @click="warning"> 警告 </ElButton>
|
||||
<ElButton type="success" @click="success"> 成功 </ElButton>
|
||||
</ElSpace>
|
||||
</ElCard>
|
||||
<ElCard class="mb-5 w-80">
|
||||
<template #header> Notification </template>
|
||||
<ElSpace>
|
||||
<ElButton type="info" @click="notify('info')"> 信息 </ElButton>
|
||||
<ElButton type="danger" @click="notify('error')"> 错误 </ElButton>
|
||||
<ElButton type="warning" @click="notify('warning')"> 警告 </ElButton>
|
||||
<ElButton type="success" @click="notify('success')"> 成功 </ElButton>
|
||||
</ElSpace>
|
||||
</ElCard>
|
||||
<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.TableColumn label="测试列1" prop="prop1" />
|
||||
<ElTable.TableColumn label="测试列2" prop="prop2" />
|
||||
</ElTable>
|
||||
</ElCard>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/web-naive",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -8,7 +8,7 @@ import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
import type { Component, SetupContext } 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 {
|
||||
@@ -42,10 +42,12 @@ const withDefaultPlaceholder = <T extends Component>(
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type ComponentType =
|
||||
| 'ApiSelect'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
| 'DatePicker'
|
||||
| 'Divider'
|
||||
| 'IconPicker'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
| 'RadioGroup'
|
||||
@@ -63,6 +65,18 @@ async function initComponentAdapter() {
|
||||
// Button: () =>
|
||||
// import('xxx').then((res) => res.Button),
|
||||
|
||||
ApiSelect: (props, { attrs, slots }) => {
|
||||
return h(
|
||||
ApiSelect,
|
||||
{
|
||||
...props,
|
||||
...attrs,
|
||||
component: NSelect,
|
||||
modelField: 'value',
|
||||
},
|
||||
slots,
|
||||
);
|
||||
},
|
||||
Checkbox: NCheckbox,
|
||||
CheckboxGroup: NCheckboxGroup,
|
||||
DatePicker: NDatePicker,
|
||||
@@ -75,6 +89,13 @@ async function initComponentAdapter() {
|
||||
return h(NButton, { ...props, attrs, type: 'primary' }, slots);
|
||||
},
|
||||
Divider: NDivider,
|
||||
IconPicker: (props, { attrs, slots }) => {
|
||||
return h(
|
||||
IconPicker,
|
||||
{ iconSlot: 'suffix', inputComponent: NInput, ...props, ...attrs },
|
||||
slots,
|
||||
);
|
||||
},
|
||||
Input: withDefaultPlaceholder(NInput, 'input'),
|
||||
InputNumber: withDefaultPlaceholder(NInputNumber, 'input'),
|
||||
RadioGroup: NRadioGroup,
|
||||
|
@@ -148,6 +148,16 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsed: false,
|
||||
text: '布局组件',
|
||||
items: [
|
||||
{
|
||||
link: 'layout-ui/page',
|
||||
text: 'Page 页面',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsed: false,
|
||||
text: '通用组件',
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/docs",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vitepress build",
|
||||
|
@@ -88,6 +88,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
||||
| closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` |
|
||||
| confirmText | 确认按钮文本 | `string\|slot` | `确认` |
|
||||
| cancelText | 取消按钮文本 | `string\|slot` | `取消` |
|
||||
| placement | 抽屉弹出位置 | `'left'\|'right'\|'top'\|'bottom'` | `right` |
|
||||
| showCancelButton | 显示取消按钮 | `boolean` | `true` |
|
||||
| showConfirmButton | 显示确认按钮文本 | `boolean` | `true` |
|
||||
| class | modal的class,宽度通过这个配置 | `string` | - |
|
||||
|
@@ -87,7 +87,7 @@ import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
import type { Component, SetupContext } 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 {
|
||||
@@ -149,6 +149,7 @@ export type ComponentType =
|
||||
| 'TimePicker'
|
||||
| 'TreeSelect'
|
||||
| 'Upload'
|
||||
| 'IconPicker';
|
||||
| BaseFormComponentType;
|
||||
|
||||
async function initComponentAdapter() {
|
||||
@@ -166,6 +167,7 @@ async function initComponentAdapter() {
|
||||
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||
},
|
||||
Divider,
|
||||
IconPicker,
|
||||
Input: withDefaultPlaceholder(Input, 'input'),
|
||||
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
|
||||
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
|
||||
@@ -314,6 +316,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
|
||||
| commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - |
|
||||
| schema | 表单项的每一项配置 | `FormSchema` | - |
|
||||
| submitOnEnter | 按下回车健时提交表单 | `boolean` | false |
|
||||
| submitOnChange | 字段值改变时提交表单 | `boolean` | false |
|
||||
|
||||
### TS 类型说明
|
||||
|
||||
@@ -419,7 +422,7 @@ export interface FormSchema<
|
||||
help?: string;
|
||||
/** 表单项 */
|
||||
label?: string;
|
||||
// 自定义组件内部渲染
|
||||
/** 自定义组件内部渲染 */
|
||||
renderComponentContent?: RenderComponentContentType;
|
||||
/** 字段规则 */
|
||||
rules?: FormSchemaRuleType;
|
||||
@@ -500,3 +503,20 @@ import { z } from '#/adapter/form';
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Slots
|
||||
|
||||
可以使用以下插槽在表单中插入自定义的内容
|
||||
|
||||
| 插槽名 | 描述 |
|
||||
| ------------- | ------------------ |
|
||||
| reset-before | 重置按钮之前的位置 |
|
||||
| submit-before | 提交按钮之前的位置 |
|
||||
| expand-before | 展开按钮之前的位置 |
|
||||
| expand-after | 展开按钮之后的位置 |
|
||||
|
||||
::: tip 字段插槽
|
||||
|
||||
除了以上内置插槽之外,`schema`属性中每个字段的`fieldName`都可以作为插槽名称,这些字段插槽的优先级高于`component`定义的组件。也就是说,当提供了与`fieldName`同名的插槽时,这些插槽的内容将会作为这些字段的组件,此时`component`的值将会被忽略。
|
||||
|
||||
:::
|
||||
|
@@ -93,13 +93,14 @@ const [Modal, modalApi] = useVbenModal({
|
||||
| modal | 显示遮罩 | `boolean` | `true` |
|
||||
| header | 显示header | `boolean` | `true` |
|
||||
| footer | 显示footer | `boolean\|slot` | `true` |
|
||||
| confirmDisabled | 禁用确认按钮 | `boolean` | `false` |
|
||||
| confirmLoading | 确认按钮loading状态 | `boolean` | `false` |
|
||||
| closeOnClickModal | 点击遮罩关闭弹窗 | `boolean` | `true` |
|
||||
| closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` |
|
||||
| confirmText | 确认按钮文本 | `string\|slot` | `确认` |
|
||||
| cancelText | 取消按钮文本 | `string\|slot` | `取消` |
|
||||
| showCancelButton | 显示取消按钮 | `boolean` | `true` |
|
||||
| showConfirmButton | 显示确认按钮文本 | `boolean` | `true` |
|
||||
| showConfirmButton | 显示确认按钮 | `boolean` | `true` |
|
||||
| class | modal的class,宽度通过这个配置 | `string` | - |
|
||||
| contentClass | modal内容区域的class | `string` | - |
|
||||
| footerClass | modal底部区域的class | `string` | - |
|
||||
|
@@ -6,6 +6,10 @@
|
||||
|
||||
:::
|
||||
|
||||
## 布局组件
|
||||
|
||||
布局组件一般在页面内容区域用作顶层容器组件,提供一些统一的布局样式和基本功能。
|
||||
|
||||
## 通用组件
|
||||
|
||||
通用组件是一些常用的组件,比如弹窗、抽屉、表单等。大部分基于 `Tailwind CSS` 实现,可适用于不同 UI 组件库的应用。
|
||||
|
44
docs/src/components/layout-ui/page.md
Normal file
44
docs/src/components/layout-ui/page.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Page 常规页面组件
|
||||
|
||||
提供一个常规页面布局的组件,包括头部、内容区域、底部三个部分。
|
||||
|
||||
::: info 写在前面
|
||||
|
||||
本组件是一个基本布局组件。如果有更多的通用页面布局需求(比如双列布局等),可以根据实际需求自行封装。
|
||||
|
||||
:::
|
||||
|
||||
## 基础用法
|
||||
|
||||
将`Page`作为你的业务页面的根组件即可。
|
||||
|
||||
### Props
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| title | 页面标题 | `string\|slot` | - | - |
|
||||
| description | 页面描述(标题下的内容) | `string\|slot` | - | - |
|
||||
| contentClass | 内容区域的class | `string` | - | - |
|
||||
| headerClass | 头部区域的class | `string` | - | - |
|
||||
| footerClass | 底部区域的class | `string` | - | - |
|
||||
| autoContentHeight | 自动调整内容区域的高度 | `boolean` | `false` | - |
|
||||
|
||||
::: tip 注意
|
||||
|
||||
如果`title`、`description`、`extra`三者均未提供有效内容(通过`props`或者`slots`均可),则页面头部区域不会渲染。
|
||||
|
||||
:::
|
||||
|
||||
### Slots
|
||||
|
||||
| 插槽名称 | 描述 |
|
||||
| ----------- | ------------ |
|
||||
| default | 页面内容 |
|
||||
| title | 页面标题 |
|
||||
| description | 页面描述 |
|
||||
| extra | 页面头部右侧 |
|
||||
| footer | 页面底部 |
|
@@ -76,6 +76,8 @@ const formOptions: VbenFormProps = {
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: false,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/commitlint-config",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/stylelint-config",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/node-utils",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/tailwind-config",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/tsconfig",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/vite-config",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import type { ApplicationPluginOptions } from '../typing';
|
||||
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { fs } from '@vben/node-utils';
|
||||
@@ -21,12 +22,11 @@ function getConfFiles() {
|
||||
const script = process.env.npm_lifecycle_script as string;
|
||||
const reg = /--mode ([\d_a-z]+)/;
|
||||
const result = reg.exec(script);
|
||||
|
||||
let mode = 'production';
|
||||
if (result) {
|
||||
const mode = result[1];
|
||||
return ['.env', `.env.${mode}`];
|
||||
mode = result[1] as string;
|
||||
}
|
||||
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) {
|
||||
try {
|
||||
const envPath = await fs.readFile(join(process.cwd(), confFile), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const env = dotenv.parse(envPath);
|
||||
envConfig = { ...envConfig, ...env };
|
||||
const confFilePath = join(process.cwd(), confFile);
|
||||
if (existsSync(confFilePath)) {
|
||||
const envPath = await fs.readFile(confFilePath, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const env = dotenv.parse(envPath);
|
||||
envConfig = { ...envConfig, ...env };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error while parsing ${confFile}`, error);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vben-admin-monorepo",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"keywords": [
|
||||
"monorepo",
|
||||
@@ -99,7 +99,7 @@
|
||||
"node": ">=20.10.0",
|
||||
"pnpm": ">=9.12.0"
|
||||
},
|
||||
"packageManager": "pnpm@9.14.2",
|
||||
"packageManager": "pnpm@9.14.4",
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"allowedVersions": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/design",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
@@ -28,7 +28,7 @@
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"development": "./src/index.ts",
|
||||
"default": "./dist/style.css"
|
||||
"default": "./dist/design.css"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -58,6 +58,8 @@
|
||||
|
||||
/* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
|
||||
--accent: 216 5% 19%;
|
||||
--accent-dark: 240 0% 22%;
|
||||
--accent-darker: 240 0% 26%;
|
||||
--accent-lighter: 216 5% 12%;
|
||||
--accent-hover: 216 5% 24%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
|
@@ -58,6 +58,8 @@
|
||||
|
||||
/* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
|
||||
--accent: 240 5% 96%;
|
||||
--accent-dark: 216 14% 93%;
|
||||
--accent-darker: 216 11% 91%;
|
||||
--accent-lighter: 240 0% 98%;
|
||||
--accent-hover: 200deg 10% 90%;
|
||||
--accent-foreground: 240 6% 10%;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/icons",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/shared",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
@@ -86,12 +86,16 @@
|
||||
"dayjs": "catalog:",
|
||||
"defu": "catalog:",
|
||||
"lodash.clonedeep": "catalog:",
|
||||
"lodash.get": "catalog:",
|
||||
"lodash.isequal": "catalog:",
|
||||
"nprogress": "catalog:",
|
||||
"tailwind-merge": "catalog:",
|
||||
"theme-colors": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash.clonedeep": "catalog:",
|
||||
"@types/lodash.get": "catalog:",
|
||||
"@types/lodash.isequal": "catalog:",
|
||||
"@types/nprogress": "catalog:"
|
||||
}
|
||||
}
|
||||
|
@@ -16,3 +16,11 @@ export function formatDate(time: number | string, format = 'YYYY-MM-DD') {
|
||||
export function formatDateTime(time: number | string) {
|
||||
return formatDate(time, 'YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
|
||||
export function isDate(value: any): value is Date {
|
||||
return value instanceof Date;
|
||||
}
|
||||
|
||||
export function isDayjsObject(value: any): value is dayjs.Dayjs {
|
||||
return dayjs.isDayjs(value);
|
||||
}
|
||||
|
@@ -15,3 +15,5 @@ export * from './update-css-variables';
|
||||
export * from './util';
|
||||
export * from './window';
|
||||
export { default as cloneDeep } from 'lodash.clonedeep';
|
||||
export { default as get } from 'lodash.get';
|
||||
export { default as isEqual } from 'lodash.isequal';
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/typings",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/composables",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/preferences",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/form-ui",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -14,6 +14,8 @@ import { Store } from '@vben-core/shared/store';
|
||||
import {
|
||||
bindMethods,
|
||||
createMerge,
|
||||
isDate,
|
||||
isDayjsObject,
|
||||
isFunction,
|
||||
isObject,
|
||||
mergeWithArrayOverride,
|
||||
@@ -36,6 +38,7 @@ function getDefaultState(): VbenFormProps {
|
||||
showCollapseButton: false,
|
||||
showDefaultActions: true,
|
||||
submitButtonOptions: {},
|
||||
submitOnChange: false,
|
||||
submitOnEnter: false,
|
||||
wrapperClass: 'grid-cols-1',
|
||||
};
|
||||
@@ -185,7 +188,7 @@ export class FormApi {
|
||||
const fieldSet = new Set(fields);
|
||||
const schema = this.state?.schema ?? [];
|
||||
|
||||
const filterSchema = schema.filter((item) => fieldSet.has(item.fieldName));
|
||||
const filterSchema = schema.filter((item) => !fieldSet.has(item.fieldName));
|
||||
|
||||
this.setState({
|
||||
schema: filterSchema,
|
||||
@@ -251,10 +254,19 @@ export class FormApi {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并算法有待改进,目前的算法不支持object类型的值。
|
||||
* antd的日期时间相关组件的值类型为dayjs对象
|
||||
* element-plus的日期时间相关组件的值类型可能为Date对象
|
||||
* 以上两种类型需要排除深度合并
|
||||
*/
|
||||
const fieldMergeFn = createMerge((obj, key, value) => {
|
||||
if (key in obj) {
|
||||
obj[key] =
|
||||
!Array.isArray(obj[key]) && isObject(obj[key])
|
||||
!Array.isArray(obj[key]) &&
|
||||
isObject(obj[key]) &&
|
||||
!isDayjsObject(obj[key]) &&
|
||||
!isDate(obj[key])
|
||||
? fieldMergeFn(obj[key], value)
|
||||
: value;
|
||||
}
|
||||
|
@@ -342,6 +342,12 @@ export interface VbenFormProps<
|
||||
*/
|
||||
submitButtonOptions?: ActionButtonOptions;
|
||||
|
||||
/**
|
||||
* 是否在字段值改变时提交表单
|
||||
* @default false
|
||||
*/
|
||||
submitOnChange?: boolean;
|
||||
|
||||
/**
|
||||
* 是否在回车时提交表单
|
||||
* @default false
|
||||
|
@@ -6,7 +6,9 @@ import type { ExtendedFormApi, VbenFormProps } from './types';
|
||||
import { useForwardPriorityValues } from '@vben-core/composables';
|
||||
// import { isFunction } from '@vben-core/shared/utils';
|
||||
|
||||
import { useTemplateRef } from 'vue';
|
||||
import { toRaw, useTemplateRef, watch } from 'vue';
|
||||
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
|
||||
import FormActions from './components/form-actions.vue';
|
||||
import {
|
||||
@@ -56,6 +58,17 @@ function handleKeyDownEnter(event: KeyboardEvent) {
|
||||
|
||||
formActionsRef.value?.handleSubmit?.();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => form.values,
|
||||
useDebounceFn(() => {
|
||||
(props.handleValuesChange ?? state.value.handleValuesChange)?.(
|
||||
toRaw(form.values),
|
||||
);
|
||||
state.value.submitOnChange && props.formApi?.submitForm();
|
||||
}, 300),
|
||||
{ deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/layout-ui",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -63,7 +63,7 @@ const logoStyle = computed((): CSSProperties => {
|
||||
<header
|
||||
:class="theme"
|
||||
: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">
|
||||
<slot name="logo"></slot>
|
||||
|
@@ -191,7 +191,10 @@ watchEffect(() => {
|
||||
function calcMenuWidthStyle(isHiddenDom: boolean): CSSProperties {
|
||||
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;
|
||||
|
||||
|
@@ -192,7 +192,7 @@ const headerFixed = computed(() => {
|
||||
});
|
||||
|
||||
const showSidebar = computed(() => {
|
||||
return isSideMode.value && sidebarEnable.value;
|
||||
return isSideMode.value && sidebarEnable.value && !props.sidebarHidden;
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -533,7 +533,7 @@ function handleHeaderToggle() {
|
||||
<template #toggle-button>
|
||||
<VbenIconButton
|
||||
v-if="showHeaderToggleButton"
|
||||
class="my-0 ml-2 mr-1 rounded-md"
|
||||
class="my-0 mr-1 rounded-md"
|
||||
@click="handleHeaderToggle"
|
||||
>
|
||||
<Menu class="size-4" />
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/menu-ui",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -41,6 +41,7 @@ export class DrawerApi {
|
||||
loading: false,
|
||||
modal: true,
|
||||
openAutoFocus: false,
|
||||
placement: 'right',
|
||||
showCancelButton: true,
|
||||
showConfirmButton: true,
|
||||
title: '',
|
||||
|
@@ -4,6 +4,8 @@ import type { DrawerApi } from './drawer-api';
|
||||
|
||||
import type { Component, Ref } from 'vue';
|
||||
|
||||
export type DrawerPlacement = 'bottom' | 'left' | 'right' | 'top';
|
||||
|
||||
export interface DrawerProps {
|
||||
/**
|
||||
* 取消按钮文字
|
||||
@@ -72,6 +74,12 @@ export interface DrawerProps {
|
||||
* 是否自动聚焦
|
||||
*/
|
||||
openAutoFocus?: boolean;
|
||||
|
||||
/**
|
||||
* 抽屉位置
|
||||
* @default right
|
||||
*/
|
||||
placement?: DrawerPlacement;
|
||||
/**
|
||||
* 是否显示取消按钮
|
||||
* @default true
|
||||
|
@@ -62,6 +62,7 @@ const {
|
||||
loading: showLoading,
|
||||
modal,
|
||||
openAutoFocus,
|
||||
placement,
|
||||
showCancelButton,
|
||||
showConfirmButton,
|
||||
title,
|
||||
@@ -119,11 +120,13 @@ function handleFocusOutside(e: Event) {
|
||||
<SheetContent
|
||||
:class="
|
||||
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"
|
||||
:open="state?.isOpen"
|
||||
:side="placement"
|
||||
@close-auto-focus="handleFocusOutside"
|
||||
@escape-key-down="escapeKeyDown"
|
||||
@focus-outside="handleFocusOutside"
|
||||
|
@@ -41,6 +41,7 @@ export class ModalApi {
|
||||
class: '',
|
||||
closeOnClickModal: true,
|
||||
closeOnPressEscape: true,
|
||||
confirmDisabled: false,
|
||||
confirmLoading: false,
|
||||
contentClass: '',
|
||||
draggable: false,
|
||||
|
@@ -35,6 +35,10 @@ export interface ModalProps {
|
||||
* @default true
|
||||
*/
|
||||
closeOnPressEscape?: boolean;
|
||||
/**
|
||||
* 禁用确认按钮
|
||||
*/
|
||||
confirmDisabled?: boolean;
|
||||
/**
|
||||
* 确定按钮 loading
|
||||
* @default false
|
||||
|
@@ -59,6 +59,7 @@ const {
|
||||
closable,
|
||||
closeOnClickModal,
|
||||
closeOnPressEscape,
|
||||
confirmDisabled,
|
||||
confirmLoading,
|
||||
confirmText,
|
||||
contentClass,
|
||||
@@ -171,7 +172,7 @@ function handleFocusOutside(e: Event) {
|
||||
ref="contentRef"
|
||||
:class="
|
||||
cn(
|
||||
'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col p-0 sm:rounded-2xl',
|
||||
'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col p-0 sm:rounded-[var(--radius)]',
|
||||
modalClass,
|
||||
{
|
||||
'border-border border': bordered,
|
||||
@@ -235,7 +236,7 @@ function handleFocusOutside(e: Event) {
|
||||
ref="wrapperRef"
|
||||
:class="
|
||||
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
|
||||
:is="components.PrimaryButton || VbenButton"
|
||||
v-if="showConfirmButton"
|
||||
:disabled="confirmDisabled"
|
||||
:loading="confirmLoading"
|
||||
@click="() => modalApi?.onConfirm()"
|
||||
>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/shadcn-ui",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"#main": "./dist/index.mjs",
|
||||
"#module": "./dist/index.mjs",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
|
@@ -7,10 +7,7 @@ const props = defineProps<{ class?: any }>();
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
|
||||
props.class,
|
||||
)
|
||||
cn('flex flex-row flex-col-reverse justify-end gap-x-2', props.class)
|
||||
"
|
||||
>
|
||||
<slot></slot>
|
||||
|
@@ -7,10 +7,7 @@ const props = defineProps<{ class?: any }>();
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
|
||||
props.class,
|
||||
)
|
||||
cn('flex flex-row flex-col-reverse justify-end gap-x-2', props.class)
|
||||
"
|
||||
>
|
||||
<slot></slot>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/tabs-ui",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/constants",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/access",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/common-ui",
|
||||
"version": "5.4.8",
|
||||
"version": "5.5.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
@@ -29,6 +29,7 @@
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@vben-core/form-ui": "workspace:*",
|
||||
"@vben-core/popup-ui": "workspace:*",
|
||||
"@vben-core/preferences": "workspace:*",
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@vben-core/shared": "workspace:*",
|
||||
"@vben/constants": "workspace:*",
|
||||
|
@@ -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>
|
@@ -0,0 +1 @@
|
||||
export { default as ApiSelect } from './api-select.vue';
|
@@ -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]);
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user