|
|
@@ -1,9 +1,8 @@
|
|
|
import React, { useState, useEffect, useImperativeHandle, forwardRef } from 'react';
|
|
|
-import { menuApi, MenuVO, MenuType, MenuStatus } from '../api/Menu';
|
|
|
+import { menuApi, MenuVO, MenuType } from '../api/Menu';
|
|
|
import { handleTree, TreeNode } from '../utils/tree';
|
|
|
import { toast } from 'sonner';
|
|
|
import {
|
|
|
- Maximize2, HelpCircle, ChevronDown, Check,
|
|
|
FileText, Folder, Menu, Settings, User, Users,
|
|
|
Lock, Unlock, Search, Edit, Trash2, Plus, Minus,
|
|
|
CheckCircle, X, AlertTriangle, Info, Bell, MessageSquare,
|
|
|
@@ -12,11 +11,8 @@ import {
|
|
|
ShoppingBag, Package, Database, PieChart, BarChart3,
|
|
|
Grid3x3, List, Link2, Key, Wrench, Monitor, Server
|
|
|
} from 'lucide-react';
|
|
|
-import { Input } from './ui/input';
|
|
|
-import { Label } from './ui/label';
|
|
|
-import { Switch } from './ui/switch';
|
|
|
-import { Popover, PopoverTrigger, PopoverContent } from './ui/popover';
|
|
|
-import { Tabs, TabsList, TabsTrigger } from './ui/tabs';
|
|
|
+import { Modal, Input, Button, Radio, TreeSelect, Popover, Space } from 'antd';
|
|
|
+import { QuestionCircleOutlined } from '@ant-design/icons';
|
|
|
|
|
|
interface MenuNode extends Omit<MenuVO, 'id'>, Omit<TreeNode, 'id'> {
|
|
|
id: number;
|
|
|
@@ -37,7 +33,6 @@ const MenuForm = forwardRef<MenuFormRef, MenuFormProps>(({ onSuccess }, ref) =>
|
|
|
const [formType, setFormType] = useState<'create' | 'update'>('create');
|
|
|
const [formLoading, setFormLoading] = useState(false);
|
|
|
const [menuTree, setMenuTree] = useState<MenuNode[]>([]);
|
|
|
- const [popoverOpen, setPopoverOpen] = useState(false);
|
|
|
const [iconPickerOpen, setIconPickerOpen] = useState(false);
|
|
|
const [formData, setFormData] = useState<MenuVO>({
|
|
|
name: '',
|
|
|
@@ -208,24 +203,6 @@ const MenuForm = forwardRef<MenuFormRef, MenuFormProps>(({ onSuccess }, ref) =>
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- // 获取当前选中菜单的名称
|
|
|
- const getSelectedMenuName = (): string => {
|
|
|
- if (!formData.parentId || formData.parentId === 0) return '主类目';
|
|
|
-
|
|
|
- const findMenuName = (nodes: MenuNode[], targetId: number): string | null => {
|
|
|
- for (const node of nodes) {
|
|
|
- if (node.id === targetId) return node.name;
|
|
|
- if (node.children) {
|
|
|
- const found = findMenuName(node.children, targetId);
|
|
|
- if (found) return found;
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- };
|
|
|
-
|
|
|
- return findMenuName(menuTree, formData.parentId) || '主类目';
|
|
|
- };
|
|
|
-
|
|
|
// ElementPlus 图标列表(显示 React 图标,保存 ElementPlus 格式)
|
|
|
const elementPlusIcons = [
|
|
|
{ name: 'ep:document', label: '文档', icon: FileText },
|
|
|
@@ -277,549 +254,311 @@ const MenuForm = forwardRef<MenuFormRef, MenuFormProps>(({ onSuccess }, ref) =>
|
|
|
{ name: 'ep:database', label: '数据库', icon: Database },
|
|
|
];
|
|
|
|
|
|
- // 递归渲染菜单树选项(自定义组件)
|
|
|
- const renderMenuTreeOptions = (nodes: MenuNode[], level: number = 0, onSelect: (id: number) => void): React.ReactNode => {
|
|
|
- return nodes.map(node => {
|
|
|
- if (node.id === undefined || node.id === null) return null;
|
|
|
- const idValue = node.id;
|
|
|
- if (idValue === undefined) return null;
|
|
|
-
|
|
|
- const indentWidth = level * 20;
|
|
|
- const hasChildren = node.children && node.children.length > 0;
|
|
|
- const isSelected = formData.parentId === idValue;
|
|
|
- const isDisabled = node.id === formData.id;
|
|
|
-
|
|
|
- return (
|
|
|
- <React.Fragment key={node.id}>
|
|
|
- <div
|
|
|
- onClick={() => {
|
|
|
- if (!isDisabled) {
|
|
|
- onSelect(idValue);
|
|
|
- }
|
|
|
- }}
|
|
|
- className={`
|
|
|
- flex items-center justify-between px-3 py-2 text-sm cursor-pointer
|
|
|
- transition-colors select-none
|
|
|
- ${isDisabled
|
|
|
- ? 'opacity-50 cursor-not-allowed'
|
|
|
- : 'hover:bg-blue-50 active:bg-blue-100'
|
|
|
- }
|
|
|
- ${isSelected
|
|
|
- ? 'bg-blue-50 text-blue-600 font-semibold'
|
|
|
- : 'text-gray-700'
|
|
|
- }
|
|
|
- `}
|
|
|
- style={{
|
|
|
- paddingLeft: `${12 + indentWidth}px`,
|
|
|
- }}
|
|
|
- >
|
|
|
- {/* 文本区域:左侧层级线 + 菜单名 */}
|
|
|
- <div className="flex items-center gap-1.5 flex-1 min-w-0">
|
|
|
- {level > 0 && (
|
|
|
- <span className="flex-shrink-0 text-gray-400 text-xs font-mono">│</span>
|
|
|
- )}
|
|
|
- <span className="flex-1 min-w-0 truncate">{node.name}</span>
|
|
|
- </div>
|
|
|
- {/* 自定义选中标记 */}
|
|
|
- {isSelected && !isDisabled && (
|
|
|
- <Check className="w-4 h-4 text-blue-600 ml-2 flex-shrink-0" />
|
|
|
- )}
|
|
|
- </div>
|
|
|
-
|
|
|
- {hasChildren && node.children && renderMenuTreeOptions(node.children, level + 1, onSelect)}
|
|
|
- </React.Fragment>
|
|
|
- );
|
|
|
- }).filter(Boolean);
|
|
|
+ // 将菜单树转换为 TreeSelect 需要的格式
|
|
|
+ const convertToTreeSelectData = (nodes: MenuNode[]): any[] => {
|
|
|
+ return nodes.map(node => ({
|
|
|
+ title: node.name,
|
|
|
+ value: node.id,
|
|
|
+ key: node.id,
|
|
|
+ disabled: node.id === formData.id,
|
|
|
+ children: node.children ? convertToTreeSelectData(node.children) : undefined,
|
|
|
+ }));
|
|
|
};
|
|
|
- if (!dialogVisible) return null;
|
|
|
|
|
|
return (
|
|
|
- <div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-[9999] animate-in fade-in duration-200 p-4">
|
|
|
- <div className="bg-white rounded-xl shadow-2xl w-full max-w-4xl animate-in zoom-in duration-200 relative z-[10000] max-h-[85vh] flex flex-col overflow-hidden">
|
|
|
- {/* 弹窗标题 */}
|
|
|
- <div className="px-5 py-3 border-b border-gray-200 flex items-center justify-between shrink-0">
|
|
|
- <h3 className="text-base text-gray-900">{dialogTitle}</h3>
|
|
|
- <div className="flex items-center gap-2">
|
|
|
- <button className="p-2 hover:bg-gray-100 rounded-lg transition-colors">
|
|
|
- <Maximize2 className="w-4 h-4 text-gray-600" />
|
|
|
- </button>
|
|
|
- <button
|
|
|
- onClick={() => setDialogVisible(false)}
|
|
|
- className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
|
|
+ <Modal
|
|
|
+ title={dialogTitle}
|
|
|
+ open={dialogVisible}
|
|
|
+ onCancel={() => setDialogVisible(false)}
|
|
|
+ width={600}
|
|
|
+ footer={[
|
|
|
+ <Button key="cancel" onClick={() => setDialogVisible(false)}>
|
|
|
+ 取消
|
|
|
+ </Button>,
|
|
|
+ <Button
|
|
|
+ key="submit"
|
|
|
+ type="primary"
|
|
|
+ loading={formLoading}
|
|
|
+ onClick={submitForm}
|
|
|
+ >
|
|
|
+ 确定
|
|
|
+ </Button>,
|
|
|
+ ]}
|
|
|
+ destroyOnClose
|
|
|
+ >
|
|
|
+ {formLoading && formType === 'update' ? (
|
|
|
+ <div className="py-6 text-center text-gray-500">加载中...</div>
|
|
|
+ ) : (
|
|
|
+ <div style={{ padding: '8px 0' }}>
|
|
|
+ {/* 上级菜单 */}
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>上级菜单</span>
|
|
|
+ <TreeSelect
|
|
|
+ style={{ flex: 1 }}
|
|
|
+ value={formData.parentId || 0}
|
|
|
+ treeData={convertToTreeSelectData(menuTree)}
|
|
|
+ placeholder="请选择上级菜单"
|
|
|
+ onChange={(value) => setFormData(prev => ({ ...prev, parentId: value as number }))}
|
|
|
+ disabled={!menuTree || menuTree.length === 0}
|
|
|
+ treeDefaultExpandAll
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 菜单名称 */}
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ <span className="text-red-500 mr-1">*</span>
|
|
|
+ 菜单名称
|
|
|
+ </span>
|
|
|
+ <Input
|
|
|
+ style={{ flex: 1 }}
|
|
|
+ value={formData.name}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
|
|
+ placeholder="请输入菜单名称"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 菜单类型 */}
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ <span className="text-red-500 mr-1">*</span>
|
|
|
+ 菜单类型
|
|
|
+ </span>
|
|
|
+ <Radio.Group
|
|
|
+ value={formData.type}
|
|
|
+ onChange={(e) => {
|
|
|
+ setFormData(prev => ({ ...prev, type: e.target.value }));
|
|
|
+ }}
|
|
|
+ buttonStyle="solid"
|
|
|
+ optionType="button"
|
|
|
>
|
|
|
- <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
|
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
|
- </svg>
|
|
|
- </button>
|
|
|
+ <Radio.Button value={MenuType.DIR}>目录</Radio.Button>
|
|
|
+ <Radio.Button value={MenuType.MENU}>菜单</Radio.Button>
|
|
|
+ <Radio.Button value={MenuType.BUTTON}>按钮</Radio.Button>
|
|
|
+ </Radio.Group>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- {/* 弹窗内容 */}
|
|
|
- <div className="px-5 py-3 overflow-y-auto overflow-x-hidden flex-1 min-h-0">
|
|
|
- {formLoading ? (
|
|
|
- <div className="py-6 text-center text-gray-500">加载中...</div>
|
|
|
- ) : (
|
|
|
- <div className="grid grid-cols-2 gap-x-32 gap-y-2.5">
|
|
|
- {/* 上级菜单 - 占满整行(优化版) */}
|
|
|
-<div className="col-span-2 grid grid-cols-[100px_1fr] items-center gap-3 min-h-[40px]">
|
|
|
- <label className="text-sm text-gray-700 whitespace-nowrap">上级菜单</label>
|
|
|
- {/* 外层包裹确保下拉容器定位正确 */}
|
|
|
- <div className="relative w-full">
|
|
|
- <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>
|
|
|
- <PopoverTrigger asChild>
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- disabled={!menuTree || menuTree.length === 0}
|
|
|
- className="w-full h-[36px] bg-white border border-gray-200 focus:border-blue-400 focus:ring-2 focus:ring-blue-100
|
|
|
- rounded-md transition-all outline-none px-3 text-left flex items-center justify-between
|
|
|
- disabled:opacity-50 disabled:cursor-not-allowed hover:border-gray-300"
|
|
|
- >
|
|
|
- <span className={formData.parentId === 0 ? 'text-gray-500' : 'text-gray-900'}>
|
|
|
- {getSelectedMenuName()}
|
|
|
- </span>
|
|
|
- <ChevronDown className={`w-4 h-4 text-gray-400 transition-transform ${popoverOpen ? 'rotate-180' : ''}`} />
|
|
|
- </button>
|
|
|
- </PopoverTrigger>
|
|
|
- <PopoverContent
|
|
|
- className="p-0 border-gray-200 shadow-md bg-white rounded-md"
|
|
|
- align="start"
|
|
|
- side="bottom"
|
|
|
- sideOffset={4}
|
|
|
- style={{ width: 'var(--radix-popover-trigger-width)' }}
|
|
|
- >
|
|
|
- <div
|
|
|
- className="max-h-[200px] overflow-y-auto"
|
|
|
- style={{ maxHeight: '200px' }}
|
|
|
- >
|
|
|
- {/* 空数据兜底 */}
|
|
|
- {!menuTree || menuTree.length === 0 ? (
|
|
|
- <div className="px-3 py-2 text-sm text-gray-400">暂无可选菜单</div>
|
|
|
- ) : (
|
|
|
- renderMenuTreeOptions(menuTree, 0, (id: number) => {
|
|
|
- setFormData(prev => ({ ...prev, parentId: id }));
|
|
|
- setPopoverOpen(false);
|
|
|
- })
|
|
|
- )}
|
|
|
- </div>
|
|
|
- </PopoverContent>
|
|
|
- </Popover>
|
|
|
- </div>
|
|
|
-</div>
|
|
|
-
|
|
|
- {/* 菜单名称 */}
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700">
|
|
|
- <span className="text-red-500 mr-1">*</span>
|
|
|
- 菜单名称
|
|
|
- </label>
|
|
|
+ {/* 菜单图标 */}
|
|
|
+ {formData.type !== MenuType.BUTTON && (
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>菜单图标</span>
|
|
|
+ <Space.Compact style={{ flex: 1 }}>
|
|
|
<Input
|
|
|
- value={formData.name}
|
|
|
- onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
|
|
- placeholder="请输入菜单名称"
|
|
|
- className="bg-white border-gray-200 focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
|
|
|
+ value={formData.icon}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, icon: e.target.value }))}
|
|
|
+ placeholder="请选择或输入图标名称(如:ep:document)"
|
|
|
/>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 菜单类型 */}
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3">
|
|
|
- <label className="text-sm text-gray-700">
|
|
|
- <span className="text-red-500 mr-1">*</span>
|
|
|
- 菜单类型
|
|
|
- </label>
|
|
|
- <div className="flex gap-0">
|
|
|
- <button
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, type: MenuType.DIR }))}
|
|
|
- className={`flex-1 px-3 py-1.5 text-xs border border-r-0 rounded-l-lg transition-all ${
|
|
|
- formData.type === MenuType.DIR
|
|
|
- ? 'bg-blue-500 text-white border-blue-500 z-10'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- 目录
|
|
|
- </button>
|
|
|
- <button
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, type: MenuType.MENU }))}
|
|
|
- className={`flex-1 px-3 py-1.5 text-xs border transition-all ${
|
|
|
- formData.type === MenuType.MENU
|
|
|
- ? 'bg-blue-500 text-white border-blue-500 z-10'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- 菜单
|
|
|
- </button>
|
|
|
- <button
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, type: MenuType.BUTTON }))}
|
|
|
- className={`flex-1 px-3 py-1.5 text-xs border border-l-0 rounded-r-lg transition-all ${
|
|
|
- formData.type === MenuType.BUTTON
|
|
|
- ? 'bg-blue-500 text-white border-blue-500 z-10'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- 按钮
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 菜单图标 */}
|
|
|
- {formData.type !== MenuType.BUTTON && (
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700">菜单图标</label>
|
|
|
- <div className="flex items-center gap-2">
|
|
|
- <Input
|
|
|
- value={formData.icon}
|
|
|
- onChange={(e) => setFormData(prev => ({ ...prev, icon: e.target.value }))}
|
|
|
- placeholder="请选择或输入图标名称(如:ep:document)"
|
|
|
- className="bg-white border-gray-200 focus:border-blue-400 focus:ring-2 focus:ring-blue-100 flex-1"
|
|
|
- />
|
|
|
- <Popover open={iconPickerOpen} onOpenChange={setIconPickerOpen}>
|
|
|
- <PopoverTrigger asChild>
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- className="px-3 h-[36px] bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors text-sm whitespace-nowrap"
|
|
|
- >
|
|
|
- 选择图标
|
|
|
- </button>
|
|
|
- </PopoverTrigger>
|
|
|
- <PopoverContent
|
|
|
- className="w-[400px] p-3 border-gray-200 shadow-md bg-white rounded-md max-h-[85vh]"
|
|
|
- align="end"
|
|
|
- side="bottom"
|
|
|
- sideOffset={4}
|
|
|
+ <Popover
|
|
|
+ content={
|
|
|
+ <div style={{ width: 400 }}>
|
|
|
+ <h4 style={{ marginBottom: 8, fontSize: 14, fontWeight: 600 }}>选择图标</h4>
|
|
|
+ <div
|
|
|
+ style={{
|
|
|
+ display: 'grid',
|
|
|
+ gridTemplateColumns: 'repeat(8, 1fr)',
|
|
|
+ gap: 6,
|
|
|
+ maxHeight: 300,
|
|
|
+ overflowY: 'auto'
|
|
|
+ }}
|
|
|
>
|
|
|
- <div className="flex flex-col">
|
|
|
- <h4 className="text-sm font-semibold text-gray-700 mb-2">选择图标</h4>
|
|
|
- <div
|
|
|
- className="grid gap-1.5 overflow-y-auto"
|
|
|
- style={{
|
|
|
- gridTemplateColumns: 'repeat(8, 1fr)',
|
|
|
- maxHeight: '300px',
|
|
|
- height: '300px'
|
|
|
- }}
|
|
|
- >
|
|
|
- {elementPlusIcons.map((iconItem) => {
|
|
|
- const IconComponent = iconItem.icon;
|
|
|
- const isSelected = formData.icon === iconItem.name;
|
|
|
- return (
|
|
|
- <div
|
|
|
- key={iconItem.name}
|
|
|
- onClick={() => {
|
|
|
- setFormData(prev => ({ ...prev, icon: iconItem.name }));
|
|
|
- setIconPickerOpen(false);
|
|
|
- }}
|
|
|
- className={`
|
|
|
- flex items-center justify-center p-1.5 rounded cursor-pointer
|
|
|
- transition-all border
|
|
|
- ${isSelected
|
|
|
- ? 'bg-blue-50 border-blue-500 shadow-sm'
|
|
|
- : 'bg-white border-gray-200 hover:bg-gray-50 hover:border-blue-300'
|
|
|
- }
|
|
|
- `}
|
|
|
- style={{ aspectRatio: '1' }}
|
|
|
- title={`${iconItem.name} - ${iconItem.label}`}
|
|
|
- >
|
|
|
- <IconComponent
|
|
|
- className={`
|
|
|
- w-3.5 h-3.5
|
|
|
- ${isSelected ? 'text-blue-600' : 'text-gray-600'}
|
|
|
- `}
|
|
|
- />
|
|
|
- </div>
|
|
|
- );
|
|
|
- })}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </PopoverContent>
|
|
|
- </Popover>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 路由地址 */}
|
|
|
- {formData.type !== MenuType.BUTTON && (
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3">
|
|
|
- <label className="text-sm text-gray-700 flex items-center gap-1">
|
|
|
- <span className="text-red-500">*</span>
|
|
|
- 路由地址
|
|
|
- <HelpCircle className="w-3.5 h-3.5 text-gray-400" />
|
|
|
- </label>
|
|
|
- <Input
|
|
|
- value={formData.path}
|
|
|
- onChange={(e) => setFormData(prev => ({ ...prev, path: e.target.value }))}
|
|
|
- placeholder="请输入路由地址"
|
|
|
- className="bg-white border-gray-200 focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
|
|
|
- />
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 组件地址 */}
|
|
|
- {formData.type === MenuType.MENU && (
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700">组件地址</label>
|
|
|
- <Input
|
|
|
- value={formData.component}
|
|
|
- onChange={(e) => setFormData(prev => ({ ...prev, component: e.target.value }))}
|
|
|
- placeholder="例如:system/user/index"
|
|
|
- className="bg-white border-gray-200 focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
|
|
|
- />
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 组件名字 */}
|
|
|
- {formData.type === MenuType.MENU && (
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3">
|
|
|
- <label className="text-sm text-gray-700">组件名字</label>
|
|
|
- <Input
|
|
|
- value={formData.componentName || ''}
|
|
|
- onChange={(e) => setFormData(prev => ({ ...prev, componentName: e.target.value }))}
|
|
|
- placeholder="例如:SystemUser"
|
|
|
- className="bg-white border-gray-200 focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
|
|
|
- />
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 权限标识 */}
|
|
|
- {formData.type !== MenuType.DIR && (
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700 flex items-center gap-1">
|
|
|
- 权限标识
|
|
|
- <HelpCircle className="w-3.5 h-3.5 text-gray-400" />
|
|
|
- </label>
|
|
|
- <Input
|
|
|
- value={formData.permission}
|
|
|
- onChange={(e) => setFormData(prev => ({ ...prev, permission: e.target.value }))}
|
|
|
- placeholder="请输入权限标识"
|
|
|
- className="bg-white border-gray-200 focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
|
|
|
- />
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 显示排序 */}
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700">
|
|
|
- <span className="text-red-500 mr-1">*</span>
|
|
|
- 显示排序
|
|
|
- </label>
|
|
|
- <Input
|
|
|
- type="number"
|
|
|
- value={formData.sort}
|
|
|
- onChange={(e) => setFormData(prev => ({ ...prev, sort: Number(e.target.value) || 0 }))}
|
|
|
- placeholder="请输入排序号"
|
|
|
- min={0}
|
|
|
- className="bg-white border-gray-200 focus:border-blue-400 focus:ring-2 focus:ring-blue-100"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 菜单状态 */}
|
|
|
- <div className="col-span-4 grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700">
|
|
|
- <span className="text-red-500 mr-1">*</span>
|
|
|
- 菜单状态
|
|
|
- </label>
|
|
|
- <div className="flex gap-0">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, status: 0 }))}
|
|
|
- className={`flex-1 px-4 py-1.5 text-xs border rounded-l-lg transition-all flex items-center justify-center gap-1.5 ${
|
|
|
- Number(formData.status) === 0
|
|
|
- ? 'bg-blue-50 text-blue-700 border-blue-500'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- <div className={`w-3 h-3 rounded-full border-2 flex items-center justify-center ${
|
|
|
- Number(formData.status) === 0 ? 'border-blue-500 bg-blue-500' : 'border-gray-300 bg-white'
|
|
|
- }`}>
|
|
|
- {Number(formData.status) === 0 && (
|
|
|
- <div className="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- 开启
|
|
|
- </button>
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, status: 1 }))}
|
|
|
- className={`flex-1 px-4 py-1.5 text-xs border border-l-0 rounded-r-lg transition-all flex items-center justify-center gap-1.5 ${
|
|
|
- Number(formData.status) === 1
|
|
|
- ? 'bg-blue-50 text-blue-700 border-blue-500'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- <div className={`w-3 h-3 rounded-full border-2 flex items-center justify-center ${
|
|
|
- Number(formData.status) === 1 ? 'border-blue-500 bg-blue-500' : 'border-gray-300 bg-white'
|
|
|
- }`}>
|
|
|
- {Number(formData.status) === 1 && (
|
|
|
- <div className="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- 关闭
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 显示状态 */}
|
|
|
- {formData.type !== MenuType.BUTTON && (
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700 flex items-center gap-1">
|
|
|
- 显示状态
|
|
|
- <HelpCircle className="w-3.5 h-3.5 text-gray-400" />
|
|
|
- </label>
|
|
|
- <div className="flex gap-0">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, visible: true }))}
|
|
|
- className={`flex-1 px-4 py-1.5 text-xs border rounded-l-lg transition-all flex items-center justify-center gap-1.5 ${
|
|
|
- formData.visible === true
|
|
|
- ? 'bg-blue-50 text-blue-700 border-blue-500'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- <div className={`w-3 h-3 rounded-full border-2 flex items-center justify-center ${
|
|
|
- formData.visible === true ? 'border-blue-500 bg-blue-500' : 'border-gray-300 bg-white'
|
|
|
- }`}>
|
|
|
- {formData.visible === true && (
|
|
|
- <div className="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- )}
|
|
|
+ {elementPlusIcons.map((iconItem) => {
|
|
|
+ const IconComponent = iconItem.icon;
|
|
|
+ const isSelected = formData.icon === iconItem.name;
|
|
|
+ return (
|
|
|
+ <div
|
|
|
+ key={iconItem.name}
|
|
|
+ onClick={() => {
|
|
|
+ setFormData(prev => ({ ...prev, icon: iconItem.name }));
|
|
|
+ setIconPickerOpen(false);
|
|
|
+ }}
|
|
|
+ style={{
|
|
|
+ display: 'flex',
|
|
|
+ alignItems: 'center',
|
|
|
+ justifyContent: 'center',
|
|
|
+ padding: 6,
|
|
|
+ borderRadius: 4,
|
|
|
+ cursor: 'pointer',
|
|
|
+ border: `1px solid ${isSelected ? '#1890ff' : '#d9d9d9'}`,
|
|
|
+ backgroundColor: isSelected ? '#e6f7ff' : '#fff',
|
|
|
+ aspectRatio: '1'
|
|
|
+ }}
|
|
|
+ title={`${iconItem.name} - ${iconItem.label}`}
|
|
|
+ >
|
|
|
+ <IconComponent
|
|
|
+ style={{
|
|
|
+ width: 14,
|
|
|
+ height: 14,
|
|
|
+ color: isSelected ? '#1890ff' : '#666'
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ })}
|
|
|
</div>
|
|
|
- 显示
|
|
|
- </button>
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, visible: false }))}
|
|
|
- className={`flex-1 px-4 py-1.5 text-xs border border-l-0 rounded-r-lg transition-all flex items-center justify-center gap-1.5 ${
|
|
|
- formData.visible === false
|
|
|
- ? 'bg-blue-50 text-blue-700 border-blue-500'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- <div className={`w-3 h-3 rounded-full border-2 flex items-center justify-center ${
|
|
|
- formData.visible === false ? 'border-blue-500 bg-blue-500' : 'border-gray-300 bg-white'
|
|
|
- }`}>
|
|
|
- {formData.visible === false && (
|
|
|
- <div className="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- 隐藏
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 总是显示 */}
|
|
|
- {formData.type !== MenuType.BUTTON && (
|
|
|
- <div className="grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700 flex items-center gap-1">
|
|
|
- 总是显示
|
|
|
- <HelpCircle className="w-3.5 h-3.5 text-gray-400" />
|
|
|
- </label>
|
|
|
- <div className="flex gap-0">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, alwaysShow: true }))}
|
|
|
- className={`flex-1 px-4 py-1.5 text-xs border rounded-l-lg transition-all flex items-center justify-center gap-1.5 ${
|
|
|
- (formData.alwaysShow ?? true) === true
|
|
|
- ? 'bg-blue-50 text-blue-700 border-blue-500'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- <div className={`w-3 h-3 rounded-full border-2 flex items-center justify-center ${
|
|
|
- (formData.alwaysShow ?? true) === true ? 'border-blue-500 bg-blue-500' : 'border-gray-300 bg-white'
|
|
|
- }`}>
|
|
|
- {(formData.alwaysShow ?? true) === true && (
|
|
|
- <div className="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- 总是
|
|
|
- </button>
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, alwaysShow: false }))}
|
|
|
- className={`flex-1 px-4 py-1.5 text-xs border border-l-0 rounded-r-lg transition-all flex items-center justify-center gap-1.5 ${
|
|
|
- (formData.alwaysShow ?? true) === false
|
|
|
- ? 'bg-blue-50 text-blue-700 border-blue-500'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- <div className={`w-3 h-3 rounded-full border-2 flex items-center justify-center ${
|
|
|
- (formData.alwaysShow ?? true) === false ? 'border-blue-500 bg-blue-500' : 'border-gray-300 bg-white'
|
|
|
- }`}>
|
|
|
- {(formData.alwaysShow ?? true) === false && (
|
|
|
- <div className="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- 不是
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 缓存状态 */}
|
|
|
- {formData.type === MenuType.MENU && (
|
|
|
- <div className="col-span-4 grid grid-cols-[100px_1fr] items-center gap-3" style={{ marginRight: '30px' }}>
|
|
|
- <label className="text-sm text-gray-700 flex items-center gap-1">
|
|
|
- 缓存状态
|
|
|
- <HelpCircle className="w-3.5 h-3.5 text-gray-400" />
|
|
|
- </label>
|
|
|
- <div className="flex gap-0">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, keepAlive: true }))}
|
|
|
- className={`flex-1 px-4 py-1.5 text-xs border rounded-l-lg transition-all flex items-center justify-center gap-1.5 ${
|
|
|
- (formData.keepAlive ?? true) === true
|
|
|
- ? 'bg-blue-50 text-blue-700 border-blue-500'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- <div className={`w-3 h-3 rounded-full border-2 flex items-center justify-center ${
|
|
|
- (formData.keepAlive ?? true) === true ? 'border-blue-500 bg-blue-500' : 'border-gray-300 bg-white'
|
|
|
- }`}>
|
|
|
- {(formData.keepAlive ?? true) === true && (
|
|
|
- <div className="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- 缓存
|
|
|
- </button>
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- onClick={() => setFormData(prev => ({ ...prev, keepAlive: false }))}
|
|
|
- className={`flex-1 px-4 py-1.5 text-xs border border-l-0 rounded-r-lg transition-all flex items-center justify-center gap-1.5 ${
|
|
|
- (formData.keepAlive ?? true) === false
|
|
|
- ? 'bg-blue-50 text-blue-700 border-blue-500'
|
|
|
- : 'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
|
- }`}
|
|
|
- >
|
|
|
- <div className={`w-3 h-3 rounded-full border-2 flex items-center justify-center ${
|
|
|
- (formData.keepAlive ?? true) === false ? 'border-blue-500 bg-blue-500' : 'border-gray-300 bg-white'
|
|
|
- }`}>
|
|
|
- {(formData.keepAlive ?? true) === false && (
|
|
|
- <div className="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- 不缓存
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- )}
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ title="选择图标"
|
|
|
+ trigger="click"
|
|
|
+ open={iconPickerOpen}
|
|
|
+ onOpenChange={setIconPickerOpen}
|
|
|
+ >
|
|
|
+ <Button type="primary">选择图标</Button>
|
|
|
+ </Popover>
|
|
|
+ </Space.Compact>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 路由地址 */}
|
|
|
+ {formData.type !== MenuType.BUTTON && (
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700 flex items-center gap-1" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ <span className="text-red-500">*</span>
|
|
|
+ 路由地址
|
|
|
+ <QuestionCircleOutlined style={{ fontSize: 14, color: '#999' }} />
|
|
|
+ </span>
|
|
|
+ <Input
|
|
|
+ style={{ flex: 1 }}
|
|
|
+ value={formData.path}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, path: e.target.value }))}
|
|
|
+ placeholder="请输入路由地址"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 组件地址 */}
|
|
|
+ {formData.type === MenuType.MENU && (
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>组件地址</span>
|
|
|
+ <Input
|
|
|
+ style={{ flex: 1 }}
|
|
|
+ value={formData.component}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, component: e.target.value }))}
|
|
|
+ placeholder="例如:system/user/index"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 组件名字 */}
|
|
|
+ {formData.type === MenuType.MENU && (
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>组件名字</span>
|
|
|
+ <Input
|
|
|
+ style={{ flex: 1 }}
|
|
|
+ value={formData.componentName || ''}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, componentName: e.target.value }))}
|
|
|
+ placeholder="例如:SystemUser"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 权限标识 */}
|
|
|
+ {formData.type !== MenuType.DIR && (
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700 flex items-center gap-1" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ 权限标识
|
|
|
+ <QuestionCircleOutlined style={{ fontSize: 14, color: '#999' }} />
|
|
|
+ </span>
|
|
|
+ <Input
|
|
|
+ style={{ flex: 1 }}
|
|
|
+ value={formData.permission}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, permission: e.target.value }))}
|
|
|
+ placeholder="请输入权限标识"
|
|
|
+ />
|
|
|
</div>
|
|
|
)}
|
|
|
- </div>
|
|
|
|
|
|
- {/* 弹窗底部 */}
|
|
|
- <div className="px-5 py-3 bg-gray-50/50 border-t border-gray-200 flex justify-end gap-2 rounded-b-xl shrink-0">
|
|
|
- <button
|
|
|
- onClick={() => setDialogVisible(false)}
|
|
|
- className="px-4 py-2 text-sm text-gray-600 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors"
|
|
|
- >
|
|
|
- 取消
|
|
|
- </button>
|
|
|
- <button
|
|
|
- onClick={submitForm}
|
|
|
- disabled={formLoading}
|
|
|
- className="px-4 py-2 text-sm text-white bg-gradient-to-r from-blue-500 to-blue-600 rounded-lg hover:shadow-lg hover:shadow-blue-400/40 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
|
|
- >
|
|
|
- {formLoading ? '提交中...' : '确定'}
|
|
|
- </button>
|
|
|
+ {/* 显示排序 */}
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ <span className="text-red-500 mr-1">*</span>
|
|
|
+ 显示排序
|
|
|
+ </span>
|
|
|
+ <Input
|
|
|
+ style={{ flex: 1 }}
|
|
|
+ type="number"
|
|
|
+ value={formData.sort}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, sort: Number(e.target.value) || 0 }))}
|
|
|
+ placeholder="请输入排序号"
|
|
|
+ min={0}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 菜单状态 */}
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ <span className="text-red-500 mr-1">*</span>
|
|
|
+ 菜单状态
|
|
|
+ </span>
|
|
|
+ <Radio.Group
|
|
|
+ value={Number(formData.status)}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, status: Number(e.target.value) }))}
|
|
|
+ buttonStyle="solid"
|
|
|
+ optionType="button"
|
|
|
+ >
|
|
|
+ <Radio.Button value={0}>开启</Radio.Button>
|
|
|
+ <Radio.Button value={1}>关闭</Radio.Button>
|
|
|
+ </Radio.Group>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 显示状态 */}
|
|
|
+ {formData.type !== MenuType.BUTTON && (
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700 flex items-center gap-1" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ 显示状态
|
|
|
+ <QuestionCircleOutlined style={{ fontSize: 14, color: '#999' }} />
|
|
|
+ </span>
|
|
|
+ <Radio.Group
|
|
|
+ value={formData.visible}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, visible: e.target.value as boolean }))}
|
|
|
+ buttonStyle="solid"
|
|
|
+ optionType="button"
|
|
|
+ >
|
|
|
+ <Radio.Button value={true}>显示</Radio.Button>
|
|
|
+ <Radio.Button value={false}>隐藏</Radio.Button>
|
|
|
+ </Radio.Group>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 总是显示 */}
|
|
|
+ {formData.type !== MenuType.BUTTON && (
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700 flex items-center gap-1" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ 总是显示
|
|
|
+ <QuestionCircleOutlined style={{ fontSize: 14, color: '#999' }} />
|
|
|
+ </span>
|
|
|
+ <Radio.Group
|
|
|
+ value={formData.alwaysShow ?? true}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, alwaysShow: e.target.value as boolean }))}
|
|
|
+ buttonStyle="solid"
|
|
|
+ optionType="button"
|
|
|
+ >
|
|
|
+ <Radio.Button value={true}>总是</Radio.Button>
|
|
|
+ <Radio.Button value={false}>不是</Radio.Button>
|
|
|
+ </Radio.Group>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 缓存状态 */}
|
|
|
+ {formData.type === MenuType.MENU && (
|
|
|
+ <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
|
+ <span className="text-sm text-gray-700 flex items-center gap-1" style={{ fontWeight: 500, minWidth: '80px', flexShrink: 0 }}>
|
|
|
+ 缓存状态
|
|
|
+ <QuestionCircleOutlined style={{ fontSize: 14, color: '#999' }} />
|
|
|
+ </span>
|
|
|
+ <Radio.Group
|
|
|
+ value={formData.keepAlive ?? true}
|
|
|
+ onChange={(e) => setFormData(prev => ({ ...prev, keepAlive: e.target.value as boolean }))}
|
|
|
+ buttonStyle="solid"
|
|
|
+ optionType="button"
|
|
|
+ >
|
|
|
+ <Radio.Button value={true}>缓存</Radio.Button>
|
|
|
+ <Radio.Button value={false}>不缓存</Radio.Button>
|
|
|
+ </Radio.Group>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ )}
|
|
|
+ </Modal>
|
|
|
);
|
|
|
});
|
|
|
|