项目初始化

This commit is contained in:
2026-03-19 10:57:24 +08:00
commit ee94d420ad
3822 changed files with 582614 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div class="flex flex-row flex-wrap">
<template v-for="item in dataScopes" :key="item.moduleCode">
<div class="mb-5 mr-5" v-if="moduleCodes.includes(item.moduleCode) && ctrlPermis.includes(item.ctrlPermi)">
<BasicTree
class="bg-gray"
style="min-width: 300px"
:title="t(item['ctrlName_' + localeStore.getLocale] || item.ctrlName)"
:toolbar="true"
:checkable="true"
:checkStrictly="!(item.chkboxType?.Y + item.chkboxType?.N).includes('p')"
:api="api"
:params="{ url: item.ctrlDataUrl, ctrlPermi: ctrlPermi, parentAttr: 'disableCheckbox' }"
:canSelectParent="!isLoadUser.includes(item.ctrlType)"
:immediate="immediate"
:defaultExpandLevel="Number(item.expandLevel)"
:ref="setTreeRefs(item.ctrlType)"
@tree-data-change="handleTreeDataChange"
/>
</div>
</template>
</div>
</template>
<script lang="ts" setup name="ViewsSysRoleAuthDataScope">
import { ref, nextTick, type PropType } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useLocaleStore } from '@jeesite/core/store/modules/locale';
import { BasicTree, TreeActionType } from '@jeesite/core/components/Tree';
import { propTypes } from '@jeesite/core/utils/propTypes';
const props = defineProps({
namespace: propTypes.string,
checkStrictly: propTypes.bool,
api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> },
ctrlPermis: propTypes.array.def(['0', '1']),
});
const { t } = useI18n(props.namespace);
const localeStore = useLocaleStore();
const dataScopes = ref<Array<Recordable>>([]);
const dataScopeList = ref<Array<Recordable>>([]);
const moduleCodes = ref<Array<string>>([]);
const ctrlPermi = ref<string>('');
const menuCode = ref<string>('');
const immediate = ref(false);
const isLoadUser = ref<Array<string>>([]);
const treeRefs: Recordable<TreeActionType> = {};
const setTreeRefs = (key: string) => (el: any) => {
if (el) treeRefs[key] = el;
};
let loadTreeDataNum: number;
async function loadDataScopeList(data: Recordable) {
dataScopes.value = data.dataScopes || [];
dataScopeList.value = data.dataScopeList || [];
moduleCodes.value = data.moduleCodes || [];
ctrlPermi.value = data.ctrlPermi || '1';
menuCode.value = data.menuCode || '0';
loadTreeDataNum = 0;
await nextTick(() => {
if (immediate.value) {
for (const key of Object.keys(treeRefs)) {
treeRefs[key].setCheckedKeys([]);
treeRefs[key].reload();
}
} else {
immediate.value = true;
}
});
isLoadUser.value = dataScopes.value
.filter((item) => String(item.ctrlDataUrl).includes('isLoadUser=true'))
.map((item) => item.ctrlType);
}
function handleTreeDataChange() {
const keys = Object.keys(treeRefs);
loadTreeDataNum = loadTreeDataNum + 1;
if (loadTreeDataNum == keys.length) {
let checkedKeys = {};
dataScopeList.value.forEach((item) => {
if (!checkedKeys[item.ctrlType]) {
checkedKeys[item.ctrlType] = [];
}
checkedKeys[item.ctrlType].push((isLoadUser.value.includes(item.ctrlType) ? 'u_' : '') + item.ctrlData);
});
for (const key of keys) {
treeRefs[key].setCheckedKeys(checkedKeys[key] || []);
}
}
}
function getDataScopeList() {
const keys = Object.keys(treeRefs);
let dataScopeData: Array<any> = [];
for (const key of keys) {
const ks = treeRefs[key].getCheckedKeys();
for (const k of ks as Array<any>) {
dataScopeData.push({
ctrlType: String(key),
ctrlData: String(k).replace(/^u_/g, ''),
menuCode: menuCode.value,
});
}
}
return dataScopeData;
}
function getDataScopeListJson() {
return JSON.stringify(getDataScopeList());
}
defineExpose({
loadDataScopeList,
getDataScopeList,
getDataScopeListJson,
});
</script>

View File

