diff --git a/package.json b/package.json index 0a29eab..ae4e0d4 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "jest": "jest", "lint": "npm run biome:lint && npm run tsc", "lint-staged": "lint-staged", + "lint:fix": "eslint --fix --quiet", "biome:lint": "npx @biomejs/biome lint", "openapi": "max openapi", "prepare": "husky", diff --git a/src/constants/index.ts b/src/constants/index.ts index 3b7d7fd..ede1a5d 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -3,3 +3,34 @@ export const formStatusType: { [key: string]: string } = { update: '编辑', test: '测试', }; + +export const tenantStatus = [ + { label: '开启', value: 0 }, + { label: '关闭', value: 1 }, +]; + +export const modlaTypeText: { [key: string]: string } = { + edit: '编辑', + add: '新增', + menu: '菜单权限', + data: '数据权限', + prod_status: '商品状态控制', + prod_audit: '商品上架审核', + prod_sku: '', +}; + +export const SystemDataScopeEnum = { + ALL: 1, // 全部数据权限 + DEPT_CUSTOM: 2, // 指定部门数据权限 + DEPT_ONLY: 3, // 部门数据权限 + DEPT_AND_CHILD: 4, // 部门及以下数据权限 + DEPT_SELF: 5, // 仅本人数据权限 +}; + +export const systemDataScopetStatus = [ + { label: '全部数据权限', value: 1 }, + { label: '指定部门数据权限', value: 2 }, + { label: '部门数据权限', value: 3 }, + { label: '部门及以下数据权限', value: 4 }, + { label: '仅本人数据权限', value: 5 }, +]; diff --git a/src/pages/system/dept/config.tsx b/src/pages/system/dept/config.tsx new file mode 100644 index 0000000..52b524c --- /dev/null +++ b/src/pages/system/dept/config.tsx @@ -0,0 +1,170 @@ +import type { + ProColumns, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import { Tag } from 'antd'; +import dayjs from 'dayjs'; +import { tenantStatus } from '@/constants'; +import type { DeptVO } from '@/services/system/dept'; +import { getStatusLabel } from '@/utils/constant'; + +export const baseDeptColumns: ProColumns[] = [ + { + title: '部门名称', + dataIndex: 'name', + ellipsis: true, + hideInSearch: true, + }, + // { prop: "leaderUserId", label: "负责人", width: 100 }, + { + title: '负责人', + dataIndex: 'leaderUserId', + hideInSearch: true, + }, + // { prop: "sort", label: "排序", width: 100 }, + { + title: '排序', + dataIndex: 'sort', + hideInSearch: true, + }, + // { + // prop: "status", + // label: "状态", + // width: 100, + // render: (record) => ( + // + // {getStatusLabel(tenantStatus, record.status)} + // + // ) + // }, + { + title: '状态', + dataIndex: 'status', + hideInSearch: true, + render: (_, record: DeptVO) => [ + + {getStatusLabel(tenantStatus, record.status)} + , + ], + }, + // { + // prop: "createTime", + // label: "创建时间", + // width: 160, + // render: (row) => dateFormatter(row.createTime) + // }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + 'createTime[0]': dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + 'createTime[1]': dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: DeptVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (_type: string): ProFormColumnsType[] => [ + { + title: '上级部门', + dataIndex: 'parentId', + valueType: 'treeSelect', + fieldProps: () => { + return { + multiple: true, + placeholder: '请选择上级部门', + options: [{ label: '11', value: 5016 }], + }; + }, + }, + { + title: '部门名称', + dataIndex: 'name', + valueType: 'text', + formItemProps: { + rules: [ + { + required: true, + message: '请输入部门名称', + }, + ], + }, + }, + { + title: '显示顺序', + dataIndex: 'sort', + valueType: 'digit', + fieldProps: { + min: 0, + }, + formItemProps: { + rules: [ + { + required: true, + message: '请输入显示顺序', + }, + ], + }, + }, + { + title: '负责人', + dataIndex: 'leaderUserId', + formItemProps: { + rules: [ + { + required: true, + message: '请选择负责人', + }, + ], + }, + }, + { + title: '邮箱', + dataIndex: 'email', + formItemProps: { + rules: [ + { + required: true, + message: '请输入邮箱', + }, + ], + }, + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { + label: '正常', + value: 1, + }, + { + label: '禁用', + value: 2, + }, + ], + }, + formItemProps: { + rules: [ + { + required: true, + }, + ], + }, + }, +]; diff --git a/src/pages/system/dept/index.tsx b/src/pages/system/dept/index.tsx index 6aa7795..fa6788e 100644 --- a/src/pages/system/dept/index.tsx +++ b/src/pages/system/dept/index.tsx @@ -1,5 +1,136 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createDept, + type DeptReqVO, + type DeptVO, + deleteDept, + getDeptPage, + updateDept, +} from '@/services/system/dept'; +import { handleTree } from '@/utils/tree'; +import { baseDeptColumns, formColumns } from './config'; + const SyStemDept = () => { - return <>SyStemDept; + const configurableDrawerRef = useRef(null); + const tableRef = useRef(null); + + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + + const handleEdit = (record: DeptVO) => { + setType('update'); + setId(record.id); + + configurableDrawerRef.current?.open(record); + }; + + const onFetch = async ( + params: DeptReqVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getDeptPage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: handleTree(data), + success: true, + total: data.total, + }; + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open({}); + }; + + const handleSubmit = useCallback( + async (values: DeptVO) => { + if (type === 'create') { + await createDept(values); + } else { + await updateDept({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: (_text: React.ReactNode, record: DeptVO, _: any, action: any) => [ + handleEdit(record)}> + 编辑 + , + { + await deleteDept(record.id); + action?.reload(); + }} + okText="是" + cancelText="否" + > + 删除 + , + ], + }; + + const columns = [...baseDeptColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + toolbarActions={toolbarActions} + headerTitle="租户列表" + showIndex={false} + showSelection={false} + expandable={{ + defaultExpandAllRows: true, + }} + /> + + + + ); }; export default SyStemDept; diff --git a/src/pages/system/dict/config.tsx b/src/pages/system/dict/config.tsx new file mode 100644 index 0000000..3e90129 --- /dev/null +++ b/src/pages/system/dict/config.tsx @@ -0,0 +1,120 @@ +import type { + ProColumns, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import { Tag } from 'antd'; +import dayjs from 'dayjs'; +import { tenantStatus } from '@/constants'; +import type { DictTypeVO } from '@/services/system/dict/dict.type'; + +export const baseDictTypeColumns: ProColumns[] = [ + { + title: '字典编号', + dataIndex: 'id', + width: 80, + align: 'left', + hideInSearch: true, + }, + { + title: '字典名称', + dataIndex: 'name', + width: 120, + }, + { + title: '字典类型', + dataIndex: 'type', + width: 120, + }, + { + title: '状态', + dataIndex: 'status', + width: 100, + render: (_, record) => ( + + {record.status === 1 ? '正常' : '禁用'} + + ), + }, + { + title: '备注', + dataIndex: 'remark', + width: 120, + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + 'createTime[0]': dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + 'createTime[1]': dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: DictTypeVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (type: string): ProFormColumnsType[] => [ + { + title: '字典名称', + dataIndex: 'name', + valueType: 'text', + colProps: { + span: 12, + }, + formItemProps: { + rules: [ + { + required: true, + message: '请输入字典名称', + }, + ], + }, + }, + { + title: '字典类型', + dataIndex: 'type', + valueType: 'text', + fieldProps: { + disabled: type === 'update', + }, + colProps: { + span: 12, + }, + formItemProps: { + rules: [ + { + required: true, + message: '请输入字典类型', + }, + ], + }, + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: tenantStatus, + }, + }, + { + title: '备注', + dataIndex: 'remark', + valueType: 'textarea', + colProps: { + span: 24, + }, + }, +]; diff --git a/src/pages/system/dict/data/config.tsx b/src/pages/system/dict/data/config.tsx new file mode 100644 index 0000000..2eeb93c --- /dev/null +++ b/src/pages/system/dict/data/config.tsx @@ -0,0 +1,189 @@ +import type { + ProColumns, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import { Tag } from 'antd'; +import dayjs from 'dayjs'; +import { tenantStatus } from '@/constants'; +import type { DictDataVO } from '@/services/system/dict/dict.data'; +import { getStatusLabel } from '@/utils/constant'; + +export const baseDictDataColumns: ProColumns[] = [ + { + title: '字典编码', + dataIndex: 'id', + width: 80, + hideInSearch: true, + }, + { + title: '字典标签', + dataIndex: 'label', + width: 120, + }, + { + title: '字典键值', + dataIndex: 'value', + width: 120, + }, + { + title: '字典排序', + dataIndex: 'sort', + width: 80, + }, + { + title: '状态', + dataIndex: 'status', + width: 80, + render: (_, record) => ( + + {getStatusLabel(tenantStatus, record.status)} + + ), + }, + { + title: '颜色类型', + dataIndex: 'colorType', + width: 80, + }, + { + title: 'CSS Class', + dataIndex: 'cssClass', + width: 120, + }, + { + title: '备注', + dataIndex: 'remark', + width: 120, + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + 'createTime[0]': dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + 'createTime[1]': dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: DictDataVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (_type: string): ProFormColumnsType[] => [ + { + title: '字典类型', + dataIndex: 'dictType', + fieldProps: { + disabled: true, + }, + }, + { + title: '字典标签', + dataIndex: 'label', + formItemProps: { + rules: [ + { + required: true, + message: '请输入字典标签', + }, + ], + }, + }, + { + title: '字典键值', + dataIndex: 'value', + formItemProps: { + rules: [ + { + required: true, + message: '请输入字典键值', + }, + ], + }, + }, + { + title: '显示排序', + dataIndex: 'sort', + valueType: 'digit', + fieldProps: { + min: 0, + max: 9999, + }, + formItemProps: { + rules: [ + { + required: true, + message: '请输入排序', + }, + ], + }, + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { + label: '启用', + value: 1, + }, + { + label: '禁用', + value: 0, + }, + ], + }, + formItemProps: { + rules: [ + { + required: true, + }, + ], + }, + }, + { + title: '颜色类型', + dataIndex: 'colorType', + valueType: 'select', + fieldProps: { + options: [ + { + label: '默认(waiting)', + value: 'waiting', + }, + { + label: '成功(success)', + value: 'success', + }, + { + label: '信息(processing)', + value: 'processing', + }, + { + label: '失败(error)', + value: 'error', + }, + { + label: '警告(warning)', + value: 'warning', + }, + ], + }, + }, + { + title: 'CSS Class', + dataIndex: 'cssClass', + }, + { + title: '备注', + dataIndex: 'remark', + }, +]; diff --git a/src/pages/system/dict/data/index.tsx b/src/pages/system/dict/data/index.tsx new file mode 100644 index 0000000..7cdb76f --- /dev/null +++ b/src/pages/system/dict/data/index.tsx @@ -0,0 +1,139 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { useParams } from '@umijs/max'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createDictData, + type DictDataVO, + deleteDictData, + getDictDataPage, + updateDictData, +} from '@/services/system/dict/dict.data'; +import { baseDictDataColumns, formColumns } from './config'; + +const SyStemDictData = () => { + const configurableDrawerRef = useRef(null); + const tableRef = useRef(null); + + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + + const routerParams = useParams(); + + console.log(routerParams); + + const handleEdit = (record: DictDataVO) => { + setType('update'); + setId(record.id); + + configurableDrawerRef.current?.open(record); + }; + + const onFetch = async (params: { pageSize?: number; current?: number }) => { + const data = await getDictDataPage({ + ...params, + dictType: routerParams.type, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open({ + dictType: routerParams.type, + }); + }; + + const handleSubmit = useCallback( + async (values: DictDataVO) => { + if (type === 'create') { + await createDictData(values); + } else { + await updateDictData({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: ( + _text: React.ReactNode, + record: DictDataVO, + _: any, + action: any, + ) => [ + handleEdit(record)}> + 编辑 + , + { + await deleteDictData(record.id); + action?.reload(); + }} + okText="是" + cancelText="否" + > + 删除 + , + ], + }; + + const columns = [...baseDictDataColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + toolbarActions={toolbarActions} + headerTitle="租户列表" + showIndex={false} + showSelection={false} + /> + + + + ); +}; + +export default SyStemDictData; diff --git a/src/pages/system/dict/index.tsx b/src/pages/system/dict/index.tsx index 1fa62c3..a99deb3 100644 --- a/src/pages/system/dict/index.tsx +++ b/src/pages/system/dict/index.tsx @@ -1,5 +1,144 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { history } from '@umijs/max'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createDictType, + type DictTypeVO, + deleteDictType, + getDictTypePage, + updateDictType, +} from '@/services/system/dict/dict.type'; +import { baseDictTypeColumns, formColumns } from './config'; + const SyStemDict = () => { - return <>SyStemDict; + const configurableDrawerRef = useRef(null); + const tableRef = useRef(null); + + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + + //获取路由数据 + + const handleEdit = (record: DictTypeVO) => { + setType('update'); + setId(record.id); + + configurableDrawerRef.current?.open(record); + }; + + const onFetch = async (params: { pageSize?: number; current?: number }) => { + const data = await getDictTypePage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open({}); + }; + + const handleSubmit = useCallback( + async (values: DictTypeVO) => { + if (type === 'create') { + await createDictType(values); + } else { + await updateDictType({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: ( + _text: React.ReactNode, + record: DictTypeVO, + _: any, + action: any, + ) => [ + handleEdit(record)}> + 编辑 + , + { + await deleteDictType(record.id); + action?.reload(); + }} + okText="是" + cancelText="否" + > + 删除 + , + + history.push({ + pathname: '/system/dict/data/' + record.type, + }) + } + > + 数据 + , + ], + }; + + const columns = [...baseDictTypeColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + toolbarActions={toolbarActions} + headerTitle="租户列表" + showIndex={false} + showSelection={false} + /> + + + + ); }; export default SyStemDict; diff --git a/src/pages/system/menu/config.tsx b/src/pages/system/menu/config.tsx new file mode 100644 index 0000000..8018186 --- /dev/null +++ b/src/pages/system/menu/config.tsx @@ -0,0 +1,125 @@ +// src/pages/system/menu/config.tsx +import type { + ProColumns, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import { Switch } from 'antd'; +import type { MenuVO } from '@/services/system/menu'; + +export const baseMenuColumns: ProColumns[] = [ + { + title: '菜单名称', + dataIndex: 'name', + width: 150, + }, + { + title: '图标', + dataIndex: 'icon', + width: 60, + render: (_, record: MenuVO) => ( + + {record.icon ? : '—'} + + ), + }, + { + title: '排序', + dataIndex: 'sort', + width: 80, + }, + { + title: '权限标识', + dataIndex: 'permission', + width: 150, + }, + { + title: '组件路径', + dataIndex: 'component', + width: 150, + }, + { + title: '组件名称', + dataIndex: 'componentName', + width: 150, + }, + { + title: '状态', + dataIndex: 'status', + width: 80, + render: (_, record: MenuVO) => ( + + ), + }, +]; + +export const formColumns = (_type: string): ProFormColumnsType[] => [ + { + title: '菜单名称', + dataIndex: 'name', + formItemProps: { + rules: [ + { + required: true, + message: '请输入菜单名称', + }, + ], + }, + }, + { + title: '图标', + dataIndex: 'icon', + valueType: 'select', + fieldProps: { + placeholder: '请选择图标', + options: [ + { label: '商品', value: 'icon-product' }, + { label: '系统', value: 'icon-system' }, + { label: '用户', value: 'icon-user' }, + { label: '设置', value: 'icon-setting' }, + ], + }, + }, + { + title: '排序', + dataIndex: 'sort', + valueType: 'digit', + fieldProps: { + placeholder: '请输入排序值', + }, + }, + { + title: '权限标识', + dataIndex: 'permission', + fieldProps: { + placeholder: '请输入权限标识', + }, + }, + { + title: '组件路径', + dataIndex: 'component', + fieldProps: { + placeholder: '请输入组件路径', + }, + }, + { + title: '组件名称', + dataIndex: 'componentName', + fieldProps: { + placeholder: '请输入组件名称', + }, + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { label: '启用', value: 1 }, + { label: '禁用', value: 0 }, + ], + }, + }, +]; diff --git a/src/pages/system/menu/index.tsx b/src/pages/system/menu/index.tsx index d0c7045..383a9cc 100644 --- a/src/pages/system/menu/index.tsx +++ b/src/pages/system/menu/index.tsx @@ -1,5 +1,126 @@ -const SyStemMenu = () => { - return <>SyStemMenu; +// src/pages/system/menu/index.tsx + +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createMenu, + deleteMenu, + getMenuList, + type MenuReqVO, + type MenuVO, + updateMenu, +} from '@/services/system/menu'; +import { handleTree } from '@/utils/tree'; +import { baseMenuColumns, formColumns } from './config'; + +const SystemMenu = () => { + const configurableDrawerRef = useRef(null); + const tableRef = useRef(null); + + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + + const handleEdit = (record: MenuVO) => { + setType('update'); + setId(record.id); + + configurableDrawerRef.current?.open(record); + }; + + const onFetch = async (params: MenuReqVO) => { + const data = await getMenuList({ + ...params, + }); + return { + data: handleTree(data), + success: true, + total: data.total, + }; + }; + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open({}); + }; + + const handleSubmit = useCallback( + async (values: MenuVO) => { + if (type === 'create') { + await createMenu(values); + } else { + await updateMenu({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: (_text: React.ReactNode, record: MenuVO, _: any, action: any) => [ + handleEdit(record)}> + 编辑 + , + { + await deleteMenu(record.id); + action?.reload(); + }} + okText="是" + cancelText="否" + > + 删除 + , + ], + }; + const columns = [...baseMenuColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + toolbarActions={toolbarActions} + headerTitle="租户列表" + showIndex={false} + showSelection={false} + /> + + + + ); }; -export default SyStemMenu; +export default SystemMenu; diff --git a/src/pages/system/post/config.tsx b/src/pages/system/post/config.tsx new file mode 100644 index 0000000..f745f9d --- /dev/null +++ b/src/pages/system/post/config.tsx @@ -0,0 +1,121 @@ +import type { + ProColumns, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import { Tag } from 'antd'; +import dayjs from 'dayjs'; +import { tenantStatus } from '@/constants'; +import type { PostVO } from '@/services/system/post'; +import { getStatusLabel } from '@/utils/constant'; + +export const basePostColumns: ProColumns[] = [ + { + title: '岗位编号', + dataIndex: 'id', + width: 80, + }, + { + title: '岗位名称', + dataIndex: 'name', + width: 120, + }, + { + title: '岗位编码', + dataIndex: 'code', + width: 120, + }, + { + title: '岗位顺序', + dataIndex: 'sort', + width: 80, + }, + { + title: '岗位备注', + dataIndex: 'remark', + width: 120, + }, + { + title: '状态', + dataIndex: 'status', + width: 100, + render: (_, record: PostVO) => [ + + {getStatusLabel(tenantStatus, record.status)} + , + ], + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + 'createTime[0]': dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + 'createTime[1]': dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: PostVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (_type: string): ProFormColumnsType[] => [ + { + title: '岗位名称', + dataIndex: 'name', + formItemProps: { + rules: [ + { + required: true, + message: '请输入岗位名称', + }, + ], + }, + }, + { + title: '岗位编码', + dataIndex: 'code', + formItemProps: { + rules: [ + { + required: true, + message: '请输入岗位编码', + }, + ], + }, + }, + { + title: '岗位顺序', + dataIndex: 'sort', + valueType: 'digit', + fieldProps: { + placeholder: '请输入排序值', + }, + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + fieldProps: { + placeholder: '请选择状态', + options: tenantStatus, + }, + }, + { + title: '备注', + dataIndex: 'remark', + valueType: 'textarea', + fieldProps: { + placeholder: '请输入备注', + }, + }, +]; diff --git a/src/pages/system/post/index.tsx b/src/pages/system/post/index.tsx index 195d9a8..a91c8e8 100644 --- a/src/pages/system/post/index.tsx +++ b/src/pages/system/post/index.tsx @@ -1,5 +1,132 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createPost, + deletePost, + getPostPage, + type PostReqVO, + type PostVO, + updatePost, +} from '@/services/system/post'; +import { basePostColumns, formColumns } from './config'; + const SyStemPost = () => { - return <>SyStemPost; + const configurableDrawerRef = useRef(null); + const tableRef = useRef(null); + + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + + const handleEdit = (record: PostVO) => { + setType('update'); + setId(record.id); + + configurableDrawerRef.current?.open(record); + }; + + const onFetch = async ( + params: PostReqVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getPostPage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open({}); + }; + + const handleSubmit = useCallback( + async (values: PostVO) => { + if (type === 'create') { + await createPost(values); + } else { + await updatePost({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: (_text: React.ReactNode, record: PostVO, _: any, action: any) => [ + handleEdit(record)}> + 编辑 + , + { + await deletePost(record.id); + action?.reload(); + }} + okText="是" + cancelText="否" + > + 删除 + , + ], + }; + + const columns = [...basePostColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + toolbarActions={toolbarActions} + headerTitle="租户列表" + showIndex={false} + showSelection={false} + /> + + + + ); }; export default SyStemPost; diff --git a/src/pages/system/role/config.tsx b/src/pages/system/role/config.tsx new file mode 100644 index 0000000..2b10507 --- /dev/null +++ b/src/pages/system/role/config.tsx @@ -0,0 +1,143 @@ +import type { + ProColumns, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import { Tag } from 'antd'; +import dayjs from 'dayjs'; +import { tenantStatus } from '@/constants'; +import type { RoleVO } from '@/services/system/role'; +import { getStatusLabel } from '@/utils/constant'; + +export const baseTenantColumns: ProColumns[] = [ + { + title: '角色编号', + dataIndex: 'id', + width: 100, + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '角色名称', + dataIndex: 'name', + width: 100, + }, + { + title: '角色类型', + dataIndex: 'type', + width: 100, + render: (_, record: RoleVO) => [ + + {record.type === 1 ? '内置' : '自定义'} + , + ], + hideInSearch: true, + }, + { + title: '角色标识', + dataIndex: 'code', + width: 150, + render: (_, record: RoleVO) => [ + + {record.code} + , + ], + hideInSearch: true, + }, + { + title: '显示顺序', + dataIndex: 'sort', + width: 100, + hideInSearch: true, + }, + { + title: '备注', + dataIndex: 'remark', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '租户状态', + dataIndex: 'status', + width: 100, + render: (_, record: RoleVO) => [ + + {getStatusLabel(tenantStatus, record.status)} + , + ], + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + 'createTime[0]': dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + 'createTime[1]': dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: RoleVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (_type: string): ProFormColumnsType[] => [ + { + title: '角色名称', + dataIndex: 'name', + formItemProps: { + rules: [ + { + required: true, + message: '请输入角色名称', + }, + ], + }, + }, + { + title: '角色标识', + dataIndex: 'code', + formItemProps: { + rules: [ + { + required: true, + message: '请输入角色名称', + }, + ], + }, + }, + { + title: '显示顺序', + dataIndex: 'sort', + valueType: 'digit', + fieldProps: { + placeholder: '请输入显示顺序', + }, + }, + { + title: '租户状态', + dataIndex: 'status', + valueType: 'select', + fieldProps: { + multiple: true, + placeholder: '请选择租户状态', + options: tenantStatus, + }, + }, + { + title: '备注', + dataIndex: 'remark', + valueType: 'textarea', + fieldProps: { + placeholder: '请输入备注', + min: 5, + max: 20, + }, + }, +]; diff --git a/src/pages/system/role/index.tsx b/src/pages/system/role/index.tsx index cc9b280..1fa8b6b 100644 --- a/src/pages/system/role/index.tsx +++ b/src/pages/system/role/index.tsx @@ -1,5 +1,132 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createRole, + deleteRole, + getRolePage, + type RolePageReqVO, + type RoleVO, + updateRole, +} from '@/services/system/role'; +import { baseTenantColumns, formColumns } from './config'; + const SyStemRole = () => { - return <>SyStemRole; + const configurableDrawerRef = useRef(null); + const tableRef = useRef(null); + + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + + const handleEdit = (record: RoleVO) => { + setType('update'); + setId(record.id); + + configurableDrawerRef.current?.open(record); + }; + + const onFetch = async ( + params: RolePageReqVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getRolePage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open({}); + }; + + const handleSubmit = useCallback( + async (values: RoleVO) => { + if (type === 'create') { + await createRole(values); + } else { + await updateRole({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: (_text: React.ReactNode, record: RoleVO, _: any, action: any) => [ + handleEdit(record)}> + 编辑 + , + { + await deleteRole(record.id); + action?.reload(); + }} + okText="是" + cancelText="否" + > + 删除 + , + ], + }; + + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + toolbarActions={toolbarActions} + headerTitle="租户列表" + showIndex={false} + showSelection={false} + /> + + + + ); }; export default SyStemRole; diff --git a/src/pages/system/tenant/list/config.tsx b/src/pages/system/tenant/list/config.tsx index e61c262..3284ef2 100644 --- a/src/pages/system/tenant/list/config.tsx +++ b/src/pages/system/tenant/list/config.tsx @@ -29,7 +29,6 @@ export const baseTenantColumns: ProColumns[] = [ request: async () => { const packageList: { id: number; name: string }[] = await getTenantPackageList(); - console.log(packageList); packageList.map((item) => ({ label: item.name, value: item.id })); return packageList.map((item) => ({ label: item.name, value: item.id })); }, @@ -83,7 +82,14 @@ export const baseTenantColumns: ProColumns[] = [ valueType: 'dateRange', search: { transform: (value) => { - return [`${value[0]} 00:00:00`, `${value[1]} 00:00:00`]; + return { + 'createTime[0]': dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + 'createTime[1]': dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; }, }, render: (_, record: TenantVO) => diff --git a/src/pages/system/tenant/list/index.tsx b/src/pages/system/tenant/list/index.tsx index 9a9859f..d2aa109 100644 --- a/src/pages/system/tenant/list/index.tsx +++ b/src/pages/system/tenant/list/index.tsx @@ -1,8 +1,7 @@ import { PlusOutlined } from '@ant-design/icons'; import type { ActionType, ProColumns } from '@ant-design/pro-components'; -import { Modal, Popconfirm } from 'antd'; +import { Popconfirm } from 'antd'; import dayjs from 'dayjs'; -import { ref } from 'process'; import React, { createContext, useCallback, useRef, useState } from 'react'; import ConfigurableDrawerForm, { type ConfigurableDrawerFormRef, @@ -93,7 +92,7 @@ const TenantList = () => { valueType: 'option', fixed: 'right', width: 120, - render: (text: React.ReactNode, record: TenantVO, _: any, action: any) => [ + render: (_text: React.ReactNode, record: TenantVO, _: any, action: any) => [ handleEdit(record)}> 编辑 , @@ -122,8 +121,6 @@ const TenantList = () => { headerTitle="租户列表" showIndex={false} showSelection={false} - showActions - maxActionCount={3} /> [] = [ + { + title: '套餐编号', + dataIndex: 'id', + width: 100, + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '套餐名', + dataIndex: 'name', + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + valueEnum: { + all: { text: '全部', status: 'Default' }, + open: { text: '未解决', status: 'Error' }, + closed: { text: '已解决', status: 'Success' }, + }, + }, + { + title: '备注', + dataIndex: 'remark', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + 'createTime[0]': dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + 'createTime[1]': dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: TenantPackageVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (_type: string): ProFormColumnsType[] => [ + { + title: '套餐名', + dataIndex: 'name', + formItemProps: { + rules: [ + { + required: true, + message: '请输入用户名', + }, + ], + }, + }, + { + title: '套餐权限', + dataIndex: 'menuIds', + valueType: 'treeSelect', + fieldProps: { + multiple: true, + placeholder: '请选择租户权限', + options: [{ lable: '11', value: 5016 }], + }, + }, + { + title: '租户状态', + dataIndex: 'status', + valueType: 'radio', + formItemProps: { + rules: [ + { + required: true, + message: '请选择租户状态', + }, + ], + }, + fieldProps: { + placeholder: '请选择套餐类型', + options: [ + { + label: '开启', + value: 1, + }, + { + label: '关闭', + value: 0, + }, + ], + }, + }, + { + title: '备注', + dataIndex: 'remark', + valueType: 'textarea', + }, +]; diff --git a/src/pages/system/tenant/package/index.tsx b/src/pages/system/tenant/package/index.tsx index 34286af..1b3ee4b 100644 --- a/src/pages/system/tenant/package/index.tsx +++ b/src/pages/system/tenant/package/index.tsx @@ -1,5 +1,137 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createTenantPackage, + deleteTenantPackage, + getTenantPackagePage, + type TenantPackagePageReqVO, + type TenantPackageVO, + updateTenantPackage, +} from '@/services/system/tenant/package'; +import { baseTenantColumns, formColumns } from './config'; + const TenantPackage = () => { - return
TenantPackage
; + const configurableDrawerRef = useRef(null); + const tableRef = useRef(null); + + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + + const handleEdit = (record: TenantPackageVO) => { + setType('update'); + setId(record.id); + + configurableDrawerRef.current?.open(record); + }; + + const onFetch = async ( + params: TenantPackagePageReqVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getTenantPackagePage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open({}); + }; + + const handleSubmit = useCallback( + async (values: TenantPackageVO) => { + if (type === 'create') { + await createTenantPackage(values); + } else { + await updateTenantPackage({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: ( + _text: React.ReactNode, + record: TenantPackageVO, + _: any, + action: any, + ) => [ + handleEdit(record)}> + 编辑 + , + { + await deleteTenantPackage(record.id); + action?.reload(); + }} + okText="是" + cancelText="否" + > + 删除 + , + ], + }; + + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + toolbarActions={toolbarActions} + headerTitle="租户列表" + showIndex={false} + showSelection={false} + /> + + + + ); }; export default TenantPackage; diff --git a/src/pages/system/user/config.tsx b/src/pages/system/user/config.tsx new file mode 100644 index 0000000..f22bb43 --- /dev/null +++ b/src/pages/system/user/config.tsx @@ -0,0 +1,220 @@ +import type { + ProColumns, + ProCoreActionType, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import { Modal, message, Switch } from 'antd'; +import dayjs from 'dayjs'; +import { updateUserStatus } from '@/services/system/user'; +import type { UserVO } from '@/services/system/user/index'; + +export const baseTenantColumns: ProColumns[] = [ + { + title: '用户编号', + dataIndex: 'id', + width: 80, + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '用户名称', + dataIndex: 'username', + width: 100, + }, + { + title: '用户昵称', + dataIndex: 'nickname', + width: 100, + hideInSearch: true, + }, + { + title: '部门', + dataIndex: 'deptName', + width: 100, + }, + { + title: '手机号码', + dataIndex: 'mobile', + width: 100, + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + valueEnum: { + all: { text: '全部', status: 'Default' }, + open: { text: '未解决', status: 'Error' }, + closed: { text: '已解决', status: 'Success' }, + }, + render: ( + _dom: React.ReactNode, + record: UserVO, + _index: number, + action: ProCoreActionType | undefined, + ) => [ + { + const text = record.status ? '启用' : '停用'; + Modal.confirm({ + title: '确认操作', + content: `确认要"${text}""${record.username}"用户吗?`, + onOk: async () => { + const status = record.status === 0 ? 1 : 0; + await updateUserStatus(record.id, status); + message.success('修改成功'); + action?.reload(); + // 执行状态变更逻辑 + }, + }); + // await message.confirm("修改成功"); + }} + >, + ], + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + 'createTime[0]': dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + 'createTime[1]': dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: UserVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, + { + title: '备注', + dataIndex: 'remark', + hideInSearch: true, // 在搜索表单中隐藏 + }, +]; + +export const formColumns = (type: string): ProFormColumnsType[] => [ + { + title: '用户昵称', + dataIndex: 'nickname', + formItemProps: { + rules: [ + { + required: true, + message: '请输入用户名', + }, + ], + }, + }, + { + title: '归属部门', + dataIndex: 'deptId', + valueType: 'treeSelect', + fieldProps: { + multiple: true, + placeholder: '请选择归属部门', + options: [{ lable: '11', value: 5016 }], + }, + }, + { + title: '手机号码', + dataIndex: 'mobile', + formItemProps: { + rules: [ + { + required: true, + message: '请输入联系手机', + }, + ], + }, + }, + // { prop: "email", label: "邮箱", type: "input", inputType: "email" }, + { + title: '邮箱', + dataIndex: 'email', + valueType: 'text', + fieldProps: { + type: 'email', + placeholder: '请输入邮箱', + }, + formItemProps: { + rules: [ + { + required: true, + message: '请输入邮箱', + }, + ], + }, + }, + { + title: '用户名称', + dataIndex: 'username', + hideInForm: type === 'update', + }, + { + title: '用户密码', + dataIndex: 'password', + valueType: 'password', + hideInForm: type === 'update', + fieldProps: { + placeholder: '请输入用户密码', + autoComplete: 'new-password', + }, + formItemProps: { + rules: [ + { + required: true, + message: '请输入用户密码', + }, + ], + }, + }, + { + title: '用户性别', + dataIndex: 'sex', + valueType: 'select', + fieldProps: { + placeholder: '请选择性别', + options: [ + { + label: '男', + value: 1, + }, + { + label: '女', + value: 2, + }, + ], + }, + }, + { + title: '岗位', + dataIndex: 'postIds', + valueType: 'select', + fieldProps: { + mode: 'multiple', + placeholder: '请选择岗位', + options: [ + { + label: '管理员', + value: 1, + }, + { + label: '普通用户', + value: 2, + }, + ], + }, + formItemProps: {}, + }, + { + title: '备注', + dataIndex: 'remark', + valueType: 'textarea', + }, +]; diff --git a/src/pages/system/user/index.tsx b/src/pages/system/user/index.tsx index 5b0441b..141c26e 100644 --- a/src/pages/system/user/index.tsx +++ b/src/pages/system/user/index.tsx @@ -1,5 +1,130 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createUser, + deleteUser, + getUserPage, + type UserReqVO, + type UserVO, + updateUser, +} from '@/services/system/user'; +import { baseTenantColumns, formColumns } from './config'; + const SyStemUser = () => { - return <>SyStemUser; + const configurableDrawerRef = useRef(null); + const tableRef = useRef(null); + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + + const handleEdit = (record: UserVO) => { + setType('update'); + setId(record.id); + + configurableDrawerRef.current?.open(record); + }; + + const onFetch = async ( + params: UserReqVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getUserPage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open({}); + }; + + const handleSubmit = useCallback( + async (values: UserVO) => { + if (type === 'create') { + await createUser(values); + } else { + await updateUser({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: (_text: React.ReactNode, record: UserVO, _: any, action: any) => [ + handleEdit(record)}> + 编辑 + , + { + await deleteUser(record.id); + action?.reload(); + }} + okText="是" + cancelText="否" + > + 删除 + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + toolbarActions={toolbarActions} + headerTitle="租户列表" + showIndex={false} + showSelection={false} + /> + + + + ); }; export default SyStemUser; diff --git a/src/services/system/dept/index.ts b/src/services/system/dept/index.ts new file mode 100644 index 0000000..ce55ade --- /dev/null +++ b/src/services/system/dept/index.ts @@ -0,0 +1,89 @@ +import { request } from "@umijs/max"; + +export interface DeptVO { + id: number; + name: string; + parentId: number; + status: number; + sort: number; + leaderUserId: number; + phone: string; + email: string; + createTime: Date; +} + +export interface DeptReqVO extends PageParam { + name?: string; + status?: number; +} + +// 查询部门(精简)列表 +// export const getSimpleDeptList = async (): Promise => { +// return await request.get({ url: "/system/dept/simple-list" }); +// }; + +export const getSimpleDeptList = async () => { + return request("/system/dept/simple-list", { + method: "GET", + }); +}; + +// 查询部门列表 +// export const getDeptPage = async (params: DeptReq): Promise => { +// return await request.get({ url: "/system/dept/list", params }); +// }; + +export const getDeptPage = (params: DeptReqVO) => { + return request("/system/dept/list", { + method: "GET", + params, + }); +}; + +// 查询部门详情 +// export const getDept = async (id: number) => { +// return await request.get({ url: "/system/dept/get?id=" + id }); +// }; + +export const getDept = (id: number) => { + return request("/system/dept/get", { + method: "GET", + params: { id }, + }); +}; + +// 新增部门 +// export const createDept = async (data: Dept) => { +// return await request.post({ url: "/system/dept/create", data: data }); +// }; + +export const createDept = (data: DeptVO) => { + return request("/system/dept/create", { + method: "POST", + data, + }); +}; + +// 修改部门 +// export const updateDept = async (params: Dept) => { +// return await request.put({ url: "/system/dept/update", data: params }); +// }; + +export const updateDept = (params: DeptVO) => { + return request("/system/dept/update", { + method: "PUT", + data: params, + }); +}; + +// 删除部门 +// export const deleteDept = async (id: number) => { +// return await request.delete({ url: "/system/dept/delete?id=" + id }); +// }; + +export const deleteDept = (id: number) => { + return request("/system/dept/delete", { + method: "DELETE", + params: { id }, + }); +}; diff --git a/src/services/system/dict/dict.data.ts b/src/services/system/dict/dict.data.ts new file mode 100644 index 0000000..2e0866b --- /dev/null +++ b/src/services/system/dict/dict.data.ts @@ -0,0 +1,101 @@ +import { request } from "@umijs/max"; + +export type DictDataVO = { + id: number; + sort: number | undefined; + label: string; + value: string; + dictType: string; + status: number; + colorType: string; + cssClass: string; + remark: string; + createTime: Date; +}; + +export interface DictDataReqVO extends PageParam { + dictType?: string; + createTime?: Date; +} + +// 查询字典数据(精简)列表 +// export const getSimpleDictDataList = () => { +// return request.get({ url: "/system/dict-data/simple-list" }); +// }; + +export const getSimpleDictDataList = () => { + return request("/system/dict-data/simple-list", { + method: "GET", + }); +}; + +// 查询字典数据列表 +// export const getDictDataPage = (params: PageParam) => { +// return request.get({ url: "/system/dict-data/page", params }); +// }; + +export const getDictDataPage = (params: DictDataReqVO) => { + return request("/system/dict-data/page", { + method: "GET", + params, + }); +}; + +// 查询字典数据详情 +// export const getDictData = (id: number) => { +// return request.get({ url: "/system/dict-data/get?id=" + id }); +// }; + +export const getDictData = (id: number) => { + return request("/system/dict-data/get", { + method: "GET", + params: { id }, + }); +}; + +// 新增字典数据 +// export const createDictData = (data: DictData) => { +// return request.post({ url: "/system/dict-data/create", data }); +// }; + +export const createDictData = (data: DictDataVO) => { + return request("/system/dict-data/create", { + method: "POST", + data, + }); +}; + +// 修改字典数据 +// export const updateDictData = (data: DictData) => { +// return request.put({ url: "/system/dict-data/update", data }); +// }; + +export const updateDictData = (data: DictDataVO) => { + return request("/system/dict-data/update", { + method: "PUT", + data, + }); +}; + +// 删除字典数据 +// export const deleteDictData = (id: number) => { +// return request.delete({ url: "/system/dict-data/delete?id=" + id }); +// }; + +export const deleteDictData = (id: number) => { + return request("/system/dict-data/delete", { + method: "DELETE", + params: { id }, + }); +}; + +// 导出字典类型数据 +// export const exportDictData = (params: DictData) => { +// return request.download({ url: "/system/dict-data/export", params }); +// }; +export const exportDictData = (params: DictDataVO) => { + return request("/system/dict-data/export", { + method: "GET", + params, + }); +}; diff --git a/src/services/system/dict/dict.type.ts b/src/services/system/dict/dict.type.ts new file mode 100644 index 0000000..616f1d9 --- /dev/null +++ b/src/services/system/dict/dict.type.ts @@ -0,0 +1,101 @@ +import { request } from "@umijs/max"; + +export interface DictTypeVO extends PageParam { + id: number; + name: string; + type: string; + status: number; + remark: string; + createTime: Date; +} + +export interface DictReqVO extends PageParam { + name?: string; + status?: number; + type?: string; + createTime?: Date; +} + +// 查询字典(精简)列表 +// export const getSimpleDictTypeList = () => { +// return request.get({ url: "/system/dict-type/list-all-simple" }); +// }; + +export const getSimpleDictTypeList = () => { + return request("/system/dict-type/list-all-simple", { + method: "GET", + }); +}; + +// 查询字典列表 +// export const getDictTypePage = ( +// params: DictReq +// ): Promise> => { +// return request.get({ url: "/system/dict-type/page", params }); +// }; + +export const getDictTypePage = (params: PageParam) => { + return request("/system/dict-type/page", { + method: "GET", + params, + }); +}; + +// // 查询字典详情 +// export const getDictType = (id?: number) => { +// return request.get({ url: "/system/dict-type/get?id=" + id }); +// }; +export const getDictType = (id: number) => { + return request("/system/dict-type/get", { + method: "GET", + params: { id }, + }); +}; + +// 新增字典 +// export const createDictType = (data: DictType) => { +// return request.post({ url: "/system/dict-type/create", data }); +// }; + +export const createDictType = (data: DictTypeVO) => { + return request("/system/dict-type/create", { + method: "POST", + data, + }); +}; + +// 修改字典 +// export const updateDictType = (data: DictType) => { +// return request.put({ url: "/system/dict-type/update", data }); +// }; + +export const updateDictType = (data: DictTypeVO) => { + return request("/system/dict-type/update", { + method: "PUT", + data, + }); +}; + +// 删除字典 +// export const deleteDictType = (id: number) => { +// return request.delete({ url: "/system/dict-type/delete?id=" + id }); +// }; + +export const deleteDictType = (id: number) => { + return request("/system/dict-type/delete", { + method: "DELETE", + params: { id }, + }); +}; + +// 导出字典类型 +// export const exportDictType = (params: DictType) => { +// return request.download({ url: "/system/dict-type/export", params }); +// }; + +export const exportDictType = (params: DictTypeVO) => { + return request("/system/dict-type/export", { + method: "POST", + params, + }); +}; diff --git a/src/services/system/menu/index.ts b/src/services/system/menu/index.ts index 8f048ed..0410449 100644 --- a/src/services/system/menu/index.ts +++ b/src/services/system/menu/index.ts @@ -19,32 +19,81 @@ export interface MenuVO { children?: MenuVO[]; } +export interface MenuReqVO { + name?: string; + status?: 0 | 1; +} + // 查询菜单(精简)列表 +export const getSimpleMenusList = () => { + return request("/system/menu/simple-list", { + method: "GET", + }); +}; // export const getSimpleMenusList = () => { // return request.get({ url: '/system/menu/simple-list' }) // } // // 查询菜单列表 + +export const getMenuList = (params: MenuReqVO) => { + return request("/system/menu/list", { + method: "GET", + params, + }); +}; + // export const getMenuList = (params) => { // return request.get({ url: '/system/menu/list', params }) // } // // 获取菜单详情 + +export const getMenu = (id: number) => { + return request("/system/menu/get", { + method: "GET", + params: { id }, + }); +}; + // export const getMenu = (id: number) => { // return request.get({ url: '/system/menu/get?id=' + id }) // } // // 新增菜单 +export const createMenu = (data: MenuVO) => { + return request("/system/menu/create", { + method: "POST", + data, + }); +}; + // export const createMenu = (data: MenuVO) => { // return request.post({ url: '/system/menu/create', data }) // } // // 修改菜单 + +export const updateMenu = (data: MenuVO) => { + return request("/system/menu/update", { + method: "PUT", + data, + }); +}; + // export const updateMenu = (data: MenuVO) => { // return request.put({ url: '/system/menu/update', data }) // } // // 删除菜单 + +export const deleteMenu = (id: number) => { + return request("/system/menu/delete", { + method: "DELETE", + params: { id }, + }); +}; + // export const deleteMenu = (id: number) => { // return request.delete({ url: '/system/menu/delete?id=' + id }) // } diff --git a/src/services/system/post/index.ts b/src/services/system/post/index.ts new file mode 100644 index 0000000..919402a --- /dev/null +++ b/src/services/system/post/index.ts @@ -0,0 +1,87 @@ +import { request } from "@umijs/max"; + +export interface PostVO { + id: number; + name: string; + code: string; + sort: number; + status: number; + remark: string; + createTime?: Date; +} + +export interface PostReqVO extends PageParam { + name?: string; + status?: number; + code?: string; +} + +// 查询岗位列表 +// export const getPostPage = async (params: PostReq) => { +// return await request.get({ url: "/system/post/page", params }); +// }; + +export const getPostPage = (params: PostReqVO) => { + return request("/system/post/page", { + method: "GET", + params, + }); +}; + +// 获取岗位精简信息列表 +// export const getSimplePostList = async (): Promise => { +// return await request.get({ url: "/system/post/simple-list" }); +// }; + +export const getSimplePostList = () => { + return request("/system/post/simple-list", { + method: "GET", + }); +}; + +// 查询岗位详情 +// export const getPost = async (id: number) => { +// return await request.get({ url: "/system/post/get?id=" + id }); +// }; + +export const getPost = (id: number) => { + return request("/system/post/get", { + method: "GET", + params: { id }, + }); +}; +// 新增岗位 +// export const createPost = async (data: Post) => { +// return await request.post({ url: "/system/post/create", data }); +// }; + +export const createPost = async (data: PostVO) => { + return await request("/system/post/create", { + method: "POST", + data, + }); +}; + +// 修改岗位 +// export const updatePost = async (data: Post) => { +// return await request.put({ url: "/system/post/update", data }); +// }; + +export const updatePost = async (data: PostVO) => { + return await request("/system/post/update", { + method: "PUT", + data, + }); +}; + +// 删除岗位 +// export const deletePost = async (id: number) => { +// return await request.delete({ url: "/system/post/delete?id=" + id }); +// }; + +export const deletePost = async (id: number) => { + return await request("/system/post/delete", { + method: "DELETE", + params: { id }, + }); +}; diff --git a/src/services/system/role/index.ts b/src/services/system/role/index.ts new file mode 100644 index 0000000..8448061 --- /dev/null +++ b/src/services/system/role/index.ts @@ -0,0 +1,106 @@ +import { request } from "@umijs/max"; + +export interface RoleVO { + id: number; + name: string; + code: string; + sort: number; + status: number; + type: number; + dataScope: number; + dataScopeDeptIds: number[]; + createTime: Date; + menuIds: number[]; +} + +export interface RolePageReqVO extends PageParam { + name?: string; + code?: string; + status?: string; + createTime?: Date[]; +} + +export interface UpdateStatusReq { + id: number; + status: number; +} + +// 查询角色列表 +export const getRolePage = (params: RolePageReqVO) => { + return request("/system/role/page", { + method: "GET", + params, + }); +}; +// export const getRolePage = async ( +// params: RolePageReq +// ): Promise> => { +// return await request.get({ url: "/system/role/page", params }); +// }; + +// 查询角色(精简)列表 + +export const getSimpleRoleList = () => { + return request("/system/role/simple-list", { + method: "GET", + }); +}; +// export const getSimpleRoleList = async (): Promise => { +// return await request.get({ url: "/system/role/simple-list" }); +// }; + +// 查询角色详情 +export const getRole = (id: number) => { + return request("/system/role/get", { + method: "GET", + params: { id }, + }); +}; +// export const getRole = async (id: number) => { +// return await request.get({ url: "/system/role/get?id=" + id }); +// }; + +// 新增角色 +export const createRole = (data: RoleVO) => { + return request("/system/role/create", { + method: "POST", + data, + }); +}; + +// export const createRole = async (data: Role) => { +// return await request.post({ url: "/system/role/create", data }); +// }; + +// 修改角色 +// export const updateRole = async (data: Role) => { +// return await request.put({ url: "/system/role/update", data }); +// }; +export const updateRole = (data: RoleVO) => { + return request("/system/role/update", { + method: "PUT", + data, + }); +}; + +// 修改角色状态 +export const updateRoleStatus = (data: UpdateStatusReq) => { + return request("/system/role/update-status", { + method: "PUT", + data, + }); +}; +// export const updateRoleStatus = async (data: UpdateStatusReq) => { +// return await request.put({ url: "/system/role/update-status", data }); +// }; + +// 删除角色 +export const deleteRole = (id: number) => { + return request("/system/role/delete", { + method: "DELETE", + params: { id }, + }); +}; +// export const deleteRole = async (id: number) => { +// return await request.delete({ url: "/system/role/delete?id=" + id }); +// }; diff --git a/src/services/system/tenant/package.ts b/src/services/system/tenant/package.ts index 3299d6d..effed7c 100644 --- a/src/services/system/tenant/package.ts +++ b/src/services/system/tenant/package.ts @@ -12,11 +12,27 @@ export interface TenantPackageVO { createTime: Date; } + +export interface TenantPackagePageReqVO extends PageParam { + name?: string; + contactName?: string; + contactMobile?: string; + status?: number; + createTime?: Date[]; +} + // 查询租户套餐列表 // export const getTenantPackagePage = (params: PageParam) => { // return request.get({ url: '/system/tenant-package/page', params }) // } +export async function getTenantPackagePage(params: TenantPackagePageReqVO) { + return request("/system/tenant-package/page", { + method: "GET", + params, + }); +} + // 获得租户 // export const getTenantPackage = (id: number) => { // return request.get({ url: '/system/tenant-package/get?id=' + id }) diff --git a/src/services/system/user/index.ts b/src/services/system/user/index.ts new file mode 100644 index 0000000..01590e2 --- /dev/null +++ b/src/services/system/user/index.ts @@ -0,0 +1,154 @@ +// import request from "@/config/axios"; +import { request } from "@umijs/max"; + +export interface UserVO { + id: number; + username: string; + nickname: string; + deptId: number; + postIds: string[]; + email: string; + mobile: string; + sex: number; + avatar: string; + loginIp: string; + status: number; + remark: string; + loginDate: Date; + createTime: Date; +} + +// export interface User { +// id: number; +// username: string; +// nickname: string; +// deptId: number; +// postIds: string[]; +// email: string; +// mobile: string; +// sex: number; +// avatar: string; +// loginIp: string; +// status: number; +// remark: string; +// loginDate: Date; +// createTime: Date; +// } + +export interface UserReqVO extends PageParam { + username?: string; + status?: number; + mobile?: string; + createTime?: Date; + deptId?: number; +} + +// export interface UserReq extends PageParam { +// username?: string; +// status?: number; +// mobile?: string; +// createTime?: Date; +// deptId?: number; +// } + +// 查询用户管理列表 + +export const getUserPage = (params: UserReqVO) => { + return request("/system/user/page", { + method: "GET", + params, + }); +}; + +// export const getUserPage = (params: UserReq) => { +// return request.get({ url: "/system/user/page", params }); +// }; + +// 查询用户详情 +export const getUser = (id: number) => { + return request("/system/user/get", { + method: "GET", + params: { id }, + }); +}; + +// export const getUser = (id: number) => { +// return request.get({ url: "/system/user/get?id=" + id }); +// }; + +// 新增用户 +export const createUser = (data: UserVO) => { + return request("/system/user/create", { + method: "POST", + data, + }); +}; + +// export const createUser = (data: User) => { +// return request.post({ url: "/system/user/create", data }); +// }; + +// 修改用户 + +export const updateUser = (data: UserVO) => { + return request("/system/user/update", { + method: "PUT", + data, + }); +}; +// export const updateUser = (data: User) => { +// return request.put({ url: "/system/user/update", data }); +// }; + +// 删除用户 + +export const deleteUser = (id: number) => { + return request("/system/user/delete", { + method: "DELETE", + params: { id }, + }); +}; +// export const deleteUser = (id: number) => { +// return request.delete({ url: "/system/user/delete?id=" + id }); +// }; + +// 用户密码重置 + +export const resetUserPassword = (id: number, password: string) => { + return request("/system/user/reset-password", { + method: "PUT", + params: { id, password }, + }); +}; +// export const resetUserPassword = (id: number, password: string) => { +// const data = { +// id, +// password +// }; +// return request.put({ url: "/system/user/update-password", data: data }); +// }; + +// 用户状态修改 +export const updateUserStatus = (id: number, status: number) => { + return request("/system/user/update-status", { + method: "PUT", + data: { id, status }, + }); +}; +// export const updateUserStatus = (id: number, status: number) => { +// const data = { +// id, +// status +// }; +// return request.put({ url: "/system/user/update-status", data: data }); +// }; + +// 获取用户精简信息列表 +export const getSimpleUserList = () => { + return request("/system/user/simple-list", { + method: "GET", + }); +}; +// export const getSimpleUserList = (): Promise => { +// return request.get({ url: "/system/user/simple-list" }); +// }; diff --git a/src/types/constant.ts b/src/types/constant.ts new file mode 100644 index 0000000..d09232a --- /dev/null +++ b/src/types/constant.ts @@ -0,0 +1,5 @@ +export type Operations = { + handleSearch?: (evt: MouseEvent) => void; + handleReset?: (evt: MouseEvent) => void; + handleExport?: (evt: MouseEvent) => void; +}; diff --git a/src/utils/constant.ts b/src/utils/constant.ts new file mode 100644 index 0000000..3827781 --- /dev/null +++ b/src/utils/constant.ts @@ -0,0 +1,22 @@ +// 创建 value 到 label 的映射 +const statusMap = (status: { label: string; value: number }[]) => + new Map(status?.map((item) => [item.value, item.label])); + +const packageMap = (status: { id: number; name: string }[]) => + new Map(status?.map((item) => [item.id, item.name])); + +// 获取 label 的函数 +export const getStatusLabel = ( + status: { label: string; value: number }[], + value: number, +): string => { + return statusMap(status).get(value) || ''; +}; + +// 获取 label 的函数 +export const getpackageLabel = ( + status: { id: number; name: string }[], + id: number, +): string => { + return packageMap(status).get(id) || ''; +}; diff --git a/src/utils/tree.ts b/src/utils/tree.ts new file mode 100644 index 0000000..6dc18aa --- /dev/null +++ b/src/utils/tree.ts @@ -0,0 +1,451 @@ +interface TreeHelperConfig { + id: string; + children: string; + pid: string; +} + +const DEFAULT_CONFIG: TreeHelperConfig = { + id: 'id', + children: 'children', + pid: 'pid', +}; +export const defaultProps = { + children: 'children', + label: 'name', + value: 'id', + isLeaf: 'leaf', + emitPath: false, // 用于 cascader 组件:在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值 +}; + +const getConfig = (config: Partial) => + Object.assign({}, DEFAULT_CONFIG, config); + +// tree from list +export const listToTree = ( + list: any[], + config: Partial = {}, +): T[] => { + const conf = getConfig(config) as TreeHelperConfig; + const nodeMap = new Map(); + const result: T[] = []; + const { id, children, pid } = conf; + + for (const node of list) { + node[children] = node[children] || []; + nodeMap.set(node[id], node); + } + for (const node of list) { + const parent = nodeMap.get(node[pid]); + (parent ? parent.children : result).push(node); + } + return result; +}; + +export const treeToList = ( + tree: any, + config: Partial = {}, +): T => { + config = getConfig(config); + const { children } = config; + const result: any = [...tree]; + for (let i = 0; i < result.length; i++) { + if (!children) return result; + if (!result[i][children]) continue; + result.splice(i + 1, 0, ...result[i][children]); + } + return result; +}; + +export const findNode = ( + tree: any, + func: Fn, + config: Partial = {}, +): T | null => { + config = getConfig(config); + const { children } = config; + const list = [...tree]; + if (!children) return null; + for (const node of list) { + if (func(node)) return node; + node[children] && list.push(...node[children]); + } + return null; +}; + +export const findNodeAll = ( + tree: any, + func: Fn, + config: Partial = {}, +): T[] => { + config = getConfig(config); + const { children } = config; + const list = [...tree]; + const result: T[] = []; + if (!children) return result; + for (const node of list) { + func(node) && result.push(node); + node[children] && list.push(...node[children]); + } + return result; +}; + +export const findPath = ( + tree: any, + func: Fn, + config: Partial = {}, +): T | T[] | null => { + config = getConfig(config); + const path: T[] = []; + const list = [...tree]; + const visitedSet = new Set(); + const { children } = config; + if (!children) return null; + while (list.length) { + const node = list[0]; + if (visitedSet.has(node)) { + path.pop(); + list.shift(); + } else { + visitedSet.add(node); + node[children] && list.unshift(...node[children]); + path.push(node); + if (func(node)) { + return path; + } + } + } + return null; +}; + +export const findPathAll = ( + tree: any, + func: Fn, + config: Partial = {}, +) => { + config = getConfig(config); + const path: any[] = []; + const list = [...tree]; + const result: any[] = []; + const visitedSet = new Set(), + { children } = config; + + if (!children) return []; + while (list.length) { + const node = list[0]; + if (visitedSet.has(node)) { + path.pop(); + list.shift(); + } else { + visitedSet.add(node); + node[children] && list.unshift(...node[children]); + path.push(node); + func(node) && result.push([...path]); + } + } + return result; +}; + +export const filter = ( + tree: T[], + func: (n: T) => boolean, + config: Partial = {}, +): T[] => { + config = getConfig(config); + const children = config.children as string; + + function listFilter(list: T[]) { + return list + .map((node: any) => ({ ...node })) + .filter((node) => { + node[children] = node[children] && listFilter(node[children]); + return func(node) || node[children]?.length; + }); + } + + return listFilter(tree); +}; + +export const forEach = ( + tree: T[], + func: (n: T) => any, + config: Partial = {}, +): void => { + config = getConfig(config); + const list: any[] = [...tree]; + const { children } = config; + for (let i = 0; i < list.length; i++) { + // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿 + if (func(list[i])) { + return; + } + children && + list[i][children] && + list.splice(i + 1, 0, ...list[i][children]); + } +}; + +/** + * @description: Extract tree specified structure + */ +export const treeMap = ( + treeData: T[], + opt: { children?: string; conversion: Fn }, +): T[] => { + return treeData.map((item) => treeMapEach(item, opt)); +}; + +/** + * @description: Extract tree specified structure + */ +export const treeMapEach = ( + data: any, + { children = 'children', conversion }: { children?: string; conversion: Fn }, +) => { + const haveChildren = + Array.isArray(data[children]) && data[children].length > 0; + const conversionData = conversion(data) || {}; + if (haveChildren) { + return { + ...conversionData, + [children]: data[children].map((i: number) => + treeMapEach(i, { + children, + conversion, + }), + ), + }; + } else { + return { + ...conversionData, + }; + } +}; + +/** + * 递归遍历树结构 + * @param treeDatas 树 + * @param callBack 回调 + * @param parentNode 父节点 + */ +export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => { + treeDatas.forEach((element) => { + const newNode = callBack(element, parentNode) || element; + if (element.children) { + eachTree(element.children, callBack, newNode); + } + }); +}; + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export const handleTree = ( + data: any[], + id?: string, + parentId?: string, + children?: string, +) => { + if (!Array.isArray(data)) { + console.warn('data must be an array'); + return []; + } + const config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children', + }; + + const childrenListMap: any = {}; + const nodeIds: any = {}; + const tree: any[] = []; + + for (const d of data) { + const parentId = d[config.parentId]; + if (childrenListMap[parentId] == null) { + childrenListMap[parentId] = []; + } + nodeIds[d[config.id]] = d; + childrenListMap[parentId].push(d); + } + + for (const d of data) { + const parentId = d[config.parentId]; + if (nodeIds[parentId] == null) { + tree.push(d); + } + } + + for (const t of tree) { + adaptToChildrenList(t); + } + + function adaptToChildrenList(o: { [x: string]: any }) { + if (childrenListMap[o[config.id]] !== null) { + o[config.childrenList] = childrenListMap[o[config.id]]; + } + if (o[config.childrenList]) { + for (const c of o[config.childrenList]) { + adaptToChildrenList(c); + } + } + } + + return tree; +}; + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + * @param {*} rootId 根Id 默认 0 + */ +// @ts-expect-error +export const handleTree2 = (data, id, parentId, _children, rootId) => { + id = id || 'id'; + parentId = parentId || 'parentId'; + // children = children || 'children' + rootId = + rootId || + Math.min( + ...data.map((item: { [x: string]: any }) => { + return item[parentId]; + }), + ) || + 0; + // 对源数据深度克隆 + const cloneData = JSON.parse(JSON.stringify(data)); + // 循环所有项 + const treeData = cloneData.filter( + (father: { [x: string]: any; children: any }) => { + const branchArr = cloneData.filter((child: { [x: string]: any }) => { + // 返回每一项的子级数组 + return father[id] === child[parentId]; + }); + + if (branchArr.length > 0) { + father.children = branchArr; + } + // 返回第一层 + return father[parentId] === rootId; + }, + ); + return treeData !== '' ? treeData : data; +}; + +/** + * 校验选中的节点,是否为指定 level + * + * @param tree 要操作的树结构数据 + * @param nodeId 需要判断在什么层级的数据 + * @param level 检查的级别, 默认检查到二级 + * @return true 是;false 否 + */ +export const checkSelectedNode = ( + tree: any[], + nodeId: any, + level = 2, +): boolean => { + if ( + typeof tree === 'undefined' || + !Array.isArray(tree) || + tree.length === 0 + ) { + console.warn('tree must be an array'); + return false; + } + + // 校验是否是一级节点 + if (tree.some((item) => item.id === nodeId)) { + return false; + } + + // 递归计数 + let count = 1; + + // 深层次校验 + function performAThoroughValidation(arr: any[]): boolean { + count += 1; + for (const item of arr) { + if (item.id === nodeId) { + return true; + } else if ( + typeof item.children !== 'undefined' && + item.children.length !== 0 + ) { + if (performAThoroughValidation(item.children)) { + return true; + } + } + } + return false; + } + + for (const item of tree) { + count = 1; + if (performAThoroughValidation(item.children)) { + // 找到后对比是否是期望的层级 + if (count >= level) { + return true; + } + } + } + + return false; +}; + +/** + * 获取节点的完整结构 + * @param tree 树数据 + * @param nodeId 节点 id + */ +export const treeToString = (tree: any[], nodeId: number) => { + if ( + typeof tree === 'undefined' || + !Array.isArray(tree) || + tree.length === 0 + ) { + console.warn('tree must be an array'); + return ''; + } + // 校验是否是一级节点 + const node = tree.find((item) => item.id === nodeId); + if (typeof node !== 'undefined') { + return node.name; + } + let str = ''; + + function performAThoroughValidation(arr: string | any[]) { + if (typeof arr === 'undefined' || !Array.isArray(arr) || arr.length === 0) { + return false; + } + for (const item of arr) { + if (item.id === nodeId) { + str += ` / ${item.name}`; + return true; + } else if ( + typeof item.children !== 'undefined' && + item.children.length !== 0 + ) { + str += ` / ${item.name}`; + if (performAThoroughValidation(item.children)) { + return true; + } + } + } + return false; + } + + for (const item of tree) { + str = `${item.name}`; + if (performAThoroughValidation(item.children)) { + break; + } + } + return str; +};