diff --git a/src/models/user.data.ts b/src/models/user.data.ts index 2659b6e..44a0230 100644 --- a/src/models/user.data.ts +++ b/src/models/user.data.ts @@ -71,7 +71,7 @@ export interface Menu { sort?: number; status?: number; parentPaths?: string[]; - createTime: number; + createTime?: number; children?: Array; } diff --git a/src/pages/custom/product/finished/index.tsx b/src/pages/custom/product/finished/index.tsx index 6d1aba6..748c28c 100644 --- a/src/pages/custom/product/finished/index.tsx +++ b/src/pages/custom/product/finished/index.tsx @@ -3,7 +3,7 @@ import type { TableColumnsType, DatePickerProps } from 'antd'; import { t } from '@/utils/i18n'; import React, { useState } from 'react'; import mData from '../../../../../mock/productListPage.json' -import { PlusOutlined, ExclamationCircleFilled, SearchOutlined,UndoOutlined } from '@ant-design/icons'; +import { PlusOutlined, ExclamationCircleFilled, SearchOutlined, UndoOutlined } from '@ant-design/icons'; import type { Product, ProductBase, ProductImage, ProductSku } from '@/models' import { antdUtils } from '@/utils/antd'; import customParseFormat from 'dayjs/plugin/customParseFormat'; @@ -236,19 +236,19 @@ const FinishedProductPage: React.FC = () => {
- + - + - - - + + +
diff --git a/src/pages/custom/product/sample/index.tsx b/src/pages/custom/product/sample/index.tsx index 9aea3eb..c4a18d2 100644 --- a/src/pages/custom/product/sample/index.tsx +++ b/src/pages/custom/product/sample/index.tsx @@ -8,7 +8,14 @@ import MaskPictureEditor from './components/mask-picture-editor'; import type { SampleAttribute } from './components/attr-editor' import type { MaskPicture } from './components/mask-picture-editor'; import { useNavigate } from 'react-router-dom'; -import { ExclamationCircleFilled, PlusOutlined, DeleteOutlined, CarryOutOutlined } from '@ant-design/icons'; +import { + ExclamationCircleFilled, + PlusOutlined, + DeleteOutlined, + CarryOutOutlined, + SearchOutlined, + UndoOutlined +} from '@ant-design/icons'; import { antdUtils } from '@/utils/antd'; const { Search } = Input; @@ -341,7 +348,7 @@ const TablePage: React.FC = () => { return (
- +
{ placeholder="请选择类目" /> - - + placeholder='请输入样机名称' /> + + + +
diff --git a/src/pages/custom/product/shape/index.tsx b/src/pages/custom/product/shape/index.tsx index ed26c08..b901e02 100644 --- a/src/pages/custom/product/shape/index.tsx +++ b/src/pages/custom/product/shape/index.tsx @@ -4,7 +4,13 @@ import { t } from '@/utils/i18n'; import React, { useState } from 'react'; import mData from '../../../../../mock/findDiySpecimenPage.json' import shapeData from '../../../../../mock/findSpecimensByPrototypeId.json' -import { PlusOutlined, ExclamationCircleFilled, CarryOutOutlined } from '@ant-design/icons'; +import { + PlusOutlined, + ExclamationCircleFilled, + CarryOutOutlined, + SearchOutlined, + UndoOutlined +} from '@ant-design/icons'; import type { ShapeProperty, Shape, ProductImage } from '@/models' import { antdUtils } from '@/utils/antd'; @@ -196,8 +202,10 @@ const ShapePage: React.FC = () => { /> - - + + + +
diff --git a/src/pages/system/menu/index.tsx b/src/pages/system/menu/index.tsx index 5184d77..fe6c702 100644 --- a/src/pages/system/menu/index.tsx +++ b/src/pages/system/menu/index.tsx @@ -1,4 +1,4 @@ -import { Space, Table, Button, Image, Divider, Tag, Card, Badge } from 'antd'; +import { Space, Table, Button, Input, Select, Divider, Tag, Card, Badge, Form } from 'antd'; import type { TableColumnsType } from 'antd'; import { t } from '@/utils/i18n'; import React, { useState, useEffect } from 'react'; @@ -6,22 +6,38 @@ import { PlusOutlined, ExclamationCircleFilled, SearchOutlined, UndoOutlined } f import type { Menu } from '@/models' import { antdUtils } from '@/utils/antd'; import { useRequest } from '@/hooks/use-request'; -import menuService from '@/request/service/menu' +import menuService from '@/request/service/menu'; import { formatDate } from '@/utils/formatTime' +import MenuEditor from './menu-editor'; + +import * as Icon from "@ant-design/icons"; +// 编写生成ReactNode的方法 +const icon2Element = (name: string) => + React.createElement(Icon && (Icon as any)[name], { + style: { fontSize: '16px' } +}) const MenuListPage: React.FC = () => { + const [menuEditorVisable, seMenuEditorVisable] = useState(false); + const [editMenu, seEditMenu] = useState(); const [dataSource, setDataSource] = useState([]); + const [searchFrom] = Form.useForm(); + + console.log() + const { runAsync: getMenuList } = useRequest(menuService.getMenuList, { manual: true }); + const { runAsync: deleteMenu } = useRequest(menuService.deleteMenu, { manual: true }); + const loadMenus = async () => { - const [error, { data: menus }] = await getMenuList(); + const [error, { data: menus }] = await getMenuList(searchFrom.getFieldsValue()); if (!error) { const menuIds = menus.map(it => it.id) - const rootMenus = menus.filter((menu) => menuIds.indexOf(menu.parentId??0) < 0) + const rootMenus = menus.filter((menu) => menuIds.indexOf(menu.parentId ?? 0) < 0) menus.forEach(menu => { const parentMenu = menus.find(it => it.id === menu.parentId); //menu.parentId - if(parentMenu) { - if(!parentMenu.children) { + if (parentMenu) { + if (!parentMenu.children) { parentMenu.children = [] } parentMenu.children.push(menu) @@ -32,7 +48,7 @@ const MenuListPage: React.FC = () => { }; - const showDeleteConfirm = () => { + const showDeleteConfirm = (menu: Menu) => { antdUtils.modal?.confirm({ title: '确认要将该菜单删除吗?', icon: , @@ -41,8 +57,15 @@ const MenuListPage: React.FC = () => { okType: 'danger', cancelText: '取消', onOk() { - return new Promise((resolve, reject) => { - + return new Promise(async (resolve) => { + const [error, { code, msg} ] = await deleteMenu({ id: menu.id }); + if(error || code !== 0) { + antdUtils.message?.open({ type: 'error', content: msg??'操作失败'}) + } else { + antdUtils.message?.open({ type: 'success', content: '删除成功'}) + } + await loadMenus(); + resolve('') }).catch(() => antdUtils.message?.open({ type: 'error', content: '操作失败', @@ -67,7 +90,7 @@ const MenuListPage: React.FC = () => { key: 'type', width: 100, render: (value: number, record, index) => { - return (value === 1 ? 目录 : (value === 2 ?菜单: 按钮)) + return (value === 1 ? 目录 : (value === 2 ? 菜单 : 按钮)) } }, { @@ -99,7 +122,7 @@ const MenuListPage: React.FC = () => { dataIndex: 'visible', width: 100, render: (value: boolean) => { - return (value? : ) + return (value ? : ) } }, { @@ -116,26 +139,23 @@ const MenuListPage: React.FC = () => { key: 'createTime', dataIndex: 'createTime', width: 200, - render:(value: number) => { + render: (value: number) => { return formatDate(new Date(value), "YYYY-mm-dd HH:MM:SS") } }, { title: t("QkOmYwne" /* 操作 */), key: 'action', - render: (_, record) => ( - - )}> - { - + render: (menu: Menu, record) => ( + )}> + { + seEditMenu(menu); + seMenuEditorVisable(true); }}> 编辑 - { - showDeleteConfirm() + { + showDeleteConfirm(menu) }}> 删除 @@ -148,9 +168,42 @@ const MenuListPage: React.FC = () => { loadMenus(); }, []); + const onReset = () => { + searchFrom.resetFields() + loadMenus() + } + return ( <>
+ +
+
+
+ + + + + + +
+ + + + +
+
+ +
+
+
{ pagination={{ position: ['bottomRight'] }} /> + { + loadMenus(); + seMenuEditorVisable(false); + }} + onCancel={() => { seMenuEditorVisable(false) }} + visible={menuEditorVisable} + editData={editMenu} /> ); }; diff --git a/src/pages/system/menu/menu-editor.tsx b/src/pages/system/menu/menu-editor.tsx new file mode 100644 index 0000000..18e2dc4 --- /dev/null +++ b/src/pages/system/menu/menu-editor.tsx @@ -0,0 +1,186 @@ +import React, { useEffect, useState } from 'react' +import { CloseOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons'; +import { TreeSelect, Form, Input, InputNumber, Radio, Modal, Switch } from 'antd' +import menuService from '@/request/service/menu'; +import { useRequest } from '@/hooks/use-request'; +import type { DefaultOptionType } from 'antd/es/select'; +import type { Menu } from '@/models' +import { antdUtils } from '@/utils/antd'; + +const layout = { + labelCol: { span: 4, }, + wrapperCol: { span: 16 }, + bordered: false, +}; + +interface MenuEditorProps { + visible: boolean; + onCancel: (flag?: boolean) => void; + onSave: (menu: Menu) => void; + editData?: Menu | null; + parentMenus: Menu[]; +} + + +const MenuEditor: React.FC = (props) => { + + const { visible, onCancel, onSave, editData, parentMenus } = props; + + const { runAsync: updateMenu } = useRequest(menuService.updateMenu, { manual: true }); + const { runAsync: createMenu } = useRequest(menuService.createMenu, { manual: true }); + + const isEdit = !!editData; + + const menu2Tree = (menu: Menu) => { + const treeItem: DefaultOptionType = { + label: menu.name, + value: menu.id + } + if (menu.children && menu.children.length > 0) { + treeItem.children = menu.children.filter(item => item.type !== 3).map(menu2Tree) + } + + return treeItem; + } + const menuTree: DefaultOptionType[] = [{ + label: '主类目', + value: 0, + children: parentMenus.filter(item => item.type !== 3).map(item => { + return menu2Tree(item) + }) + }] + + const [saveLoading, setSaveLoading] = useState(false); + const [form] = Form.useForm(); + + useEffect(() => { + if (visible) { + if (editData) { + form.setFieldsValue(editData); + } + } else { + form.resetFields(); + } + }, [visible]); + + const save = async () => { + setSaveLoading(true); + const menuValues = form.getFieldsValue(); + if(typeof(menuValues.status) === 'boolean') { + menuValues.status = menuValues.status? 0 : 1 + } + const newValue = isEdit? {...editData, ...menuValues} : { + permission: '', + icon: '', + keepAlive:true, + createTime: new Date(), + ...menuValues, + } + const [error, { msg, code }] = isEdit? await updateMenu(newValue): await createMenu(newValue); + if(!error && code === 0) { + onSave(newValue); + } else { + antdUtils.message?.open({ + type: 'error', + content: msg??'操作失败', + }); + } + setSaveLoading(false); + } + + return ( + <> + onCancel()} + destroyOnClose + > +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + // const checked = value !== 0; + // console.log(checked) + // return { checked } + // }} + valuePropName="checked" + // normalize={(value: boolean) => { + // // prevValues.status = value ? 0 : 1 + // return value ? 0 : 1 + // }} + > + + + + + + + + {/* + + */} + +
+ + ) +} + +export default MenuEditor; diff --git a/src/request/service/menu.ts b/src/request/service/menu.ts index 829e00e..fab76d6 100644 --- a/src/request/service/menu.ts +++ b/src/request/service/menu.ts @@ -1,8 +1,28 @@ import request from '@/request'; import { Menu } from '@/models'; +const BASE_URL = '/admin-api/system/menu'; + export default { - getMenuList: (params?: {name?: string, status?: number}) => { - return request.get('/admin-api/system/menu/list', {params}); + getMenuList: (params?: { name?: string, status?: number }) => { + return request.get(`${BASE_URL}/list`, { params }); + }, + // 获取菜单详情 + getMenu: (id: number) => { + return request.get(`${BASE_URL}//menu/get`, {params: { id}}) + }, + + // 新增菜单 + createMenu: (data: Menu) => { + return request.post(`${BASE_URL}/create`, data) }, + + // 修改菜单 + updateMenu: (data: Menu) => { + return request.put(`${BASE_URL}/update`, data) + }, + + deleteMenu: (params: { id: number }) => { + return request.delete(`${BASE_URL}/delete`, { params }) + } };