@@ -0,0 +1,314 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<ARow class="h-full pl-4">
<ACol :span="9" :style="getTreeStyle">
<BasicTree
ref="treeRef"
:search="true"
:toolbar="true"
:showIcon="true"
:api="menuTreeDataByRoleCode"
:params="apiParams"
:immediate="false"
:defaultExpandLevel="1"
@select="handleSelect"
>
<template #headerTitle>
<Dropdown class="cursor-pointer" :trigger="['hover']" :dropMenuList="dropMenuList">
{{ sysName }} <DownOutlined />
</Dropdown>
</template>
<template #icon="{ dataRef, isLeaf, expanded }">
<Icon v-if="dataRef.hasDataScope" icon="i-ant-design:unordered-list-outlined" color="#f00" />
<Icon v-else-if="isLeaf" icon="ant-design:file-outlined" />
<Icon v-else-if="expanded" icon="i-ant-design:folder-open-outlined" />
<Icon v-else icon="ant-design:folder-outlined" />
</template>
</BasicTree>
</ACol>
<ACol :span="15" :style="getMainStyle">
<Tabs class="jeesite-role-auth-data-scope-tabs" v-model:activeKey="ruleType" @change="handleTabChange">
<Tabs.TabPane key="1" :forceRender="true">
<template #tab>
<Icon v-if="ruleType == '1'" icon="i-ant-design:check-circle-outlined" />
<Icon v-else icon="i-ant-design:minus-circle-outlined" />
<span class="pr-1"> {{ t('通用数据权限') }} </span>
</template>
<RoleDataScope ref="roleDataScopeRef" :menuDataScope="true" />
</Tabs.TabPane>
<Tabs.TabPane key="2" :forceRender="true">
<template #tab>
<Icon v-if="ruleType == '2'" icon="i-ant-design:check-circle-outlined" />
<Icon v-else icon="i-ant-design:minus-circle-outlined" />
<span class="pr-1"> {{ t('自定义条件规则') }} </span>
</template>
<RuleDataScope ref="ruleDataScopeRef" />
</Tabs.TabPane>
<Tabs.TabPane key="3" :forceRender="true">
<template #tab>
<Icon v-if="ruleType == '3'" icon="i-ant-design:check-circle-outlined" />
<Icon v-else icon="i-ant-design:minus-circle-outlined" />
<span class="pr-1"> {{ t('自定义SQL片段') }} </span>
</template>
<SqlDataScope ref="sqlDataScopeRef" />
</Tabs.TabPane>
</Tabs>
<div class="ml-4 mt-2">
<Alert
v-if="!ruleType || ruleType === '0' || menuCode == '0'"
message="提示:请先选择菜单,然后再选择权限类型。"
type="info"
/>
</div>
</ACol>
</ARow>
</template>
<script lang="ts" setup name="ViewsSysMenuIndex">
import { ref, computed, CSSProperties } from 'vue';
import { Alert, Col, Row, Tabs } from 'ant-design-vue';
import { DownOutlined } from '@ant-design/icons-vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useDict } from '@jeesite/core/components/Dict';
import { Icon } from '@jeesite/core/components/Icon';
import { Dropdown, DropMenu } from '@jeesite/core/components/Dropdown';
import { BasicTree, TreeActionType } from '@jeesite/core/components/Tree';
import { menuTreeDataByRoleCode } from '@jeesite/core/api/sys/role';
import { useWindowSizeFn } from '@jeesite/core/hooks/event/useWindowSizeFn';
import { onMountedOrActivated } from '@jeesite/core/hooks/core/onMountedOrActivated';
import { encryptByBase64 } from '@jeesite/core/utils/cipher';
import RoleDataScope from './RoleDataScope.vue';
import RuleDataScope from './RuleDataScope.vue';
import SqlDataScope from './SqlDataScope.vue';
const ARow = Row;
const ACol = Col;
const { t } = useI18n('sys.role');
const record = ref<Recordable>({});
const menuDataScopes = ref<any>({});
const treeRef = ref<Nullable<TreeActionType>>(null);
const apiParams = ref<Recordable>({ roleCode: '', sysCode: 'default' });
const dropMenuList = ref<Array<DropMenu>>([]);
const sysCode = ref<string>('default');
const sysName = ref<string>(t('菜单'));
const menuCode = ref<string>('0');
const ruleType = ref<string>('0');
const roleDataScopeRef = ref<InstanceType<typeof RoleDataScope>>();
const ruleDataScopeRef = ref<InstanceType<typeof RuleDataScope>>();
const sqlDataScopeRef = ref<InstanceType<typeof SqlDataScope>>();
const contentHeight = ref(400);
const getTreeStyle = computed((): CSSProperties => {
const treeHeight = contentHeight.value - 220;
return {
height: `${treeHeight}px`,
minHeight: `${treeHeight}px`,
};
});
const getMainStyle = computed((): CSSProperties => {
const mainHeight = contentHeight.value - 220 + 30;
return {
height: `${mainHeight}px`,
minHeight: `${mainHeight}px`,
overflowX: 'auto',
};
});
function calcTreeHeight() {
contentHeight.value = document.documentElement.clientHeight;
}
useWindowSizeFn(calcTreeHeight, 280);
onMountedOrActivated(calcTreeHeight);
async function loadDataScopeFormData(res: Recordable) {
// 初始化变量值
const { roleCode, roleName } = (res.role || {}) as Recordable;
record.value = { roleCode, roleName, dataScope: '0' };
sysCode.value = 'default';
menuCode.value = '0';
ruleType.value = '0';
// 加载菜单树
apiParams.value = { roleCode: record.value.roleCode, sysCode: sysCode.value };
dropMenuList.value = (await useDict().initGetDictList('sys_menu_sys_code')).map((item) => {
if (item.value == sysCode.value) {
sysName.value = item.name;
}
return {
text: item.name,
event: item.value,
icon: 'i-radix-icons:dot',
onClick: () => {
sysCode.value = item.value;
sysName.value = item.name;
apiParams.value.sysCode = item.value;
treeRef.value?.reload();
},
};
});
treeRef.value?.reload();
treeRef.value?.setSelectedKeys([]);
// 读取存储的数据
menuDataScopes.value = {};
for (const menuDataScope of res.menuDataScopeList) {
menuDataScopes.value[menuDataScope.menuCode] = {
menuCode: menuDataScope.menuCode,
roleCode: menuDataScope.roleCode,
ruleName: menuDataScope.ruleName,
ruleType: menuDataScope.ruleType,
...menuDataScope.ruleConfigMap,
roleDataScopeList: [],
};
}
for (const roleDataScope of res.roleDataScopeList) {
if (roleDataScope.menuCode && roleDataScope.menuCode !== '0') {
const mds = menuDataScopes.value[roleDataScope.menuCode] || {};
if (mds.roleDataScopeList) {
mds.roleDataScopeList.push(roleDataScope);
}
}
}
// 加载数据权限表单
roleDataScopeRef.value?.loadDataScopeFormData(record.value, res);
ruleDataScopeRef.value?.loadDataScopeFormData(record.value, res);
sqlDataScopeRef.value?.loadDataScopeFormData(record.value, res);
}
async function handleSelect(_keys?: string[], obj?: any) {
// 存储上一个菜单数据权限
if (ruleType.value === '1') {
const formData = await roleDataScopeRef.value?.getDataScopeFormData();
if (formData.menuCode) {
formData.ruleType = ruleType.value;
menuDataScopes.value[formData.menuCode] = formData;
treeRef.value?.updateNodeByKey(formData.menuCode, {
hasDataScope: formData.dataScope && formData.dataScope !== '0',
});
}
} else if (ruleType.value === '2') {
const formData = await ruleDataScopeRef.value?.getDataScopeFormData();
if (formData.menuCode) {
formData.ruleType = ruleType.value;
menuDataScopes.value[formData.menuCode] = formData;
treeRef.value?.updateNodeByKey(formData.menuCode, {
hasDataScope: formData.ruleList && formData.ruleList.length > 0,
});
}
} else if (ruleType.value === '3') {
const formData = await sqlDataScopeRef.value?.getDataScopeFormData();
if (formData.menuCode) {
formData.ruleType = ruleType.value;
menuDataScopes.value[formData.menuCode] = formData;
treeRef.value?.updateNodeByKey(formData.menuCode, {
hasDataScope: formData.sqlWhere && formData.sqlWhere !== '',
});
}
}
// 加载当前选择的菜单数据权限
if (obj && obj.node && obj.node.dataRef) {
const { id, rawName, permission } = obj.node.dataRef;
menuCode.value = id;
menuDataScopes.value[id] = {
...(menuDataScopes.value[id] || record.value),
menuCode: id,
menuName: rawName,
permission: permission,
};
const menuDataScope = menuDataScopes.value[id];
ruleType.value = menuDataScope.ruleType || '1';
roleDataScopeRef.value?.loadDataScopeFormData(menuDataScope);
ruleDataScopeRef.value?.loadDataScopeFormData(menuDataScope);
sqlDataScopeRef.value?.loadDataScopeFormData(menuDataScope);
}
}
function handleTabChange() {
if (menuCode.value == '0') {
ruleType.value = '0';
}
// 更新菜单图标状态
if (ruleType.value === '1') {
const formData = menuDataScopes.value[menuCode.value] as Recordable;
if (formData && formData.menuCode) {
treeRef.value?.updateNodeByKey(formData.menuCode, {
hasDataScope: formData.dataScope && formData.dataScope !== '0',
});
}
} else if (ruleType.value === '2') {
const formData = menuDataScopes.value[menuCode.value] as Recordable;
if (formData && formData.menuCode) {
treeRef.value?.updateNodeByKey(formData.menuCode, {
hasDataScope: formData.ruleList && formData.ruleList.length > 0,
});
}
} else if (ruleType.value === '3') {
const formData = menuDataScopes.value[menuCode.value] as Recordable;
if (formData && formData.menuCode) {
treeRef.value?.updateNodeByKey(formData.menuCode, {
hasDataScope: formData.sqlWhere && formData.sqlWhere !== '',
});
}
}
}
async function getDataScopeFormData() {
await handleSelect(); // 获取最后一个设置
let menuDataScopeList: any = [];
let roleDataScopeList: any = [];
for (const key in menuDataScopes.value) {
const menuDataScope = menuDataScopes.value[key];
// 1 角色数据范围 2自定义条件规则 3自定义SQL
if (menuDataScope.ruleType === '1') {
if (menuDataScope.dataScope && menuDataScope.dataScope !== '0') {
menuDataScopeList.push({
menuCode: menuDataScope.menuCode,
ruleName: record.value.roleName,
ruleType: menuDataScope.ruleType,
ruleConfigMap: {
dataScope: menuDataScope.dataScope,
},
});
if (menuDataScope.dataScope === '2') {
roleDataScopeList = roleDataScopeList.concat(menuDataScope.roleDataScopeList);
}
}
} else if (menuDataScope.ruleType === '2') {
if (menuDataScope.ruleList && menuDataScope.ruleList.length > 0) {
menuDataScopeList.push({
menuCode: menuDataScope.menuCode,
ruleName: record.value.roleName,
ruleType: menuDataScope.ruleType,
ruleConfigMap: {
ruleList: menuDataScope.ruleList,
},
});
}
} else if (menuDataScope.ruleType === '3') {
if (menuDataScope.sqlWhere && menuDataScope.sqlWhere !== '') {
menuDataScopeList.push({
menuCode: menuDataScope.menuCode,
ruleName: record.value.roleName,
ruleType: menuDataScope.ruleType,
ruleConfigMap: {
sqlWhere: encryptByBase64(menuDataScope.sqlWhere),
},
});
}
}
}
return {
menuDataScopeList,
roleDataScopeList,
} as any;
}
defineExpose({
loadDataScopeFormData,
getDataScopeFormData,
});
</script>

