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 @@ + + + + + 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 {