diff --git a/src/api/sys/SystemApi.ts b/src/api/sys/SystemApi.ts
new file mode 100644
index 000000000..58f674a4d
--- /dev/null
+++ b/src/api/sys/SystemApi.ts
@@ -0,0 +1,47 @@
+import { defHttp, ApiServiceEnum } from '@/utils/http/axios';
+
+enum Api {
+ listUser = 'sys/user/list',
+ listUserById = 'sys/user/listById',
+ listSystem = 'sys/system/list',
+ listSystemFilterByUser = 'sys/system/listAuthUser',
+}
+
+/**
+ * 查询用户列表
+ * @param params 参数
+ * @param useYn
+ */
+export const listUserApi = (params: Recordable = {}, useYn = true) => {
+ let parameter = params.parameter;
+ if (useYn) {
+ parameter = {
+ ...parameter,
+ 'useYn@=': true,
+ };
+ }
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.listUser,
+ data: {
+ ...params,
+ parameter,
+ },
+ });
+};
+
+export const listUserByIdApi = (ids: any[]) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.listUserById,
+ data: ids,
+ });
+};
+
+export const listSystemApi = (params, filterByUser = false) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: filterByUser ? Api.listSystemFilterByUser : Api.listSystem,
+ data: params,
+ });
+};
diff --git a/src/components/Form/index.ts b/src/components/Form/index.ts
index d85b3c517..5a1b98a0d 100644
--- a/src/components/Form/index.ts
+++ b/src/components/Form/index.ts
@@ -13,5 +13,7 @@ export { default as ApiTree } from './src/components/ApiTree.vue';
export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
export { default as ApiCascader } from './src/components/ApiCascader.vue';
export { default as ApiTransfer } from './src/components/ApiTransfer.vue';
+export { default as SmartTableSelect } from './src/smart-boot/components/base/SmartTableSelect';
+export { default as SmartUserSelectModal } from './src/smart-boot/components/SmartUserSelectModal.vue';
export { BasicForm };
diff --git a/src/components/Form/src/smart-boot/components/SmartApiSelectDict.vue b/src/components/Form/src/smart-boot/components/SmartApiSelectDict.vue
new file mode 100644
index 000000000..e62f4f68b
--- /dev/null
+++ b/src/components/Form/src/smart-boot/components/SmartApiSelectDict.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/src/components/Form/src/smart-boot/components/SmartApiSelectTable.vue b/src/components/Form/src/smart-boot/components/SmartApiSelectTable.vue
new file mode 100644
index 000000000..89adf77a3
--- /dev/null
+++ b/src/components/Form/src/smart-boot/components/SmartApiSelectTable.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/Form/src/smart-boot/components/SmartUserSelectModal.vue b/src/components/Form/src/smart-boot/components/SmartUserSelectModal.vue
new file mode 100644
index 000000000..fdfa59f7d
--- /dev/null
+++ b/src/components/Form/src/smart-boot/components/SmartUserSelectModal.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/Form/src/smart-boot/components/base/SmartTableSelect.less b/src/components/Form/src/smart-boot/components/base/SmartTableSelect.less
new file mode 100644
index 000000000..19b9bfc79
--- /dev/null
+++ b/src/components/Form/src/smart-boot/components/base/SmartTableSelect.less
@@ -0,0 +1,11 @@
+.smart-table-select {
+ @width: 80px;
+
+ .select {
+ width: calc(100% - @width - 8px);
+ }
+
+ .button {
+ width: @width;
+ }
+}
diff --git a/src/components/Form/src/smart-boot/components/base/SmartTableSelect.tsx b/src/components/Form/src/smart-boot/components/base/SmartTableSelect.tsx
new file mode 100644
index 000000000..2e20ec75d
--- /dev/null
+++ b/src/components/Form/src/smart-boot/components/base/SmartTableSelect.tsx
@@ -0,0 +1,127 @@
+import type { SmartTableProps } from '@/components/SmartTable';
+
+import { defineComponent, ref } from 'vue';
+
+import { propTypes } from '@/utils/propTypes';
+import { useModal } from '@/components/Modal';
+
+import SmartTableSelectModal from './SmartTableSelectModal';
+
+import './SmartTableSelect.less';
+
+export default defineComponent({
+ name: 'SmartTableSelect',
+ props: {
+ // 是否支持多选
+ multiple: propTypes.bool.def(true),
+ value: propTypes.oneOfType([propTypes.string, propTypes.array]),
+ // label字段
+ labelField: propTypes.string.isRequired,
+ // value字段
+ valueField: propTypes.string.isRequired,
+ tableProps: {
+ type: Object as PropType,
+ required: true,
+ },
+ disabled: propTypes.bool.def(false),
+ size: String as PropType,
+ },
+ emits: ['update:value', 'change'],
+ setup(props, { emit }) {
+ const [registerModal, { openModal }] = useModal();
+ const optionsRef = ref>([]);
+
+ const handleOptionChange = (options) => {
+ optionsRef.value = options;
+ };
+ const handleSelectData = (options: any[]) => {
+ emit(
+ 'update:value',
+ options.map((item) => item.value),
+ );
+ emit(
+ 'change',
+ options.map((item) => item.value),
+ );
+ };
+ const handleDeselect = (value) => {
+ const data = (props.value as any[]).filter((item) => item !== value);
+ emit('update:value', data);
+ emit('change', data);
+ };
+ return {
+ registerModal,
+ openModal,
+ handleSelectData,
+ optionsRef,
+ handleDeselect,
+ handleOptionChange,
+ };
+ },
+ render() {
+ const {
+ $attrs,
+ multiple,
+ tableProps,
+ $slots,
+ disabled,
+ $t,
+ openModal,
+ registerModal,
+ labelField,
+ valueField,
+ handleSelectData,
+ optionsRef,
+ value,
+ handleDeselect,
+ handleOptionChange,
+ size,
+ } = this;
+ const modalSlots: any = {
+ table: $slots.table,
+ };
+ return (
+
+
+
+
+
+
+ openModal(true, value || {})}
+ >
+ {$t('common.button.choose')}
+
+
+
+
+ {modalSlots}
+
+
+ );
+ },
+});
diff --git a/src/components/Form/src/smart-boot/components/base/SmartTableSelectModal.tsx b/src/components/Form/src/smart-boot/components/base/SmartTableSelectModal.tsx
new file mode 100644
index 000000000..e677a32aa
--- /dev/null
+++ b/src/components/Form/src/smart-boot/components/base/SmartTableSelectModal.tsx
@@ -0,0 +1,205 @@
+import type { SmartTableProps } from '@/components/SmartTable';
+
+import { computed, defineComponent, toRefs, unref, watch } from 'vue';
+import { propTypes } from '@/utils/propTypes';
+import { Col, Row } from 'ant-design-vue';
+
+import { BasicModal, useModalInner } from '@/components/Modal';
+import { SmartTable } from '@/components/SmartTable';
+
+import { useSmartTableSelect } from '../../hooks/useSmartTableSelect';
+
+export default defineComponent({
+ name: 'SmartTableSelectModal',
+ components: {
+ BasicModal,
+ },
+ props: {
+ tableProps: {
+ type: Object as PropType,
+ required: true,
+ },
+ selectTableProps: {
+ type: Object as PropType>,
+ },
+ // 是否多选
+ multiple: propTypes.bool.def(true),
+ // 是否显示选中
+ showSelect: propTypes.bool.def(false),
+ // label字段
+ labelField: propTypes.string.isRequired,
+ // value字段
+ valueField: propTypes.string.isRequired,
+ selectValues: propTypes.array.def([]),
+ listApi: {
+ type: Function as PropType<(data: any) => Promise>,
+ required: true,
+ },
+ // 是否每次弹窗都加载数据
+ alwaysLoad: propTypes.bool.def(false),
+ },
+ emits: ['register', 'select-data', 'option-change'],
+ setup(props, { emit, slots }) {
+ const { tableProps, selectTableProps, valueField, selectValues, alwaysLoad, multiple } =
+ toRefs(props);
+
+ const hasTableSlot = computed(() => {
+ return slots.table !== undefined;
+ });
+
+ const emitSelectData = () => {
+ const selectOptions = getSelectOptions();
+ closeModal();
+ emit('option-change', selectOptions);
+ emit('select-data', selectOptions, unref(selectRowsRef));
+ };
+
+ const getSelectOptions = (): LabelValueOptions => {
+ return unref(selectRowsRef).map((item) => {
+ return {
+ label: item[props.labelField],
+ value: item[props.valueField],
+ };
+ });
+ };
+
+ const {
+ registerTable,
+ handleCheckboxChange,
+ registerSelectTable,
+ selectRowsRef,
+ setSelectData,
+ addSelectData,
+ removeSelectData,
+ getSelectData,
+ getTableCheckboxConfig,
+ handleCheckboxAll,
+ getHasSearchForm,
+ query,
+ getTableRadioConfig,
+ handleRadioChange,
+ handleSetSelect,
+ } = useSmartTableSelect(
+ tableProps,
+ selectTableProps,
+ props.showSelect,
+ valueField,
+ selectValues,
+ hasTableSlot,
+ props.listApi,
+ alwaysLoad,
+ multiple,
+ );
+ const [registerModal, { closeModal }] = useModalInner(async (_) => {
+ if (unref(alwaysLoad)) {
+ await query();
+ await handleSetSelect();
+ }
+ });
+
+ watch(selectRowsRef, () => {
+ const selectOptions = getSelectOptions();
+ emit('option-change', selectOptions);
+ });
+
+ const handleOk = () => {
+ emitSelectData();
+ };
+
+ return {
+ registerModal,
+ registerTable,
+ setSelectData,
+ addSelectData,
+ removeSelectData,
+ getSelectData,
+ handleCheckboxChange,
+ registerSelectTable,
+ selectRowsRef,
+ handleOk,
+ getTableCheckboxConfig,
+ handleCheckboxAll,
+ getHasSearchForm,
+ getTableRadioConfig,
+ handleRadioChange,
+ };
+ },
+ render() {
+ const {
+ $attrs,
+ registerModal,
+ $slots,
+ setSelectData,
+ handleOk,
+ addSelectData,
+ removeSelectData,
+ selectRowsRef,
+ } = this;
+ return (
+
+ {$slots.table
+ ? $slots.table({
+ setSelectData,
+ addSelectData,
+ removeSelectData,
+ selectData: selectRowsRef,
+ })
+ : renderTable(this)}
+
+ );
+ },
+});
+
+const renderTable = (instance) => {
+ const {
+ $attrs,
+ showSelect,
+ multiple,
+ registerTable,
+ handleCheckboxChange,
+ registerSelectTable,
+ selectRowsRef,
+ getTableCheckboxConfig,
+ handleCheckboxAll,
+ getHasSearchForm,
+ getTableRadioConfig,
+ handleRadioChange,
+ } = instance;
+ let tableAttrs = {
+ ...$attrs,
+ };
+ if (multiple) {
+ tableAttrs = {
+ ...tableAttrs,
+ checkboxConfig: unref(getTableCheckboxConfig),
+ onCheckboxChange: handleCheckboxChange,
+ onCheckboxAll: handleCheckboxAll,
+ };
+ } else {
+ tableAttrs = {
+ ...tableAttrs,
+ radioConfig: unref(getTableRadioConfig),
+ onRadioChange: handleRadioChange,
+ };
+ }
+ return (
+
+
+
+
+ {showSelect ? (
+
+
+
+ ) : (
+ ''
+ )}
+
+ );
+};
diff --git a/src/components/Form/src/smart-boot/components/user/SmartUserTableSelect.vue b/src/components/Form/src/smart-boot/components/user/SmartUserTableSelect.vue
new file mode 100644
index 000000000..da2cab583
--- /dev/null
+++ b/src/components/Form/src/smart-boot/components/user/SmartUserTableSelect.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
diff --git a/src/components/Form/src/smart-boot/hooks/useSmartTableSelect.ts b/src/components/Form/src/smart-boot/hooks/useSmartTableSelect.ts
new file mode 100644
index 000000000..22f88a454
--- /dev/null
+++ b/src/components/Form/src/smart-boot/hooks/useSmartTableSelect.ts
@@ -0,0 +1,236 @@
+import type { SmartTableProps } from '@/components/SmartTable';
+import { useSmartTable } from '@/components/SmartTable';
+import type { ComputedRef, Ref } from 'vue';
+import { computed, ref, unref, watch } from 'vue';
+import { remove } from 'lodash-es';
+
+export const useSmartTableSelect = (
+ tablePropsRef: Ref,
+ selectTablePropsRef: Ref,
+ showSelect: boolean,
+ valueFieldRef: Ref,
+ selectValuesRef: Ref>,
+ hasTableSlot: ComputedRef,
+ listApi: ((data: any) => Promise) | undefined,
+ alwaysLoad: Ref,
+ multiple: Ref,
+) => {
+ const getTableProps = computed(() => {
+ const tableProps = unref(tablePropsRef);
+ if (unref(alwaysLoad) && tableProps.proxyConfig) {
+ tableProps.proxyConfig.autoLoad = false;
+ }
+ return {
+ ...tableProps,
+ rowConfig: {
+ keyField: unref(valueFieldRef),
+ },
+ };
+ });
+ /**
+ * 是否有搜索表单
+ */
+ const getHasSearchForm = computed(() => {
+ return unref(tablePropsRef).useSearchForm;
+ });
+
+ const getTableCheckboxConfig = computed(() => {
+ return {
+ highlight: true,
+ checkRowKeys: unref(selectValuesRef),
+ };
+ });
+
+ /**
+ * 获取单选配置
+ */
+ const getTableRadioConfig = computed(() => {
+ const result: Recordable = {
+ highlight: true,
+ strict: false,
+ reserve: true,
+ };
+ const selectValues = unref(selectValuesRef);
+ if (selectValues && selectValues.length > 0) {
+ result.checkRowKey = selectValues[0];
+ }
+ return result;
+ });
+
+ watch(selectValuesRef, async () => {
+ selectRowsRef.value = await getSelectRows();
+ if (!unref(hasTableSlot)) {
+ handleSetSelect();
+ }
+ });
+
+ const handleSetSelect = async () => {
+ if (unref(multiple)) {
+ await handleSetSelectRows();
+ } else {
+ await handleSetRadioRow();
+ }
+ };
+
+ const handleSetSelectRows = async () => {
+ await getTableInstance()?.setAllCheckboxRow(false);
+ await setCheckboxRow(unref(selectRowsRef), true);
+ };
+
+ const handleSetRadioRow = async () => {
+ const selectRows = unref(selectRowsRef);
+ if (selectRows && selectRows.length > 0) {
+ await getTableInstance()?.clearRadioRow();
+ await getTableInstance()?.setRadioRow(selectRows[0]);
+ }
+ };
+
+ /**
+ * 获取选中的数据
+ */
+ const getSelectRows = async () => {
+ const selectValues = unref(selectValuesRef);
+ if (!selectValues || selectValues.length === 0) {
+ return [];
+ }
+ const valueField = unref(valueFieldRef);
+ let tableData: any[] = [];
+ try {
+ tableData = getData();
+ } catch (e) {
+ // do nothing
+ }
+ // 没有匹配上的数据
+ let noDataValue: any[] = [];
+ const matchDataList: any[] = [];
+ if (tableData) {
+ tableData.forEach((item) => {
+ const key = item[valueField];
+ if (selectValues.includes(key)) {
+ matchDataList.push(item);
+ }
+ });
+ const matchKeyList = matchDataList.map((item) => item[valueField]);
+ noDataValue = selectValues.filter((item) => !matchKeyList.includes(item));
+ }
+ if (noDataValue.length > 0) {
+ // 没有匹配的数据
+ // 1、从已经选中的数据中查找
+ const selectRows = unref(selectRowsRef);
+ if (selectRows.length > 0) {
+ selectRows.forEach((item) => {
+ if (noDataValue.includes(item[valueField])) {
+ matchDataList.push(item);
+ }
+ });
+ const matchKeyList2 = matchDataList.map((item) => item[valueField]);
+ noDataValue = noDataValue.filter((item) => !matchKeyList2.includes(item));
+ }
+ }
+ if (noDataValue.length > 0) {
+ // 通过API查询
+ const result = await listApi!(noDataValue);
+ matchDataList.push(...result);
+ }
+ return matchDataList;
+ };
+
+ const [registerTable, { setCheckboxRow, getData, getTableInstance, query }] = useSmartTable(
+ unref(getTableProps),
+ );
+ const [registerSelectTable, { setPagination }] = useSmartTable(unref(selectTablePropsRef) || {});
+
+ const selectRowsRef = ref([]);
+
+ /**
+ * 设置选中的数据
+ * @param dataList
+ */
+ const setSelectData = (dataList: any[]) => {
+ console.log('-----------------');
+ selectRowsRef.value = dataList;
+ };
+
+ /**
+ * 添加选中的数据
+ * @param dataList
+ */
+ const addSelectData = (dataList: any[]) => {
+ const selectRows = unref(selectRowsRef);
+ selectRows.push(...dataList);
+ };
+
+ /**
+ * 移除数据
+ * @param dataList
+ */
+ const removeSelectData = (dataList: any[]) => {
+ const selectRows = unref(selectRowsRef);
+ const valueField = unref(valueFieldRef);
+ remove(selectRows, (item) => {
+ return dataList.some((current) => current[valueField] === item[valueField]);
+ });
+ };
+ /**
+ * 获取选中的数据
+ */
+ const getSelectData = () => unref(selectRowsRef);
+
+ const handleCheckboxChange = ({ checked, row }) => {
+ const selectRows = unref(selectRowsRef);
+ if (checked) {
+ addSelectData([row]);
+ } else {
+ removeSelectData([row]);
+ }
+ if (showSelect) {
+ setPagination({
+ total: selectRows.length,
+ });
+ }
+ };
+
+ /**
+ * 单选触发
+ */
+ const handleRadioChange = ({ row, newValue }) => {
+ setSelectData([]);
+ if (newValue) {
+ addSelectData([row]);
+ }
+ };
+
+ const handleCheckboxAll = ({ checked }) => {
+ const currentDataList = getData();
+ if (!currentDataList || currentDataList.length === 0) {
+ return;
+ }
+ if (checked) {
+ const keyList = unref(selectRowsRef).map((item) => item[unref(valueFieldRef)]);
+ addSelectData(
+ currentDataList.filter((item) => !keyList.includes(item[unref(valueFieldRef)])),
+ );
+ } else {
+ removeSelectData(currentDataList);
+ }
+ };
+
+ return {
+ registerTable,
+ handleCheckboxChange,
+ registerSelectTable,
+ selectRowsRef,
+ setSelectData,
+ addSelectData,
+ getSelectData,
+ removeSelectData,
+ getTableCheckboxConfig,
+ handleCheckboxAll,
+ getData,
+ getHasSearchForm,
+ query,
+ getTableRadioConfig,
+ handleRadioChange,
+ handleSetSelect,
+ };
+};
diff --git a/src/components/SmartTable/src/SmartTable.less b/src/components/SmartTable/src/SmartTable.less
index 70b3d09c9..aaacadcd3 100644
--- a/src/components/SmartTable/src/SmartTable.less
+++ b/src/components/SmartTable/src/SmartTable.less
@@ -2,10 +2,6 @@
/* 表格样式 */
.smart-table {
- .vxe-tools--wrapper {
- margin-right: 12px;
- }
-
/* 文本颜色 */
.text-color--success {
diff --git a/src/components/VxeTable/src/css/toolbar.scss b/src/components/VxeTable/src/css/toolbar.scss
index 2b98e861d..fd3241abb 100644
--- a/src/components/VxeTable/src/css/toolbar.scss
+++ b/src/components/VxeTable/src/css/toolbar.scss
@@ -8,7 +8,7 @@
margin-left: 10px;
}
-.vxe-toolbar .vxe-tools--wrapper,
+.vxe-toolbar .vxe-tools--wrapper .vxe-button,
.vxe-toolbar .vxe-tools--operate .vxe-button {
margin-left: 1px;
border-radius: 0 !important;
diff --git a/src/components/registerGlobComp.ts b/src/components/registerGlobComp.ts
index 0bfb15eec..bd48d4e55 100644
--- a/src/components/registerGlobComp.ts
+++ b/src/components/registerGlobComp.ts
@@ -1,6 +1,6 @@
import type { App } from 'vue';
import { Button } from './Button';
-import { Input, Layout, Radio, Tag } from 'ant-design-vue';
+import { Input, Layout, Radio, Tag, Select, Tooltip, Tree } from 'ant-design-vue';
import VXETable from 'vxe-table';
import { i18n } from '@/locales/setupI18n';
@@ -21,5 +21,14 @@ export function registerGlobComp(app: App) {
return key;
},
});
- app.use(Input).use(Button).use(Layout).use(Radio).use(Tag).use(VXETable);
+ app
+ .use(Input)
+ .use(Button)
+ .use(Layout)
+ .use(Radio)
+ .use(Tag)
+ .use(Select)
+ .use(Tooltip)
+ .use(Tree)
+ .use(VXETable);
}
diff --git a/src/directives/permission.ts b/src/directives/permission.ts
index 319e340f6..582566f0f 100644
--- a/src/directives/permission.ts
+++ b/src/directives/permission.ts
@@ -6,6 +6,8 @@
import type { App, Directive, DirectiveBinding } from 'vue';
import { usePermission } from '@/hooks/web/usePermission';
+import { unref } from 'vue';
+import { NoPermissionModeEnum } from '@/enums/appEnum';
function isAuth(el: Element, binding: any) {
const { hasPermission } = usePermission();
@@ -25,8 +27,31 @@ const authDirective: Directive = {
mounted,
};
+const permissionDirective: Directive = {
+ beforeMount(el, binding) {
+ const permission = binding.value;
+ if (!permission) {
+ return;
+ }
+ const { hasPermission, getNoPermissionMode } = usePermission();
+ const has = hasPermission(permission);
+ if (!has) {
+ const noPermissionMode = unref(getNoPermissionMode);
+ if (el.type === 'button') {
+ if (noPermissionMode === NoPermissionModeEnum.disabled) {
+ el.disabled = true;
+ } else if (noPermissionMode === NoPermissionModeEnum.hide) {
+ el.style.display = 'none';
+ }
+ }
+ // TODO:其他情况未处理
+ }
+ },
+};
+
export function setupPermissionDirective(app: App) {
app.directive('auth', authDirective);
+ app.directive('permission', permissionDirective);
}
export default authDirective;
diff --git a/src/enums/appEnum.ts b/src/enums/appEnum.ts
index 1fc3989fc..d39b066f2 100644
--- a/src/enums/appEnum.ts
+++ b/src/enums/appEnum.ts
@@ -50,3 +50,11 @@ export enum RouterTransitionEnum {
FADE_BOTTOM = 'fade-bottom',
FADE_SCALE = 'fade-scale',
}
+
+/**
+ * 无权限显示状态枚举
+ */
+export enum NoPermissionModeEnum {
+ hide = 'hide',
+ disabled = 'disabled',
+}
diff --git a/src/hooks/web/usePermission.ts b/src/hooks/web/usePermission.ts
index 2ffc61eeb..b8285f544 100644
--- a/src/hooks/web/usePermission.ts
+++ b/src/hooks/web/usePermission.ts
@@ -16,6 +16,7 @@ import { RoleEnum } from '@/enums/roleEnum';
import { intersection } from 'lodash-es';
import { isArray } from '@/utils/is';
import { useMultipleTabStore } from '@/store/modules/multipleTab';
+import { computed } from 'vue';
// User permissions related operations
export function usePermission() {
@@ -115,5 +116,6 @@ export function usePermission() {
resume();
}
- return { changeRole, hasPermission, togglePermissionMode, refreshMenu };
+ const getNoPermissionMode = computed(() => appStore.getProjectConfig.noPermissionMode);
+ return { changeRole, hasPermission, togglePermissionMode, refreshMenu, getNoPermissionMode };
}
diff --git a/src/modules/system/views/role/RoleListView.api.ts b/src/modules/system/views/role/RoleListView.api.ts
new file mode 100644
index 000000000..e466853c3
--- /dev/null
+++ b/src/modules/system/views/role/RoleListView.api.ts
@@ -0,0 +1,70 @@
+import { ApiServiceEnum, defHttp } from '@/utils/http/axios';
+
+enum Api {
+ list = 'sys/role/list',
+ getById = 'sys/role/getById',
+ listUser = 'sys/user/list',
+ listUserByRoleId = 'sys/user/listUserByRoleId',
+ setRoleUser = 'sys/role/setRoleUser',
+ delete = 'sys/role/batchDeleteById',
+ batchSaveUpdate = 'sys/role/batchSaveUpdate',
+}
+
+export const listApi = (parameter) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.list,
+ data: parameter,
+ });
+};
+
+export const deleteApi = (parameter: any[]) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.delete,
+ data: parameter.map((item) => item.roleId),
+ });
+};
+
+export const getByIdApi = (model) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.getById,
+ data: model.roleId,
+ });
+};
+
+export const listUserApi = (parameter?) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.listUser,
+ data: parameter,
+ });
+};
+
+export const listUserByRoleIdApi = (roleIds: number[]) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.listUserByRoleId,
+ data: roleIds,
+ });
+};
+
+export const setRoleUserApi = (roleId: number, userIdList: number[]) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.setRoleUser,
+ data: {
+ roleId,
+ userIdList,
+ },
+ });
+};
+
+export const batchSaveUpdateApi = (dataList: any[]) => {
+ return defHttp.post({
+ service: ApiServiceEnum.SMART_SYSTEM,
+ url: Api.batchSaveUpdate,
+ data: dataList,
+ });
+};
diff --git a/src/modules/system/views/role/RoleListView.config.ts b/src/modules/system/views/role/RoleListView.config.ts
new file mode 100644
index 000000000..a39a6c7aa
--- /dev/null
+++ b/src/modules/system/views/role/RoleListView.config.ts
@@ -0,0 +1,179 @@
+import type { SmartColumn, SmartSearchFormSchema } from '@/components/SmartTable';
+import type { FormSchema } from '@/components/Form';
+import { tableUseYnClass } from '@/components/SmartTable';
+
+export const getTableColumns = (): SmartColumn[] => {
+ return [
+ {
+ type: 'checkbox',
+ width: 60,
+ align: 'center',
+ fixed: 'left',
+ },
+ {
+ title: '{system.views.role.table.roleName}',
+ field: 'roleName',
+ width: 120,
+ fixed: 'left',
+ },
+ {
+ title: '{system.views.role.table.roleCode}',
+ field: 'roleCode',
+ width: 150,
+ fixed: 'left',
+ },
+ {
+ title: '{system.views.role.table.roleType}',
+ field: 'roleType',
+ width: 120,
+ },
+ {
+ ...tableUseYnClass(),
+ sortable: true,
+ },
+ {
+ title: '{common.table.remark}',
+ field: 'remark',
+ minWidth: 160,
+ },
+ {
+ title: '{common.table.seq}',
+ field: 'seq',
+ width: 100,
+ sortable: true,
+ },
+ {
+ title: '{common.table.createTime}',
+ field: 'createTime',
+ width: 165,
+ sortable: true,
+ },
+ {
+ title: '{common.table.createUser}',
+ field: 'createUserId',
+ width: 120,
+ formatter: ({ row }: any) => {
+ if (row.createUser) {
+ return row.createUser.fullName;
+ }
+ return '';
+ },
+ },
+ {
+ title: '{common.table.updateTime}',
+ field: 'updateTime',
+ width: 165,
+ sortable: true,
+ },
+ {
+ title: '{common.table.updateUser}',
+ field: 'updateUserId',
+ width: 120,
+ formatter: ({ row }: any) => {
+ if (row.updateUser) {
+ return row.updateUser.fullName;
+ }
+ return '';
+ },
+ },
+ {
+ title: '{common.table.operation}',
+ field: 'operation',
+ width: 120,
+ fixed: 'right',
+ slots: {
+ default: 'table-operation',
+ },
+ },
+ ];
+};
+
+export const getSearchSchemas = (t: Function): SmartSearchFormSchema[] => {
+ return [
+ {
+ label: t('system.views.role.table.roleName'),
+ field: 'roleName',
+ component: 'Input',
+ searchSymbol: 'like',
+ componentProps: {
+ style: {
+ width: '130px',
+ },
+ },
+ },
+ {
+ label: t('system.views.role.table.roleCode'),
+ field: 'roleCode',
+ component: 'Input',
+ searchSymbol: 'like',
+ componentProps: {
+ style: {
+ width: '130px',
+ },
+ },
+ },
+ {
+ label: t('common.title.useYn'),
+ field: 'useYn',
+ component: 'Select',
+ defaultValue: 1,
+ searchSymbol: '=',
+ componentProps: {
+ style: {
+ width: '100px',
+ },
+ options: [
+ {
+ label: t('common.form.use'),
+ value: 1,
+ },
+ {
+ label: t('common.form.noUse'),
+ value: 0,
+ },
+ ],
+ },
+ },
+ ];
+};
+
+export const getAddEditFormSchemas = (t: Function): FormSchema[] => {
+ return [
+ {
+ label: '',
+ field: 'roleId',
+ component: 'Input',
+ show: false,
+ },
+ {
+ label: t('system.views.role.table.roleCode'),
+ field: 'roleCode',
+ component: 'Input',
+ required: true,
+ },
+ {
+ label: t('system.views.role.table.roleName'),
+ field: 'roleName',
+ component: 'Input',
+ required: true,
+ },
+ {
+ label: t('common.table.useYn'),
+ field: 'useYn',
+ component: 'Switch',
+ defaultValue: true,
+ },
+ {
+ label: t('system.views.role.table.roleType'),
+ field: 'roleType',
+ component: 'Input',
+ },
+ {
+ label: t('common.table.seq'),
+ field: 'seq',
+ component: 'InputNumber',
+ required: true,
+ defaultValue: 1,
+ },
+ ];
+};
diff --git a/src/modules/system/views/role/RoleListView.vue b/src/modules/system/views/role/RoleListView.vue
new file mode 100644
index 000000000..591d653bb
--- /dev/null
+++ b/src/modules/system/views/role/RoleListView.vue
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/system/views/role/RoleSetFunction.vue b/src/modules/system/views/role/RoleSetFunction.vue
new file mode 100644
index 000000000..c6d68cbf1
--- /dev/null
+++ b/src/modules/system/views/role/RoleSetFunction.vue
@@ -0,0 +1,158 @@
+
+
+
+ {{ $t('system.views.role.title.setFunction') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('common.button.save') }}
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/system/views/role/hook/useRoleSetUser.ts b/src/modules/system/views/role/hook/useRoleSetUser.ts
new file mode 100644
index 000000000..e2d220a3b
--- /dev/null
+++ b/src/modules/system/views/role/hook/useRoleSetUser.ts
@@ -0,0 +1,42 @@
+import { useModal } from '@/components/Modal';
+import { message } from 'ant-design-vue';
+import { ref, unref } from 'vue';
+
+import { listUserByRoleIdApi, setRoleUserApi } from '../RoleListView.api';
+
+export const useRoleSetUser = (t: Function) => {
+ const [registerSetUserModal, { openModal, setModalProps, closeModal }] = useModal();
+ const currentRole = ref(null);
+ const selectUserList = ref([]);
+
+ const handleShowSetUser = async (role: Recordable) => {
+ currentRole.value = role;
+ openModal(true, { a: 1 });
+ try {
+ setModalProps({ loading: true });
+ const result = await listUserByRoleIdApi([role.roleId]);
+ selectUserList.value = result.map((item) => item.userId);
+ } finally {
+ setModalProps({ loading: false });
+ }
+ };
+
+ const handleSetUser = async (userId: number[]) => {
+ selectUserList.value = userId;
+ try {
+ setModalProps({ confirmLoading: true });
+ await setRoleUserApi(unref(currentRole)!.roleId, userId);
+ message.success(t('common.message.OperationSucceeded'));
+ closeModal();
+ } finally {
+ setModalProps({ confirmLoading: false });
+ }
+ };
+
+ return {
+ selectUserList,
+ handleShowSetUser,
+ registerSetUserModal,
+ handleSetUser,
+ };
+};
diff --git a/src/modules/system/views/role/lang/en_US.ts b/src/modules/system/views/role/lang/en_US.ts
new file mode 100644
index 000000000..0bcb55ea4
--- /dev/null
+++ b/src/modules/system/views/role/lang/en_US.ts
@@ -0,0 +1,24 @@
+export default {
+ system: {
+ views: {
+ role: {
+ title: {
+ setFunction: 'Set function',
+ },
+ table: {
+ roleName: 'Role name',
+ roleCode: 'Role code',
+ roleType: 'Role type',
+ },
+ validate: {
+ roleName: 'Please enter role name',
+ roleCode: 'Please enter role code',
+ roleType: 'Please enter role type',
+ },
+ button: {
+ setRoleUser: 'Set user',
+ },
+ },
+ },
+ },
+};
diff --git a/src/modules/system/views/role/lang/zh_CN.ts b/src/modules/system/views/role/lang/zh_CN.ts
new file mode 100644
index 000000000..2124c1cab
--- /dev/null
+++ b/src/modules/system/views/role/lang/zh_CN.ts
@@ -0,0 +1,24 @@
+export default {
+ system: {
+ views: {
+ role: {
+ title: {
+ setFunction: '设置功能',
+ },
+ table: {
+ roleName: '角色名称',
+ roleCode: '角色编码',
+ roleType: '角色类型',
+ },
+ validate: {
+ roleName: '请输入角色名称',
+ roleCode: '请输入角色编码',
+ roleType: '请输入角色类型',
+ },
+ button: {
+ setRoleUser: '关联用户',
+ },
+ },
+ },
+ },
+};
diff --git a/src/settings/projectSetting.ts b/src/settings/projectSetting.ts
index fa597dfb6..3a445d02f 100644
--- a/src/settings/projectSetting.ts
+++ b/src/settings/projectSetting.ts
@@ -1,18 +1,19 @@
import type { ProjectConfig } from '#/config';
-import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '@/enums/menuEnum';
+import { MenuModeEnum, MenuTypeEnum, MixSidebarTriggerEnum, TriggerEnum } from '@/enums/menuEnum';
import { CacheTypeEnum } from '@/enums/cacheEnum';
import {
ContentEnum,
+ NoPermissionModeEnum,
PermissionModeEnum,
- ThemeEnum,
RouterTransitionEnum,
- SettingButtonPositionEnum,
SessionTimeoutProcessingEnum,
+ SettingButtonPositionEnum,
+ ThemeEnum,
} from '@/enums/appEnum';
import {
- SIDE_BAR_BG_COLOR_LIST,
- HEADER_PRESET_BG_COLOR_LIST,
APP_PRESET_COLOR_LIST,
+ HEADER_PRESET_BG_COLOR_LIST,
+ SIDE_BAR_BG_COLOR_LIST,
} from './designSetting';
// ! You need to clear the browser cache after the change
@@ -189,6 +190,7 @@ const setting: ProjectConfig = {
table: 'small',
form: 'small',
},
+ noPermissionMode: NoPermissionModeEnum.disabled,
};
export default setting;
diff --git a/types/config.d.ts b/types/config.d.ts
index 08c9a4de2..ebdb99df9 100644
--- a/types/config.d.ts
+++ b/types/config.d.ts
@@ -6,6 +6,7 @@ import {
RouterTransitionEnum,
SettingButtonPositionEnum,
SessionTimeoutProcessingEnum,
+ NoPermissionModeEnum,
} from '@/enums/appEnum';
import { CacheTypeEnum } from '@/enums/cacheEnum';
@@ -138,6 +139,8 @@ export interface ProjectConfig {
removeAllHttpPending: boolean;
// 尺寸配置
sizeConfig: SizeConfig;
+ // 无权限模式
+ noPermissionMode: NoPermissionModeEnum;
}
export interface GlobConfig {