View File

@@ -0,0 +1,152 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div class="pt-2">
<BasicForm @register="registerForm">
<template #dataScopeTrees>
<CustomDataScope ref="customDataScopeRef" :api="roleCtrlDataTreeData" />
</template>
</BasicForm>
</div>
</template>
<script lang="ts" setup name="ViewsSysRoleAuthDataScope">
import { ref } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
// import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { roleCtrlDataTreeData } from '@jeesite/core/api/sys/role';
import { propTypes } from '@jeesite/core/utils/propTypes';
import CustomDataScope from './CustomDataScope.vue';
const props = defineProps({
menuDataScope: propTypes.bool,
});
const { t } = useI18n('sys.role');
// const { showMessage } = useMessage();
const record = ref<Recordable>({});
const customDataScope = ref<Recordable>({});
const customDataScopeRef = ref<InstanceType<typeof CustomDataScope>>();
const inputFormSchemas: FormSchema[] = [
{
label: t('菜单名称'),
field: 'menuName',
component: 'Input',
componentProps: {
disabled: true,
},
ifShow: () => !!props.menuDataScope,
},
{
label: t('权限标识'),
field: 'permission',
component: 'Input',
componentProps: {
disabled: true,
},
ifShow: () => !!props.menuDataScope,
},
{
label: t('角色名称'),
field: 'roleName',
component: 'Input',
componentProps: {
disabled: true,
},
ifShow: () => !props.menuDataScope,
},
{
label: t('角色编码'),
field: 'roleCode',
component: 'Input',
componentProps: {
disabled: true,
},
ifShow: () => !props.menuDataScope,
},
{
label: t('数据范围'),
field: 'dataScope',
helpMessage: t('指定数据权限范围类型,多个角色同时指定,之间为或者关系'),
component: 'RadioGroup',
componentProps: {
dictType: 'sys_role_data_scope',
allowClear: true,
},
colProps: { md: 24, lg: 24 },
},
{
label: t('业务范围'),
field: 'bizScope',
helpMessage: t(
'在 addFilter 权限过滤的时候指定适应的业务范围,不指定代表所有生效,如:有的功能看本部门,有的功能看本公司;新的业务范围从字典 sys_role_biz_scope 类型添加。',
),
component: 'Select',
componentProps: {
dictType: 'sys_role_biz_scope',
allowClear: true,
mode: 'multiple',
},
colProps: { md: 24, lg: 24 },
ifShow: () => !props.menuDataScope,
},
{
label: t('授权数据权限'),
field: 'authDataScopeInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
show: ({ values }) => values.dataScope === '2',
},
{
field: 'roleDataScopeList',
component: 'Input',
colProps: { md: 24, lg: 24 },
slot: 'dataScopeTrees',
show: ({ values }) => values.dataScope === '2',
},
];
const [registerForm, { resetFields, setFieldsValue, getFieldsValue, validate }] = useForm({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
});
async function loadDataScopeFormData(role: Recordable, res?: Recordable) {
await resetFields();
record.value = role;
await setFieldsValue(record.value);
if (res) {
customDataScope.value = {
dataScopes: res.dataScopes || [],
moduleCodes: res.moduleCodes || [],
dataScopeList: props.menuDataScope
? []
: (res.roleDataScopeList || []).filter((item: Recordable) => item.menuCode === '0'),
ctrlPermi: res.ctrlPermi || '2',
};
} else {
customDataScope.value.menuCode = role.menuCode || '0';
customDataScope.value.dataScopeList = role.roleDataScopeList || [];
}
await customDataScopeRef.value?.loadDataScopeList(customDataScope.value);
}
async function getDataScopeFormData() {
return {
...(await validate()),
menuCode: record.value.menuCode,
roleDataScopeList: customDataScopeRef.value?.getDataScopeList(),
};
}
defineExpose({
loadDataScopeFormData,
getDataScopeFormData,
});
</script>

View File

