Merge branch 'main' of https://gitee.com/xia5520/ruoyi-plus-vben5 into hr
This commit is contained in:
commit
49e689a5bb
@ -18,6 +18,9 @@
|
||||
- VxeTable在开启/关闭查询表单时 需要使用不同的padding
|
||||
- VxeTable表格刷新 默认为reload 修改为在当前页刷新(query)
|
||||
- 岗位管理 部门参数错误
|
||||
- 角色管理 菜单分配 节点独立下的回显及提交问题
|
||||
- 租户管理 套餐管理 回显时候`已选中节点`数量为0
|
||||
- 用户管理 更新用户时打开drawer需要加载该部门下的岗位信息
|
||||
|
||||
**OTHERS**
|
||||
|
||||
|
@ -2,10 +2,12 @@
|
||||
|
||||
## 提示
|
||||
|
||||
该仓库使用vben最新版本v5开发, 老版本v2地址 [前往](https://gitee.com/dapppp/ruoyi-plus-vben)
|
||||
该仓库使用vben最新版本v5开发, ~~老版本v2地址(不维护)~~ [前往](https://gitee.com/dapppp/ruoyi-plus-vben)
|
||||
|
||||
v5版本采用分仓(包)目录结构, 具体开发路径为: `根目录/apps/web-antd`
|
||||
|
||||
目前对应后端版本: **5.2.3/2.2.3**
|
||||
|
||||
## 进度
|
||||
|
||||
**工作流相关模块等待后端重构后开发**
|
||||
|
@ -74,24 +74,27 @@ const checkedRealKeys = ref<(number | string)[]>([]);
|
||||
* 取第一次的menuTree id 设置到checkedMenuKeys
|
||||
* 主要为了解决没有任何修改 直接点击保存的情况
|
||||
*/
|
||||
const stop = watch(
|
||||
() => props.treeData,
|
||||
() => {
|
||||
/** 节点关联情况下是不带父节点的 */
|
||||
if (props.checkStrictly) {
|
||||
/** 找到父节点 添加上 */
|
||||
const parentIds = findGroupParentIds(
|
||||
props.treeData,
|
||||
checkedKeys.value as any,
|
||||
);
|
||||
checkedRealKeys.value = [...parentIds, ...checkedKeys.value];
|
||||
} else {
|
||||
/** 节点独立 这里是全部的节点 */
|
||||
checkedRealKeys.value = checkedKeys.value;
|
||||
}
|
||||
const stop = watch([checkedKeys, () => props.treeData], () => {
|
||||
if (
|
||||
props.checkStrictly &&
|
||||
checkedKeys.value.length > 0 &&
|
||||
props.treeData.length > 0
|
||||
) {
|
||||
/** 找到父节点 添加上 */
|
||||
const parentIds = findGroupParentIds(
|
||||
props.treeData,
|
||||
checkedKeys.value as any,
|
||||
{ id: props.fieldNames.key },
|
||||
);
|
||||
checkedRealKeys.value = [...parentIds, ...checkedKeys.value];
|
||||
stop();
|
||||
},
|
||||
);
|
||||
}
|
||||
if (!props.checkStrictly && checkedKeys.value.length > 0) {
|
||||
/** 节点独立 这里是全部的节点 */
|
||||
checkedRealKeys.value = checkedKeys.value;
|
||||
stop();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { DEFAULT_HOME_PATH } from '@vben/constants';
|
||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
||||
|
||||
import { AuthPageLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
@ -45,6 +45,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
name: 'Authentication',
|
||||
path: '/auth',
|
||||
redirect: LOGIN_PATH,
|
||||
children: [
|
||||
{
|
||||
name: 'Login',
|
||||
|
@ -34,10 +34,7 @@ async function handleForceOffline(row: Recordable<any>) {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">我的在线设备</span>
|
||||
</template>
|
||||
<BasicTable table-title="我的在线设备">
|
||||
<template #action="{ row }">
|
||||
<Popconfirm
|
||||
:title="`确认强制下线[${row.userName}]?`"
|
||||
|
@ -153,10 +153,7 @@ async function handleUnlock() {
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">登录日志列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="登录日志列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -55,10 +55,7 @@ async function handleForceOffline(row: Recordable<any>) {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">在线用户列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="在线用户列表">
|
||||
<template #action="{ row }">
|
||||
<Popconfirm
|
||||
:get-popup-container="getPopupContainer"
|
||||
|
@ -153,10 +153,7 @@ async function handleDelete() {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">操作日志列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="操作日志列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -119,10 +119,7 @@ const { hasAccessByCodes } = useAccess();
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">系统授权列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="系统授权列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -131,10 +131,7 @@ async function handleRefreshCache() {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">参数列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="参数列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="handleRefreshCache"> 刷新缓存 </a-button>
|
||||
|
@ -11,8 +11,7 @@ import {
|
||||
removeEmptyChildren,
|
||||
} from '@vben/utils';
|
||||
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { Popconfirm, Space, Tooltip } from 'ant-design-vue';
|
||||
import { Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { deptList, deptRemove } from '#/api/system/dept';
|
||||
@ -133,15 +132,7 @@ function setExpandOrCollapse(expand: boolean) {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<div class="flex items-center gap-[6px]">
|
||||
<span class="pl-[7px] text-[16px]">部门列表</span>
|
||||
<Tooltip title="提示:双击展开/收起子菜单">
|
||||
<QuestionCircleOutlined class="text-center" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<BasicTable table-title="部门列表" table-title-help="双击展开/收起子菜单">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="setExpandOrCollapse(false)">
|
||||
|
@ -144,10 +144,7 @@ emitter.on('rowClick', async (value) => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">字典数据列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="字典数据列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -188,10 +188,7 @@ const couldSyncTenantDict = computed(() => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">字典类型列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="字典类型列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<Dropdown>
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
removeEmptyChildren,
|
||||
} from '@vben/utils';
|
||||
|
||||
import { Popconfirm, Space, Tooltip } from 'ant-design-vue';
|
||||
import { Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { menuList, menuRemove } from '#/api/system/menu';
|
||||
@ -24,7 +24,6 @@ import menuDrawer from './menu-drawer.vue';
|
||||
/**
|
||||
* 不要问为什么有两个根节点 v-if会控制只会渲染一个
|
||||
*/
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const formOptions: VbenFormProps = {
|
||||
commonConfig: {
|
||||
@ -139,15 +138,7 @@ const isAdmin = computed(() => {
|
||||
|
||||
<template>
|
||||
<Page v-if="isAdmin" :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<div class="flex items-center gap-[6px]">
|
||||
<span class="pl-[7px] text-[16px]">菜单列表</span>
|
||||
<Tooltip title="提示:双击展开/收起子菜单">
|
||||
<QuestionCircleOutlined class="text-center" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<BasicTable table-title="菜单列表" table-title-help="双击展开/收起子菜单">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="setExpandOrCollapse(false)">
|
||||
|
@ -123,10 +123,7 @@ function handleMultiDelete() {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">通知公告列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="通知公告列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -131,10 +131,7 @@ const { hasAccessByCodes } = useAccess();
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">oss配置列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="oss配置列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -167,10 +167,7 @@ const [FileUploadModal, fileUploadApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">文件列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="文件列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<Tooltip title="预览图片">
|
||||
|
@ -139,16 +139,13 @@ function handleMultiDelete() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true" content-class="flex gap-[8px]">
|
||||
<Page :auto-content-height="true" content-class="flex gap-[8px] w-full">
|
||||
<DeptTree
|
||||
v-model:select-dept-id="selectDeptId"
|
||||
class="w-[260px]"
|
||||
@select="() => tableApi.query()"
|
||||
/>
|
||||
<BasicTable class="flex-1 overflow-hidden">
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">岗位列表</span>
|
||||
</template>
|
||||
<BasicTable class="flex-1 overflow-hidden" table-title="岗位列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -118,10 +118,7 @@ function handleMultipleAuthCancel() {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">已分配的用户列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="已分配的用户列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -131,6 +131,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
componentProps: {
|
||||
allowClear: false,
|
||||
options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE),
|
||||
getPopupContainer,
|
||||
},
|
||||
defaultValue: '0',
|
||||
fieldName: 'status',
|
||||
@ -138,6 +139,15 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
label: '角色状态',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Radio',
|
||||
dependencies: {
|
||||
show: () => false,
|
||||
triggerFields: [''],
|
||||
},
|
||||
fieldName: 'menuCheckStrictly',
|
||||
label: '菜单权限',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
defaultValue: [],
|
||||
|
@ -163,10 +163,7 @@ function handleAssignRole(record: Recordable<any>) {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">角色列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="角色列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -1,162 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface';
|
||||
import type { DataNode } from 'ant-design-vue/es/tree';
|
||||
import type { CheckInfo } from 'ant-design-vue/es/vc-tree/props';
|
||||
|
||||
import { computed, type PropType, ref, watch } from 'vue';
|
||||
|
||||
import { findGroupParentIds, treeToList } from '@vben/utils';
|
||||
|
||||
import { Checkbox, Tree } from 'ant-design-vue';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
const props = defineProps({
|
||||
checkStrictly: {
|
||||
default: true,
|
||||
type: Boolean,
|
||||
},
|
||||
menuTree: {
|
||||
default: () => [],
|
||||
type: Array as PropType<DataNode[]>,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits<{ checkStrictlyChange: [boolean] }>();
|
||||
|
||||
const expandStatus = ref(false);
|
||||
const selectAllStatus = ref(false);
|
||||
|
||||
/**
|
||||
* 后台的这个字段跟antd/ele是反的
|
||||
* 组件库这个字段代表不关联
|
||||
* 后台这个代表关联
|
||||
*/
|
||||
const innerCheckedStrictly = computed(() => {
|
||||
return !props.checkStrictly;
|
||||
});
|
||||
|
||||
function handleCheckStrictlyChange(e: CheckboxChangeEvent) {
|
||||
emit('checkStrictlyChange', e.target.checked);
|
||||
}
|
||||
|
||||
const associationText = computed(() => {
|
||||
return props.checkStrictly ? '父子节点关联' : '父子节点独立';
|
||||
});
|
||||
|
||||
/**
|
||||
* 这个只用于界面显示
|
||||
* 关联情况下 只会有最末尾的节点被选中
|
||||
*/
|
||||
const checkedKeys = defineModel('value', {
|
||||
default: () => [],
|
||||
type: Array as PropType<(number | string)[]>,
|
||||
});
|
||||
// 所有节点的ID
|
||||
const allKeys = computed(() => {
|
||||
return treeToList(props.menuTree).map((item: any) => item.id);
|
||||
});
|
||||
|
||||
/** 已经选择的所有节点 包括子/父节点 */
|
||||
const checkedMenuKeys = ref<(number | string)[]>([]);
|
||||
|
||||
/**
|
||||
* 取第一次的menuTree id 设置到checkedMenuKeys
|
||||
* 主要为了解决没有任何修改 直接点击保存的情况
|
||||
*/
|
||||
const stop = watch(
|
||||
() => props.menuTree,
|
||||
() => {
|
||||
/** 节点关联情况下是不带父节点的 */
|
||||
if (props.checkStrictly) {
|
||||
/** 找到父节点 添加上 */
|
||||
const parentIds = findGroupParentIds(
|
||||
props.menuTree,
|
||||
checkedKeys.value as any,
|
||||
);
|
||||
checkedMenuKeys.value = [...parentIds, ...checkedKeys.value];
|
||||
} else {
|
||||
/** 节点独立 这里是全部的节点 */
|
||||
checkedMenuKeys.value = checkedKeys.value;
|
||||
}
|
||||
stop();
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param checkedKeys 已经选中的子节点的ID
|
||||
* @param info info.halfCheckedKeys为父节点的ID
|
||||
*/
|
||||
type CheckedState<T = number | string> =
|
||||
| { checked: T[]; halfChecked: T[] }
|
||||
| T[];
|
||||
function handleChecked(checkedKeys: CheckedState, info: CheckInfo) {
|
||||
// 数组的话为节点关联
|
||||
if (Array.isArray(checkedKeys)) {
|
||||
const halfCheckedKeys: number[] = (info.halfCheckedKeys || []) as number[];
|
||||
checkedMenuKeys.value = [...halfCheckedKeys, ...checkedKeys];
|
||||
} else {
|
||||
checkedMenuKeys.value = [...checkedKeys.checked];
|
||||
}
|
||||
}
|
||||
|
||||
function handleExpandChange(e: CheckboxChangeEvent) {
|
||||
// 这个用于展示
|
||||
checkedKeys.value = e.target.checked ? allKeys.value : [];
|
||||
// 这个用于提交
|
||||
checkedMenuKeys.value = e.target.checked ? allKeys.value : [];
|
||||
}
|
||||
|
||||
const expandedKeys = ref<string[]>([]);
|
||||
function handleExpandOrCollapseAll(e: CheckboxChangeEvent) {
|
||||
const expand = e.target.checked;
|
||||
expandedKeys.value = expand ? allKeys.value : [];
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getCheckedKeys: () => checkedMenuKeys.value,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-background w-full rounded-lg border-[1px] p-[12px]">
|
||||
<div class="flex items-center gap-2 border-b-[1px] pb-2">
|
||||
<span>节点状态: </span>
|
||||
<span
|
||||
:class="[props.checkStrictly ? 'text-primary' : 'text-red-500']"
|
||||
class="font-semibold"
|
||||
>
|
||||
{{ associationText }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-between border-b-[1px] py-2"
|
||||
>
|
||||
<Checkbox
|
||||
v-model:checked="expandStatus"
|
||||
@change="handleExpandOrCollapseAll"
|
||||
>
|
||||
展开/折叠全部
|
||||
</Checkbox>
|
||||
<Checkbox v-model:checked="selectAllStatus" @change="handleExpandChange">
|
||||
全选/取消全选
|
||||
</Checkbox>
|
||||
<Checkbox :checked="checkStrictly" @change="handleCheckStrictlyChange">
|
||||
父子节点关联
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div class="py-2">
|
||||
<Tree
|
||||
v-if="menuTree.length > 0"
|
||||
v-model:check-strictly="innerCheckedStrictly"
|
||||
v-model:checked-keys="checkedKeys"
|
||||
v-model:expanded-keys="expandedKeys"
|
||||
:checkable="true"
|
||||
:field-names="{ title: 'label', key: 'id' }"
|
||||
:selectable="false"
|
||||
:tree-data="menuTree"
|
||||
@check="handleChecked"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -188,6 +188,7 @@ export const drawerSchema: FormSchemaGetter = () => [
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
showTime: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
getPopupContainer,
|
||||
},
|
||||
defaultValue: defaultExpireTime,
|
||||
fieldName: 'expireTime',
|
||||
|
@ -155,10 +155,7 @@ const isSuperAdmin = computed(() => {
|
||||
|
||||
<template>
|
||||
<Page v-if="isSuperAdmin" :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">租户列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="租户列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -142,10 +142,7 @@ const isSuperAdmin = computed(() => {
|
||||
|
||||
<template>
|
||||
<Page v-if="isSuperAdmin" :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">租户套餐列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="租户套餐列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -5,6 +5,8 @@ import { useVbenDrawer } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
import { cloneDeep, eachTree, listToTree } from '@vben/utils';
|
||||
|
||||
import { omit } from 'lodash-es';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { menuList, tenantPackageMenuTreeSelect } from '#/api/system/menu';
|
||||
import {
|
||||
@ -66,7 +68,9 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
||||
isUpdate.value = !!id;
|
||||
if (isUpdate.value && id) {
|
||||
const record = await packageInfo(id);
|
||||
await formApi.setValues(record);
|
||||
// 需要排除menuIds menuIds为string
|
||||
// 通过setupMenuTreeSelect设置
|
||||
await formApi.setValues(omit(record, ['menuIds']));
|
||||
}
|
||||
/**
|
||||
* 加载菜单树和已勾选菜单
|
||||
|
@ -200,10 +200,7 @@ function handleResetPwd(record: Recordable<any>) {
|
||||
class="w-[260px]"
|
||||
@select="() => tableApi.query()"
|
||||
/>
|
||||
<BasicTable class="flex-1 overflow-hidden">
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">用户列表</span>
|
||||
</template>
|
||||
<BasicTable class="flex-1 overflow-hidden" table-title="用户列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -60,6 +60,24 @@ function genRoleOptionlabel(role: Role) {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 岗位的加载
|
||||
*/
|
||||
async function setupPostOptions(deptId: number | string) {
|
||||
const postListResp = await postOptionSelect(deptId);
|
||||
const options = postListResp.map((item) => ({
|
||||
label: item.postName,
|
||||
value: item.postId,
|
||||
}));
|
||||
const placeholder = options.length > 0 ? '请选择' : '该部门下暂无岗位';
|
||||
formApi.updateSchema([
|
||||
{
|
||||
componentProps: { options, placeholder },
|
||||
fieldName: 'postIds',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化部门选择
|
||||
*/
|
||||
@ -80,22 +98,7 @@ async function setupDeptSelect() {
|
||||
getPopupContainer,
|
||||
async onSelect(deptId: number | string) {
|
||||
/** 根据部门ID加载岗位 */
|
||||
const postListResp = await postOptionSelect(deptId);
|
||||
const options = postListResp.map((item) => ({
|
||||
label: item.postName,
|
||||
value: item.postId,
|
||||
}));
|
||||
const placeholder =
|
||||
options.length > 0 ? '请选择' : '该部门下暂无岗位';
|
||||
/**
|
||||
* TODO: 可以考虑加上post编码
|
||||
*/
|
||||
formApi.updateSchema([
|
||||
{
|
||||
componentProps: { options, placeholder },
|
||||
fieldName: 'postIds',
|
||||
},
|
||||
]);
|
||||
await setupPostOptions(deptId);
|
||||
/** 变化后需要重新选择岗位 */
|
||||
formModel.postIds = [];
|
||||
},
|
||||
@ -185,6 +188,8 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
|
||||
// 添加角色和岗位
|
||||
formApi.setFieldValue('postIds', postIds),
|
||||
formApi.setFieldValue('roleIds', roleIds),
|
||||
// 更新时不会触发onSelect 需要手动调用
|
||||
setupPostOptions(user.deptId),
|
||||
]);
|
||||
}
|
||||
drawerApi.drawerLoading(false);
|
||||
|
@ -199,10 +199,7 @@ function handleImport() {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">代码生成列表</span>
|
||||
</template>
|
||||
<BasicTable table-title="代码生成列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button
|
||||
|
@ -11,7 +11,10 @@ setupVbenVxeTable({
|
||||
vxeUI.setConfig({
|
||||
grid: {
|
||||
align: 'center',
|
||||
border: true,
|
||||
border: false,
|
||||
columnConfig: {
|
||||
resizable: true,
|
||||
},
|
||||
minHeight: 180,
|
||||
proxyConfig: {
|
||||
autoLoad: true,
|
||||
@ -24,6 +27,7 @@ setupVbenVxeTable({
|
||||
showResponseMsg: false,
|
||||
},
|
||||
round: true,
|
||||
showOverflow: true,
|
||||
size: 'small',
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { DEFAULT_HOME_PATH } from '@vben/constants';
|
||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
||||
|
||||
import { AuthPageLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
@ -37,6 +37,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
name: 'Authentication',
|
||||
path: '/auth',
|
||||
redirect: LOGIN_PATH,
|
||||
children: [
|
||||
{
|
||||
name: 'Login',
|
||||
|
@ -11,7 +11,10 @@ setupVbenVxeTable({
|
||||
vxeUI.setConfig({
|
||||
grid: {
|
||||
align: 'center',
|
||||
border: true,
|
||||
border: false,
|
||||
columnConfig: {
|
||||
resizable: true,
|
||||
},
|
||||
minHeight: 180,
|
||||
proxyConfig: {
|
||||
autoLoad: true,
|
||||
@ -24,6 +27,7 @@ setupVbenVxeTable({
|
||||
showResponseMsg: false,
|
||||
},
|
||||
round: true,
|
||||
showOverflow: true,
|
||||
size: 'small',
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { DEFAULT_HOME_PATH } from '@vben/constants';
|
||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
||||
|
||||
import { AuthPageLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
@ -37,6 +37,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
name: 'Authentication',
|
||||
path: '/auth',
|
||||
redirect: LOGIN_PATH,
|
||||
children: [
|
||||
{
|
||||
name: 'Login',
|
||||
|
@ -40,8 +40,5 @@
|
||||
"pkg-types": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"rimraf": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chalk": "catalog:"
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export class ModalApi {
|
||||
} = options;
|
||||
|
||||
const defaultState: ModalState = {
|
||||
bordered: false,
|
||||
bordered: true,
|
||||
centered: false,
|
||||
class: '',
|
||||
closeOnClickModal: true,
|
||||
|
@ -258,7 +258,13 @@ function handleFocusOutside(e: Event) {
|
||||
v-if="showFooter"
|
||||
ref="footerRef"
|
||||
:class="
|
||||
cn('flex-row items-center justify-end border-t p-2', footerClass)
|
||||
cn(
|
||||
'flex-row items-center justify-end p-2',
|
||||
{
|
||||
'border-t': bordered,
|
||||
},
|
||||
footerClass,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot name="prepend-footer"></slot>
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
/* base */
|
||||
--vxe-ui-base-popup-border-color: hsl(var(--border));
|
||||
--vxe-ui-input-disabled-color: hsl(var(--border) / 60%);
|
||||
|
||||
/* --vxe-ui-base-popup-box-shadow: 0px 12px 30px 8px rgb(0 0 0 / 50%); */
|
||||
|
||||
|
@ -19,6 +19,14 @@ export interface VxePaginationInfo {
|
||||
}
|
||||
|
||||
export interface VxeGridProps {
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
tableTitle?: string;
|
||||
/**
|
||||
* 标题帮助
|
||||
*/
|
||||
tableTitleHelp?: string;
|
||||
/**
|
||||
* 组件class
|
||||
*/
|
||||
|
@ -22,7 +22,7 @@ import { EmptyIcon } from '@vben/icons';
|
||||
import { $t } from '@vben/locales';
|
||||
import { usePreferences } from '@vben/preferences';
|
||||
import { cloneDeep, cn, mergeWithArrayOverride } from '@vben/utils';
|
||||
import { VbenLoading } from '@vben-core/shadcn-ui';
|
||||
import { VbenHelpTooltip, VbenLoading } from '@vben-core/shadcn-ui';
|
||||
|
||||
import { VxeGrid, VxeUI } from 'vxe-table';
|
||||
|
||||
@ -51,6 +51,8 @@ const {
|
||||
gridClass,
|
||||
gridEvents,
|
||||
formOptions,
|
||||
tableTitle,
|
||||
tableTitleHelp,
|
||||
} = usePriorityValues(props, state);
|
||||
|
||||
const { isMobile } = usePreferences();
|
||||
@ -81,31 +83,45 @@ const [Form, formApi] = useTableForm({
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
||||
});
|
||||
|
||||
const showTableTitle = computed(() => {
|
||||
return !!slots.tableTitle?.() || tableTitle.value;
|
||||
});
|
||||
|
||||
const showToolbar = computed(() => {
|
||||
return !!slots['toolbar-actions']?.() || !!slots['toolbar-tools']?.();
|
||||
return (
|
||||
!!slots['toolbar-actions']?.() ||
|
||||
!!slots['toolbar-tools']?.() ||
|
||||
showTableTitle.value
|
||||
);
|
||||
});
|
||||
|
||||
const toolbarOptions = computed(() => {
|
||||
const slotActions = slots['toolbar-actions']?.();
|
||||
const slotTools = slots['toolbar-tools']?.();
|
||||
if (!showToolbar.value) {
|
||||
return {};
|
||||
}
|
||||
// 强制使用固定的toolbar配置,不允许用户自定义
|
||||
// 减少配置的复杂度,以及后续维护的成本
|
||||
return {
|
||||
toolbarConfig: {
|
||||
slots: {
|
||||
...(slotActions || showTableTitle.value
|
||||
? { buttons: 'toolbar-actions' }
|
||||
: {}),
|
||||
...(slotTools ? { tools: 'toolbar-tools' } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const options = computed(() => {
|
||||
const slotActions = slots['toolbar-actions']?.();
|
||||
const slotTools = slots['toolbar-tools']?.();
|
||||
|
||||
const globalGridConfig = VxeUI?.getConfig()?.grid ?? {};
|
||||
|
||||
const forceUseToolbarOptions = showToolbar.value
|
||||
? {
|
||||
toolbarConfig: {
|
||||
slots: {
|
||||
...(slotActions ? { buttons: 'toolbar-actions' } : {}),
|
||||
...(slotTools ? { tools: 'toolbar-tools' } : {}),
|
||||
},
|
||||
},
|
||||
}
|
||||
: {};
|
||||
|
||||
const mergedOptions: VxeTableGridProps = cloneDeep(
|
||||
mergeWithArrayOverride(
|
||||
{},
|
||||
forceUseToolbarOptions,
|
||||
toolbarOptions.value,
|
||||
toRaw(gridOptions.value),
|
||||
globalGridConfig,
|
||||
),
|
||||
@ -166,7 +182,7 @@ const delegatedSlots = computed(() => {
|
||||
const resultSlots: string[] = [];
|
||||
|
||||
for (const key of Object.keys(slots)) {
|
||||
if (!['empty', 'form', 'loading'].includes(key)) {
|
||||
if (!['empty', 'form', 'loading', 'toolbar-actions'].includes(key)) {
|
||||
resultSlots.push(key);
|
||||
}
|
||||
}
|
||||
@ -214,6 +230,7 @@ async function init() {
|
||||
);
|
||||
}
|
||||
|
||||
// formOptions支持响应式
|
||||
watch(
|
||||
formOptions,
|
||||
() => {
|
||||
@ -256,6 +273,20 @@ onMounted(() => {
|
||||
v-bind="options"
|
||||
v-on="events"
|
||||
>
|
||||
<!-- 左侧操作区域或者title -->
|
||||
<template v-if="showToolbar" #toolbar-actions="slotProps">
|
||||
<slot v-if="showTableTitle" name="table-title">
|
||||
<div class="mr-1 pl-1 text-[1rem]">
|
||||
{{ tableTitle }}
|
||||
<VbenHelpTooltip v-if="tableTitleHelp" trigger-class="pb-1">
|
||||
{{ tableTitleHelp }}
|
||||
</VbenHelpTooltip>
|
||||
</div>
|
||||
</slot>
|
||||
<slot name="toolbar-actions" v-bind="slotProps"> </slot>
|
||||
</template>
|
||||
|
||||
<!-- 继承默认的slot -->
|
||||
<template
|
||||
v-for="slotName in delegatedSlots"
|
||||
:key="slotName"
|
||||
@ -263,6 +294,8 @@ onMounted(() => {
|
||||
>
|
||||
<slot :name="slotName" v-bind="slotProps"></slot>
|
||||
</template>
|
||||
|
||||
<!-- form表单 -->
|
||||
<template #form>
|
||||
<div v-if="formOptions" class="relative rounded py-3 pb-4">
|
||||
<slot name="form">
|
||||
@ -296,11 +329,13 @@ onMounted(() => {
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- loading -->
|
||||
<template #loading>
|
||||
<slot name="loading">
|
||||
<VbenLoading :spinning="true" />
|
||||
</slot>
|
||||
</template>
|
||||
<!-- 统一控状态 -->
|
||||
<template #empty>
|
||||
<slot name="empty">
|
||||
<EmptyIcon class="mx-auto" />
|
||||
|
@ -245,11 +245,11 @@ export const useTabbarStore = defineStore('core-tabbar', {
|
||||
|
||||
// 下一个tab存在,跳转到下一个
|
||||
if (after) {
|
||||
this._close(currentRoute.value);
|
||||
this._close(tab);
|
||||
await this._goToTab(after, router);
|
||||
// 上一个tab存在,跳转到上一个
|
||||
} else if (before) {
|
||||
this._close(currentRoute.value);
|
||||
this._close(tab);
|
||||
await this._goToTab(before, router);
|
||||
} else {
|
||||
console.error('Failed to close the tab; only one tab remains open.');
|
||||
|
@ -11,7 +11,10 @@ setupVbenVxeTable({
|
||||
vxeUI.setConfig({
|
||||
grid: {
|
||||
align: 'center',
|
||||
border: true,
|
||||
border: false,
|
||||
columnConfig: {
|
||||
resizable: true,
|
||||
},
|
||||
minHeight: 180,
|
||||
proxyConfig: {
|
||||
autoLoad: true,
|
||||
@ -24,6 +27,7 @@ setupVbenVxeTable({
|
||||
showResponseMsg: false,
|
||||
},
|
||||
round: true,
|
||||
showOverflow: true,
|
||||
size: 'small',
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { DEFAULT_HOME_PATH } from '@vben/constants';
|
||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
||||
|
||||
import { AuthPageLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
@ -37,6 +37,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
name: 'Authentication',
|
||||
path: '/auth',
|
||||
redirect: LOGIN_PATH,
|
||||
children: [
|
||||
{
|
||||
name: 'Login',
|
||||
|
@ -76,10 +76,10 @@ function changeLoading() {
|
||||
<template #extra>
|
||||
<DocButton path="/components/common-ui/vben-vxe-table" />
|
||||
</template>
|
||||
<Grid>
|
||||
<template #toolbar-actions>
|
||||
<Grid table-title="基础列表" table-title-help="提示">
|
||||
<!-- <template #toolbar-actions>
|
||||
<Button class="mr-2" type="primary">左侧插槽</Button>
|
||||
</template>
|
||||
</template> -->
|
||||
<template #toolbar-tools>
|
||||
<Button class="mr-2" type="primary" @click="changeBorder">
|
||||
{{ showBorder ? '隐藏' : '显示' }}边框
|
||||
|
@ -51,7 +51,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ gridOptions });
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<Grid>
|
||||
<Grid table-title="数据列表" table-title-help="提示">
|
||||
<template #toolbar-tools>
|
||||
<Button class="mr-2" type="primary" @click="() => gridApi.query()">
|
||||
刷新当前页面
|
||||
|
@ -50,7 +50,7 @@ const collapseAll = () => {
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<Grid>
|
||||
<Grid table-title="数据列表" table-title-help="提示">
|
||||
<template #toolbar-tools>
|
||||
<Button class="mr-2" type="primary" @click="expandAll">
|
||||
展开全部
|
||||
|
@ -22,7 +22,7 @@ catalog:
|
||||
'@ctrl/tinycolor': ^4.1.0
|
||||
'@eslint/js': ^9.13.0
|
||||
'@faker-js/faker': ^9.0.3
|
||||
'@iconify/json': ^2.2.262
|
||||
'@iconify/json': ^2.2.263
|
||||
'@iconify/tailwind': ^1.1.3
|
||||
'@iconify/vue': ^4.1.2
|
||||
'@intlify/core-base': ^10.0.4
|
||||
@ -35,22 +35,21 @@ catalog:
|
||||
'@stylistic/stylelint-plugin': ^3.1.1
|
||||
'@tailwindcss/nesting': 0.0.0-insiders.565cd3e
|
||||
'@tailwindcss/typography': ^0.5.15
|
||||
'@tanstack/vue-query': ^5.59.13
|
||||
'@tanstack/vue-query': ^5.59.16
|
||||
'@tanstack/vue-store': ^0.5.6
|
||||
'@types/archiver': ^6.0.2
|
||||
'@types/chalk': ^2.2.0
|
||||
'@types/archiver': ^6.0.3
|
||||
'@types/eslint': ^9.6.1
|
||||
'@types/html-minifier-terser': ^7.0.2
|
||||
'@types/jsonwebtoken': ^9.0.7
|
||||
'@types/lodash.clonedeep': ^4.5.9
|
||||
'@types/node': ^22.7.8
|
||||
'@types/node': ^22.7.9
|
||||
'@types/nprogress': ^0.2.3
|
||||
'@types/postcss-import': ^14.0.3
|
||||
'@types/qrcode': ^1.5.5
|
||||
'@types/sortablejs': ^1.15.8
|
||||
'@typescript-eslint/eslint-plugin': ^8.11.0
|
||||
'@typescript-eslint/parser': ^8.11.0
|
||||
'@vee-validate/zod': ^4.14.3
|
||||
'@vee-validate/zod': ^4.14.4
|
||||
'@vite-pwa/vitepress': ^0.5.3
|
||||
'@vitejs/plugin-vue': ^5.1.4
|
||||
'@vitejs/plugin-vue-jsx': ^4.0.1
|
||||
@ -127,11 +126,11 @@ catalog:
|
||||
postcss-antd-fixes: ^0.2.0
|
||||
postcss-html: ^1.7.0
|
||||
postcss-import: ^16.1.0
|
||||
postcss-preset-env: ^10.0.7
|
||||
postcss-preset-env: ^10.0.8
|
||||
postcss-scss: ^4.0.9
|
||||
prettier: ^3.3.3
|
||||
prettier-plugin-tailwindcss: ^0.6.8
|
||||
publint: ^0.2.11
|
||||
publint: ^0.2.12
|
||||
qrcode: ^1.5.4
|
||||
radix-vue: ^1.9.7
|
||||
resolve.exports: ^2.0.2
|
||||
@ -157,15 +156,15 @@ catalog:
|
||||
typescript: ^5.6.3
|
||||
unbuild: ^2.0.0
|
||||
unplugin-element-plus: ^0.8.0
|
||||
vee-validate: ^4.14.3
|
||||
vite: ^5.4.9
|
||||
vee-validate: ^4.14.4
|
||||
vite: ^5.4.10
|
||||
vite-plugin-compression: ^0.5.1
|
||||
vite-plugin-dts: 4.2.1
|
||||
vite-plugin-html: ^3.2.2
|
||||
vite-plugin-lazy-import: ^1.0.7
|
||||
vite-plugin-lib-inject-css: ^2.1.1
|
||||
vite-plugin-pwa: ^0.20.5
|
||||
vite-plugin-vue-devtools: ^7.5.2
|
||||
vite-plugin-vue-devtools: ^7.5.3
|
||||
vitepress: ^1.4.1
|
||||
vitepress-plugin-group-icons: ^1.3.0
|
||||
vitest: ^2.1.3
|
||||
@ -174,8 +173,8 @@ catalog:
|
||||
vue-i18n: ^10.0.4
|
||||
vue-router: ^4.4.5
|
||||
vue-tsc: ^2.1.6
|
||||
vxe-pc-ui: ^4.2.26
|
||||
vxe-table: ^4.7.93
|
||||
vxe-pc-ui: ^4.2.28
|
||||
vxe-table: ^4.7.94
|
||||
watermark-js-plus: ^1.5.7
|
||||
zod: ^3.23.8
|
||||
zod-defaults: ^0.1.3
|
||||
|
Loading…
Reference in New Issue
Block a user