@@ -0,0 +1,263 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div class="pt-2">
<BasicForm @register="registerForm">
<template #dataScopeTable>
<BasicTable @register="registerTable" @row-click="handleRowClick" />
<a-button class="mt-2" @click="handleRowAdd" v-auth="'sys:role:edit'">
<Icon icon="i-ant-design:plus-circle-outlined" /> {{ t('新增') }}
</a-button>
</template>
</BasicForm>
<div class="ml-5 mt-2">
<Alert
message="匹配值支持如下动态表达式:
1当前用户对象中的属性值#{currentUser.属性名}
2会话对象中的属性值#{session.属性名}
3当前查询实体中的属性值#{属性名}
4当前实体查询不到则从用户缓存里查询
仍然查询不到,则从系统参数配置里获取,
再查询不到,则原样返回。"
type="info"
/>
</div>
</div>
</template>
<script lang="ts" setup name="ViewsSysRoleAuthDataScope">
import { ref } from 'vue';
import { pick } from 'lodash-es';
import { Alert } from 'ant-design-vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
// import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { BasicTable, useTable } from '@jeesite/core/components/Table';
const { t } = useI18n('sys.role');
// const { showMessage } = useMessage();
const record = ref<Recordable>({});
const inputFormSchemas: FormSchema[] = [
{
label: t('菜单名称'),
field: 'menuName',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: t('权限标识'),
field: 'permission',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: t('规则列表'),
field: 'authDataScopeRule',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
field: 'ruleList',
component: 'Input',
colProps: { md: 24, lg: 24 },
slot: 'dataScopeTable',
},
];
const [registerForm, { resetFields, setFieldsValue, getFieldsValue, validate }] = useForm({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
});
const [registerTable, tableAction] = useTable({
columns: [
{
title: t('关系'),
dataIndex: 'andOr',
width: 60,
align: 'left',
editRow: true,
className: 'jeesite-table-tree-name',
editComponent: 'Select',
editComponentProps: {
options: [
{ label: t('并且'), value: 'AND' },
{ label: t('或者'), value: 'OR' },
],
},
format: (text, _record, _index, column) => {
return column?.editComponentProps.options.find((item) => item.value === text).label;
},
},
{
title: t('字段名'),
dataIndex: 'columnName',
width: 60,
align: 'left',
editRow: true,
editComponent: 'Input',
editComponentProps: {},
},
{
title: t('条件'),
dataIndex: 'queryType',
width: 50,
align: 'left',
editRow: true,
editComponent: 'Select',
editComponentProps: {
options: [
{ label: t('等于'), value: 'EQ' },
{ label: t('不等于'), value: 'NE' },
{ label: t('大于'), value: 'GT' },
{ label: t('大于等于'), value: 'GTE' },
{ label: t('小于'), value: 'LT' },
{ label: t('小于等于'), value: 'LTE' },
{ label: t('等多值'), value: 'IN' },
{ label: t('不等多值'), value: 'NOT_IN' },
{ label: t('包含'), value: 'LIKE' },
{ label: t('不包含'), value: 'NOT_LIKE' },
{ label: t('开始以'), value: 'RIGHT_LIKE' },
{ label: t('不开始以'), value: 'RIGHT_NOT_LIKE' },
{ label: t('结束以'), value: 'LEFT_LIKE' },
{ label: t('不结束以'), value: 'LEFT_NOT_LIKE' },
{ label: t('是空'), value: 'IS_NULL' },
{ label: t('不是空'), value: 'IS_NOT_NULL' },
],
},
format: (text, _record, _index, column) => {
return column?.editComponentProps.options.find((item) => item.value === text).label;
},
},
{
title: t('值类型'),
dataIndex: 'columnType',
width: 50,
align: 'left',
editRow: true,
editComponent: 'Select',
editComponentProps: {
options: [
{ label: t('字符串'), value: 'String' },
{ label: t('整型'), value: 'Long' },
{ label: t('长整型'), value: 'Integer' },
{ label: t('浮点型'), value: 'Float' },
{ label: t('双精度'), value: 'Double' },
{ label: t('大数值'), value: 'BigDecimal' },
{ label: t('布尔型'), value: 'Boolean' },
{ label: t('日期'), value: 'Date' },
],
},
format: (text, _record, _index, column) => {
return column?.editComponentProps.options.find((item) => item.value === text).label;
},
},
{
title: t('匹配值'),
dataIndex: 'value',
width: 80,
align: 'left',
editRow: true,
editComponent: 'Input',
editComponentProps: {},
},
],
actionColumn: {
width: 50,
actions: (record: Recordable) => [
{
icon: 'i-fluent:add-circle-24-regular',
title: t('新增下级规则'),
onClick: handleRowAdd.bind(this, record),
auth: 'sys:role:edit',
},
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
popConfirm: {
title: t('是否确认删除'),
confirm: handleRowDelete.bind(this, record),
},
auth: 'sys:role:edit',
},
],
},
rowKey: 'id',
pagination: false,
bordered: true,
size: 'small',
inset: true,
isTreeTable: true,
childrenColumnName: 'children',
});
function handleRowClick(data: Recordable) {
data.onEdit?.(true, false);
}
function handleRowAdd(parent?: Recordable) {
const record = {
id: new Date().getTime(),
editable: true,
andOr: 'AND',
queryType: 'EQ',
columnType: 'String',
children: [],
};
if (parent && parent.children) {
parent.children.push(record);
tableAction.expandRows([parent.id]);
} else {
tableAction.insertTableDataRecord(record);
}
}
function handleRowDelete(data: Recordable) {
tableAction.deleteTableDataRecord(data);
}
async function getTableData(children?: Recordable[]): Promise<Recordable> {
let tableList: Recordable[] = [];
for (const record of children || tableAction.getDataSource()) {
await record.onEdit?.(false, true);
const newRecord = pick(record, 'id', 'andOr', 'columnName', 'queryType', 'columnType', 'value') as Recordable;
if (record.children && record.children.length > 0) {
newRecord.children = await getTableData(record.children);
} else {
newRecord.children = [];
}
tableList.push(newRecord);
}
return tableList;
}
async function loadDataScopeFormData(data: Recordable, res?: Recordable) {
await resetFields();
record.value = data;
await setFieldsValue(record.value);
tableAction.setTableData(data.ruleList || []);
tableAction.expandAll();
}
async function getDataScopeFormData() {
return {
...(await validate()),
menuCode: record.value.menuCode,
ruleList: await getTableData(),
};
}
defineExpose({
loadDataScopeFormData,
getDataScopeFormData,
});
</script>

View File

@@ -0,0 +1,86 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div class="pt-2">
<BasicForm @register="registerForm" />
<div class="ml-5 mt-2">
<Alert message="提示:在后端配置文件开启 user.dataScopeRuleSql 参数后,方可使用该功能。" type="info" />
</div>
</div>
</template>
<script lang="ts" setup name="ViewsSysRoleAuthDataScope">
import { ref } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { Alert } from 'ant-design-vue';
// import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { trim } from 'lodash-es';
const { t } = useI18n('sys.role');
// const { showMessage } = useMessage();
const record = ref<Recordable>({});
const inputFormSchemas: FormSchema[] = [
{
label: t('菜单名称'),
field: 'menuName',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: t('权限标识'),
field: 'permission',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: t('SQL片段'),
field: 'authDataScopeSql',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
field: 'sqlWhere',
component: 'InputTextArea',
componentProps: {
rows: 8,
},
colProps: { md: 24, lg: 24 },
},
];
const [registerForm, { resetFields, setFieldsValue, getFieldsValue, validate }] = useForm({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
});
async function loadDataScopeFormData(data: Recordable, res?: Recordable) {
await resetFields();
record.value = data;
await setFieldsValue(record.value);
}
async function getDataScopeFormData() {
const data = {
...(await validate()),
menuCode: record.value.menuCode,
};
if (data.sqlWhere) {
data.sqlWhere = trim(data.sqlWhere);
}
return data;
}
defineExpose({
loadDataScopeFormData,
getDataScopeFormData,
});
</script>

View File

@@ -0,0 +1,339 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<BasicDrawer
v-bind="$attrs"
:showFooter="true"
:okAuth="'sys:role:edit'"
@register="registerDrawer"
@ok="handleSubmit"
width="80%"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<BasicForm @register="registerForm">
<template #menuTrees>
<div class="flex flex-row flex-wrap">
<template v-for="item in sysCodeRef" :key="item.id">
<div class="mb-5 mr-5" v-show="sysCodesRef.length == 0 || sysCodesRef.includes(item.value)">
<BasicTree
class="bg-gray"
style="width: 500px"
:title="item.name"
:toolbar="true"
:checkable="true"
:checkStrictly="false"
:immediate="false"
:defaultExpandLevel="2"
:ref="setTreeRefs(item.value)"
/>
</div>
</template>
</div>
</template>
</BasicForm>
<FormExtend v-show="op === 'add' || op === 'edit'" ref="formExtendRef" />
</BasicDrawer>
</template>
<script lang="ts" setup name="ViewsSysRoleForm">
import { ref, unref, computed } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { useDict } from '@jeesite/core/components/Dict';
import { BasicTree, TreeActionType } from '@jeesite/core/components/Tree';
import { BasicForm, FormExtend, FormSchema, useForm } from '@jeesite/core/components/Form';
import { BasicDrawer, useDrawerInner } from '@jeesite/core/components/Drawer';
import { Role, roleSave, checkRoleName, roleForm, roleMenuTreeData } from '@jeesite/core/api/sys/role';
const emit = defineEmits(['success', 'register']);
const { t } = useI18n('sys.role');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const record = ref<Role>({} as Role);
const getTitle = computed(() => ({
icon: meta.icon || 'ant-design:book-outlined',
value: record.value.isNewRecord ? t('新增角色') : op.value === 'auth' ? t('授权菜单') : t('编辑角色'),
}));
const op = ref<string>('');
const sysCodeRef = ref<Array<Recordable>>([]);
const sysCodesRef = ref<Array<string>>([]);
const formExtendRef = ref<InstanceType<typeof FormExtend>>();
const inputFormSchemas: FormSchema[] = [
{
label: t('基本信息'),
field: 'basicInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
label: t('角色名称'),
field: 'roleName',
component: 'Input',
componentProps: {
maxlength: 100,
},
rules: [
{ required: true },
{ pattern: /^[\u0391-\uFFE5\w]+$/, message: t('不能输入特殊字符') },
{
validator(_rule, value) {
return new Promise((resolve, reject) => {
if (!value || value === '') return resolve();
checkRoleName(record.value.roleName || '', value)
.then((res) => (res ? resolve() : reject(t('角色名称已存在'))))
.catch((err) => reject(err.message || t('验证失败')));
});
},
trigger: 'blur',
},
],
},
{
label: t('角色编码'),
field: 'roleCode',
component: 'Input',
componentProps: {
maxlength: 500,
},
required: true,
ifShow: () => !record.value.isNewRecord,
},
{
label: t('角色代码'),
field: 'viewCode',
component: 'Input',
componentProps: {
maxlength: 500,
},
required: true,
ifShow: () => record.value.isNewRecord,
},
{
label: t('排序号'),
field: 'roleSort',
helpMessage: '升序',
component: 'InputNumber',
defaultValue: '30',
componentProps: {
maxlength: 10,
},
required: true,
ifShow: () => op.value === 'add' || op.value === 'edit',
},
{
label: t('用户类型'),
field: 'userType',
component: 'Select',
componentProps: {
dictType: 'sys_user_type',
allowClear: true,
},
ifShow: () => op.value === 'add' || op.value === 'edit',
},
{
label: t('角色分类'),
field: 'roleType',
component: 'Select',
componentProps: {
dictType: 'sys_role_type',
allowClear: true,
},
ifShow: () => op.value === 'add' || op.value === 'edit',
},
{
label: t('系统角色'),
field: 'isSys',
component: 'RadioGroup',
defaultValue: '1',
componentProps: {
dictType: 'sys_yes_no',
allowClear: true,
},
ifShow: () => op.value === 'add' || op.value === 'edit',
},
{
label: t('其它信息'),
field: 'otherInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
ifShow: () => op.value === 'add' || op.value === 'edit',
},
{
label: t('桌面地址'),
field: 'desktopUrl',
helpMessage: '仪表盘地址,如果当前多个角色,则根据角色的排序优先级选择。',
component: 'Input',
componentProps: {
maxlength: 250,
},
ifShow: () => op.value === 'add' || op.value === 'edit',
},
{
label: t('是否可见'),
field: 'isShow',
helpMessage: '切换身份列表中是否显示该角色',
component: 'RadioGroup',
componentProps: {
dictType: 'sys_show_hide',
allowClear: true,
},
ifShow: () => op.value === 'add' || op.value === 'edit',
},
{
label: t('包含系统'),
field: 'sysCodes',
helpMessage: '展示子系统列表的时候会根据此条件进行过滤,否则展示全部子系统',
component: 'Select',
componentProps: {
dictType: 'sys_menu_sys_code',
allowClear: true,
mode: 'multiple',
onChange: (val) => {
sysCodesRef.value = val ? val.split(',') : [];
},
},
colProps: { md: 24, lg: 24 },
ifShow: () => op.value === 'add' || op.value === 'auth',
},
{
label: t('备注信息'),
field: 'remarks',
component: 'InputTextArea',
componentProps: {
maxlength: 500,
},
colProps: { md: 24, lg: 24 },
ifShow: () => op.value === 'add' || op.value === 'edit',
},
{
label: t('授权功能菜单'),
field: 'authMenuInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
ifShow: () => op.value === 'add' || op.value === 'auth',
},
{
field: 'roleMenuListJson',
component: 'Input',
colProps: { md: 24, lg: 24 },
slot: 'menuTrees',
ifShow: () => op.value === 'add' || op.value === 'auth',
},
];
const treeRefs: Recordable<TreeActionType> = {};
const setTreeRefs = (key: string) => (el: any) => {
if (el) treeRefs[key] = el;
};
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
});
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
setDrawerProps({ loading: true });
await resetFields();
await formExtendRef.value?.resetFields();
op.value = data.op || 'add';
const res = await roleForm(data);
record.value = (res.role || {}) as Role;
await setFieldsValue(record.value);
await updateSchema([
{
field: 'roleCode',
componentProps: {
disabled: !record.value.isNewRecord,
},
},
{
field: 'roleName',
componentProps: {
disabled: op.value === 'auth',
},
},
]);
if (op.value === 'add' || op.value === 'auth') {
await loadSysCode();
await loadTreeDatas();
}
await formExtendRef.value?.setFieldsValue(record.value.extend);
setDrawerProps({ loading: false });
});
async function loadSysCode() {
sysCodeRef.value = await useDict().initGetDictList('sys_menu_sys_code');
sysCodesRef.value = record.value.sysCodes ? record.value.sysCodes?.split(',') : [];
}
async function loadTreeDatas() {
const res = await roleMenuTreeData({ roleCode: record.value.roleCode });
const checkedKeys = {};
(res.roleMenuList || []).forEach((item) => {
if (!checkedKeys[item.sysCode]) {
checkedKeys[item.sysCode] = [];
}
checkedKeys[item.sysCode].push(item.id);
});
for (const key in res.menuMap) {
treeRefs[key].setTreeData(res.menuMap[key]);
treeRefs[key].setCheckedKeys(checkedKeys[key]);
}
}
function getRoleMenuListJson() {
const keys = Object.keys(treeRefs);
let menuData: Array<any> = [];
for (const key of keys) {
if (sysCodesRef.value.length == 0 || sysCodesRef.value.includes(key)) {
const ks = treeRefs[key].getCheckedKeys();
for (const k of ks as Array<any>) {
menuData.push(k);
}
}
}
return JSON.stringify(menuData);
}
async function handleSubmit() {
try {
const data = await validate();
setDrawerProps({ confirmLoading: true });
const params: any = {
isNewRecord: record.value.isNewRecord,
roleCode: record.value.roleCode,
op: op.value,
};
if (!data.viewCode) {
data.viewCode = record.value.viewCode;
}
if (op.value === 'add' || op.value === 'auth') {
data.roleMenuListJson = getRoleMenuListJson();
}
data.extend = await formExtendRef.value?.validate();
// console.log('submit', params, data, record);
const res = await roleSave(params, data);
showMessage(res.message);
setTimeout(closeDrawer);
emit('success', data);
} catch (error: any) {
if (error && error.errorFields) {
showMessage(error.message || t('common.validateError'));
}
console.log('error', error);
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>

View File

@@ -0,0 +1,141 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<BasicDrawer
wrapClassName="jeesite-role-auth-data-scope"
v-bind="$attrs"
:showFooter="true"
:okAuth="'sys:role:edit'"
@register="registerDrawer"
@ok="handleSubmit"
width="90%"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<Tabs class="jeesite-role-auth-data-scope-tabs" v-model:activeKey="activeKey">
<Tabs.TabPane key="1" :forceRender="true">
<template #tab>
<Icon icon="i-ant-design:deployment-unit-outlined" />
<span class="pr-1"> {{ t('角色数据权限') }} </span>
</template>
<RoleDataScope ref="roleDataScopeRef" />
</Tabs.TabPane>
<Tabs.TabPane key="2" :forceRender="true">
<template #tab>
<Icon icon="i-ant-design:unordered-list-outlined" />
<span class="pr-1"> {{ t('菜单数据权限') }} </span>
</template>
<MenuDataScope ref="menuDataScopeRef" />
</Tabs.TabPane>
</Tabs>
</BasicDrawer>
</template>
<script lang="ts" setup name="ViewsSysRoleAuthDataScope">
import { computed, ref, unref } from 'vue';
import { Tabs } from 'ant-design-vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicDrawer, useDrawerInner } from '@jeesite/core/components/Drawer';
import { roleFormAuthDataScope, roleSaveAuthDataScope } from '@jeesite/core/api/sys/role';
import { useDict } from '@jeesite/core/components/Dict';
import RoleDataScope from './components/RoleDataScope.vue';
import MenuDataScope from './components/MenuDataScope.vue';
import { omit } from 'lodash-es';
const emit = defineEmits(['success', 'register']);
const { t } = useI18n('sys.role');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const record = ref<Recordable>({});
const getTitle = computed(() => {
const r = record.value;
const { getDictLabel } = useDict();
return {
icon: meta.icon || 'ant-design:book-outlined',
value:
t('角色分配数据权限') +
' (' +
r.roleName +
'-' +
r.viewCode +
'-' +
getDictLabel('sys_user_type', r.userType, t('未设置')) +
')',
};
});
const activeKey = ref<string>('1');
const roleDataScopeRef = ref<InstanceType<typeof RoleDataScope>>();
const menuDataScopeRef = ref<InstanceType<typeof MenuDataScope>>();
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
setDrawerProps({ loading: true });
activeKey.value = '1';
data.menuCode = 'true'; // 查询菜单数据权限
const res = await roleFormAuthDataScope(data);
record.value = (res.role || {}) as Recordable;
roleDataScopeRef.value?.loadDataScopeFormData(record.value, res);
menuDataScopeRef.value?.loadDataScopeFormData(res);
setDrawerProps({ loading: false });
});
async function handleSubmit() {
try {
setDrawerProps({ confirmLoading: true });
const roleDataScope = await roleDataScopeRef.value?.getDataScopeFormData();
const menuDataScope = await menuDataScopeRef.value?.getDataScopeFormData();
const params: any = {
...omit(roleDataScope, ['roleDataScopeList']),
roleDataScopeListJson: JSON.stringify(roleDataScope.roleDataScopeList.concat(menuDataScope.roleDataScopeList)),
menuDataScopeListJson: JSON.stringify(menuDataScope.menuDataScopeList),
isNewRecord: record.value.isNewRecord,
roleCode: record.value.roleCode,
menuCode: 'true', // 存储菜单数据权限
};
// console.log('submit', params, data, record);
const res = await roleSaveAuthDataScope(params);
showMessage(res.message);
setTimeout(closeDrawer);
emit('success', params);
} catch (error: any) {
if (error && error.errorFields) {
showMessage(error.message || t('common.validateError'));
}
console.log('error', error);
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>
<style lang="less">
.jeesite-role-auth-data-scope {
.scroll-container {
> .scrollbar__wrap {
> .scrollbar__view {
margin: 6px !important;
}
}
}
&-tabs.ant-tabs {
margin-top: 0 !important;
padding-right: 5px;
.ant-tabs-nav {
margin-left: 20px;
}
.ant-tabs-tab {
padding: 7px 0;
}
}
}
</style>

View File

@@ -0,0 +1,270 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<BasicDrawer
v-bind="$attrs"
:showFooter="true"
:showOkBtn="false"
@register="registerDrawer"
@close="handleClose"
width="80%"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<template #centerFooter>
<a-button type="primary" @click="handleForm" v-auth="'sys:role:edit'">
<Icon icon="i-fluent:add-12-filled" /> {{ t('添加用户') }}
</a-button>
<ListSelect
ref="listSelectRef"
selectType="empUserSelect"
:checkbox="true"
@select="handleUserSelect"
v-show="false"
/>
<Popconfirm :title="t('是否确认取消选中用户的角色')" @confirm="handleDelete">
<a-button type="error" v-auth="'sys:role:edit'">
<Icon icon="i-ant-design:close-outlined" /> {{ t('批量取消') }}
</a-button>
</Popconfirm>
</template>
<BasicTable @register="registerTable" />
</BasicDrawer>
</template>
<script lang="ts" setup name="ViewsSysRoleAuthDataScope">
import { ref, unref, computed } from 'vue';
import { Popconfirm } from 'ant-design-vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicDrawer, useDrawerInner } from '@jeesite/core/components/Drawer';
import { BasicTable, BasicColumn, useTable, TableRowSelection } from '@jeesite/core/components/Table';
import { FormProps } from '@jeesite/core/components/Form';
import { ListSelect } from '@jeesite/core/components/ListSelect';
import { formAuthUser, saveAuthUser, deleteAuthUser } from '@jeesite/core/api/sys/role';
import { userListData } from '@jeesite/core/api/sys/user';
import { useDict } from '@jeesite/core/components/Dict';
//const emit = defineEmits(['success', 'register']);
const { t } = useI18n('sys.role');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const record = ref<Recordable>({});
const getTitle = computed(() => {
const r = record.value;
const { getDictLabel } = useDict();
return {
icon: meta.icon || 'ant-design:book-outlined',
value:
t('角色分配用户') +
' (' +
r.roleName +
'-' +
r.viewCode +
'-' +
getDictLabel('sys_user_type', r.userType, t('未设置')) +
')',
};
});
const listSelectRef = ref<any>(null);
const searchForm: FormProps = {
baseColProps: { md: 8, lg: 6 },
labelWidth: 60,
schemas: [
{
label: t('账号'),
field: 'loginCode',
component: 'Input',
},
{
label: t('昵称'),
field: 'userName',
component: 'Input',
},
{
label: t('状态'),
field: 'status',
component: 'Select',
componentProps: {
dictType: 'sys_user_status',
allowClear: true,
onChange: handleSuccess,
},
},
{
label: t('姓名'),
field: 'refName',
component: 'Input',
},
{
label: t('手机'),
field: 'mobile',
component: 'Input',
},
{
label: t('邮箱'),
field: 'email',
component: 'Input',
},
{
label: t('电话'),
field: 'phone',
component: 'Input',
},
],
};
const tableColumns: BasicColumn[] = [
{
title: t('登录账号'),
dataIndex: 'loginCode',
key: 'a.login_code',
sorter: true,
width: 100,
slot: 'firstColumn',
},
{
title: t('用户昵称'),
dataIndex: 'userName',
key: 'a.user_name',
sorter: true,
width: 100,
},
{
title: t('电子邮箱'),
dataIndex: 'email',
key: 'a.email',
sorter: true,
width: 100,
},
{
title: t('手机号码'),
dataIndex: 'mobile',
key: 'a.mobile',
sorter: true,
width: 100,
},
{
title: t('办公电话'),
dataIndex: 'phone',
key: 'a.phone',
sorter: true,
width: 100,
},
{
title: t('更新时间'),
dataIndex: 'updateDate',
key: 'a.update_date',
sorter: true,
width: 100,
},
{
title: t('状态'),
dataIndex: 'status',
key: 'a.status',
sorter: true,
width: 80,
dictType: 'sys_status',
},
];
const actionColumn: BasicColumn = {
width: 60,
actions: (record: Recordable) => [
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
title: t('取消授权'),
popConfirm: {
title: t('是否取消该用户角色'),
confirm: handleDelete.bind(this, { userCode: record.userCode }),
},
auth: 'sys:secAdmin:edit',
},
],
};
const rowSelection: TableRowSelection = {
type: 'checkbox',
};
const [registerTable, { reload, getSelectRowKeys, setSelectedRowKeys }] = useTable({
api: userListData,
beforeFetch: (params) => {
params.roleCode = record.value.roleCode;
params.userType = record.value.userType;
return params;
},
immediate: false,
columns: tableColumns,
actionColumn: actionColumn,
rowSelection: rowSelection,
formConfig: searchForm,
showTableSetting: false,
useSearchForm: true,
canResize: true,
resizeHeightOffset: 60,
});
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
setDrawerProps({ loading: true });
const res = await formAuthUser(data);
record.value = (res.role || {}) as Recordable;
handleSuccess();
setDrawerProps({ loading: false });
});
function handleForm() {
listSelectRef.value.openSelectModal();
}
async function handleUserSelect(values: Recordable[]) {
if (values.length == 0) {
showMessage(t('请选中要授权角色的用户'));
return;
}
const params = {
roleCode: record.value.roleCode,
userRoleString: values.map((e) => e.userCode).join(','),
};
const res = await saveAuthUser(params);
showMessage(res.message);
handleSuccess();
listSelectRef.value.setSelectList([]);
}
async function handleDelete(event: any) {
let userRoleString: string;
if (event && event.userCode) {
userRoleString = event.userCode;
} else {
const selectedRowKeys = getSelectRowKeys();
if (selectedRowKeys.length == 0) {
showMessage(t('请选中要取消角色的用户'));
return;
}
userRoleString = selectedRowKeys.join(',');
}
const params = { roleCode: record.value.roleCode, userRoleString };
const res = await deleteAuthUser(params);
showMessage(res.message);
handleSuccess();
setSelectedRowKeys([]);
}
function handleSuccess() {
reload();
}
async function handleClose() {
closeDrawer();
}
</script>

View File

@@ -0,0 +1,318 @@
<!--
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div>
<BasicTable @register="registerTable">
<template #tableTitle>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<template #toolbar>
<a-button type="primary" @click="handleForm({ op: 'add' })" v-auth="'sys:role:edit'">
<Icon icon="i-fluent:add-12-filled" /> {{ t('新增') }}
</a-button>
</template>
<template #firstColumn="{ record }">
<a @click="handleForm({ roleCode: record.roleCode, op: 'edit' })">
{{ record.roleName }}
</a>
</template>
</BasicTable>
<InputForm @register="registerDrawer" @success="handleSuccess" />
<FormAuthDataScope @register="registerAuthDataSourceDrawer" @success="handleSuccess" />
<FormAuthUser @register="registerAuthUserDrawer" @success="handleSuccess" />
</div>
</template>
<script lang="ts" setup name="ViewsSysRoleList">
import { unref } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicTable, BasicColumn, useTable } from '@jeesite/core/components/Table';
import { roleDelete, roleListData } from '@jeesite/core/api/sys/role';
import { roleDisable, roleEnable } from '@jeesite/core/api/sys/role';
import { useDrawer } from '@jeesite/core/components/Drawer';
import { FormProps } from '@jeesite/core/components/Form';
import InputForm from './form.vue';
import FormAuthDataScope from './formAuthDataScope.vue';
import FormAuthUser from './formAuthUser.vue';
const { t } = useI18n('sys.role');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const getTitle = {
icon: meta.icon || 'ant-design:book-outlined',
value: meta.title || t('角色管理'),
};
const searchForm: FormProps = {
baseColProps: { md: 8, lg: 6 },
labelWidth: 90,
schemas: [
{
label: t('角色名称'),
field: 'roleName',
component: 'Input',
},
{
label: t('角色编码'),
field: 'roleCode_like',
component: 'Input',
},
{
label: t('状态'),
field: 'status',
component: 'Select',
componentProps: {
dictType: 'sys_search_status',
allowClear: true,
onChange: handleSuccess,
},
},
{
label: t('用户类型'),
field: 'userType',
component: 'Select',
componentProps: {
dictType: 'sys_user_type',
allowClear: true,
},
},
{
label: t('系统角色'),
field: 'isSys',
component: 'Select',
componentProps: {
dictType: 'sys_yes_no',
allowClear: true,
},
},
{
label: t('角色分类'),
field: 'roleType',
component: 'Select',
componentProps: {
dictType: 'sys_role_type',
allowClear: true,
},
},
],
};
const tableColumns: BasicColumn[] = [
{
title: t('角色名称'),
dataIndex: 'roleName',
key: 'a.role_name',
sorter: true,
width: 130,
align: 'center',
slot: 'firstColumn',
},
{
title: t('角色编码'),
dataIndex: 'roleCode',
key: 'a.role_code',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('排序号'),
dataIndex: 'roleSort',
key: 'a.role_sort',
sorter: true,
width: 90,
align: 'center',
},
{
title: t('用户类型'),
dataIndex: 'userType',
key: 'a.user_type',
sorter: true,
width: 90,
align: 'center',
dictType: 'sys_user_type',
},
{
title: t('系统角色'),
dataIndex: 'isSys',
key: 'a.is_sys',
sorter: true,
width: 90,
align: 'center',
dictType: 'sys_yes_no',
},
{
title: t('角色分类'),
dataIndex: 'roleType',
key: 'a.role_type',
sorter: true,
width: 90,
align: 'center',
dictType: 'sys_role_type',
},
{
title: t('数据范围'),
dataIndex: 'dataScope',
key: 'a.data_scope',
sorter: true,
width: 100,
align: 'center',
dictType: 'sys_role_data_scope',
},
{
title: t('业务范围'),
dataIndex: 'bizScope',
key: 'a.biz_scope',
sorter: true,
width: 100,
align: 'center',
dictType: 'sys_role_biz_scope',
},
{
title: t('状态'),
dataIndex: 'status',
key: 'a.status',
sorter: true,
width: 90,
align: 'left',
dictType: 'sys_status',
},
{
title: t('更新时间'),
dataIndex: 'updateDate',
key: 'a.update_date',
sorter: true,
width: 100,
align: 'center',
},
{
title: t('备注信息'),
dataIndex: 'remarks',
key: 'a.remarks',
sorter: true,
width: 130,
align: 'left',
},
];
const actionColumn: BasicColumn = {
width: 160,
actions: (record: Recordable) => [
{
icon: 'i-clarity:note-edit-line',
title: t('编辑角色'),
onClick: handleForm.bind(this, { roleCode: record.roleCode, op: 'edit' }),
auth: 'sys:role:edit',
},
{
icon: 'i-ant-design:stop-outlined',
color: 'error',
title: t('停用角色'),
popConfirm: {
title: t('是否确认停用角色'),
confirm: handleDisable.bind(this, { roleCode: record.roleCode }),
},
auth: 'sys:role:edit',
ifShow: () => record.status === '0',
},
{
icon: 'i-ant-design:check-circle-outlined',
color: 'success',
title: t('启用角色'),
popConfirm: {
title: t('是否确认启用角色'),
confirm: handleEnable.bind(this, { roleCode: record.roleCode }),
},
auth: 'sys:role:edit',
ifShow: () => record.status === '2',
},
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
title: t('删除角色'),
popConfirm: {
title: t('是否确认删除角色'),
confirm: handleDelete.bind(this, { roleCode: record.roleCode }),
},
auth: 'sys:role:edit',
},
],
dropDownActions: (record: Recordable) => [
{
icon: 'i-ant-design:check-square-outlined',
label: t('授权菜单'),
onClick: handleForm.bind(this, { roleCode: record.roleCode, op: 'auth' }),
auth: 'sys:role:edit',
},
{
icon: 'i-ant-design:check-circle-outlined',
label: t('数据权限'),
onClick: handleFormAuthDataScope.bind(this, { roleCode: record.roleCode }),
auth: 'sys:role:edit',
},
{
icon: 'i-ant-design:user-outlined',
label: t('分配用户'),
onClick: handleFormAuthUser.bind(this, { roleCode: record.roleCode }),
auth: 'sys:role:edit',
ifShow: () => record.userType === 'employee',
},
],
};
const [registerDrawer, { openDrawer }] = useDrawer();
const [registerAuthDataSourceDrawer, { openDrawer: openAuthDataScopeDrawer }] = useDrawer();
const [registerAuthUserDrawer, { openDrawer: openAuthUserDrawer }] = useDrawer();
const [registerTable, { reload }] = useTable({
api: roleListData,
beforeFetch: (params) => {
return params;
},
columns: tableColumns,
actionColumn: actionColumn,
formConfig: searchForm,
showTableSetting: true,
useSearchForm: true,
canResize: true,
});
function handleForm(record: Recordable) {
openDrawer(true, record);
}
async function handleDisable(record: Recordable) {
const res = await roleDisable(record);
showMessage(res.message);
handleSuccess();
}
async function handleEnable(record: Recordable) {
const res = await roleEnable(record);
showMessage(res.message);
handleSuccess();
}
async function handleDelete(record: Recordable) {
const res = await roleDelete(record);
showMessage(res.message);
handleSuccess();
}
function handleFormAuthDataScope(record: Recordable) {
openAuthDataScopeDrawer(true, record);
}
function handleFormAuthUser(record: Recordable) {
openAuthUserDrawer(true, record);
}
function handleSuccess() {
reload();
}
</script>