import React from '../../react'; import { useState, useEffect } from 'react'; import { Card, Button, Input, Select, Slider, ColorPicker, InputNumber, Divider, Tag, Space, Row, Col } from 'antd'; import { BoldOutlined, ItalicOutlined, UnderlineOutlined, FontSizeOutlined, BgColorsOutlined, BorderOutlined } from '@ant-design/icons'; import type { ElementInfo, SourceInfo } from '../../../../packages/client-shared/src/messages'; const { TextArea } = Input; const { Option } = Select; export interface StyleUpdateData { sourceInfo: SourceInfo; newClass: string; } export interface StylePanelProps { selectedElement: ElementInfo | null; onUpdateStyle: (data: StyleUpdateData) => void; currentClass: string; onClassChange: (newClass: string) => void; } /** * 样式预设配置 */ const stylePresets = { colors: { backgrounds: [ { label: '白色', value: 'bg-white', color: '#ffffff' }, { label: '浅灰色', value: 'bg-gray-100', color: '#f3f4f6' }, { label: '浅蓝色', value: 'bg-blue-100', color: '#dbeafe' }, { label: '浅绿色', value: 'bg-green-100', color: '#dcfce7' }, { label: '浅红色', value: 'bg-red-100', color: '#fee2e2' }, { label: '浅黄色', value: 'bg-yellow-100', color: '#fef3c7' }, { label: '浅紫色', value: 'bg-purple-100', color: '#f3e8ff' }, { label: '深蓝色', value: 'bg-blue-600', color: '#2563eb' }, ], text: [ { label: '黑色', value: 'text-black', color: '#000000' }, { label: '深灰色', value: 'text-gray-900', color: '#111827' }, { label: '中灰色', value: 'text-gray-600', color: '#4b5563' }, { label: '蓝色', value: 'text-blue-600', color: '#2563eb' }, { label: '绿色', value: 'text-green-600', color: '#16a34a' }, { label: '红色', value: 'text-red-600', color: '#dc2626' }, { label: '黄色', value: 'text-yellow-600', color: '#ca8a04' }, { label: '紫色', value: 'text-purple-600', color: '#9333ea' }, ] }, spacing: { padding: [ { label: '无内边距', value: 'p-0', preview: 'padding: 0px' }, { label: '小内边距', value: 'p-2', preview: 'padding: 8px' }, { label: '中等内边距', value: 'p-4', preview: 'padding: 16px' }, { label: '大内边距', value: 'p-6', preview: 'padding: 24px' }, { label: '超大内边距', value: 'p-8', preview: 'padding: 32px' }, ], margin: [ { label: '无外边距', value: 'm-0', preview: 'margin: 0px' }, { label: '小外边距', value: 'm-2', preview: 'margin: 8px' }, { label: '中等外边距', value: 'm-4', preview: 'margin: 16px' }, { label: '大外边距', value: 'm-6', preview: 'margin: 24px' }, ] }, border: { radius: [ { label: '无圆角', value: 'rounded-none', preview: 'border-radius: 0px' }, { label: '小圆角', value: 'rounded-sm', preview: 'border-radius: 2px' }, { label: '中等圆角', value: 'rounded-md', preview: 'border-radius: 6px' }, { label: '大圆角', value: 'rounded-lg', preview: 'border-radius: 8px' }, { label: '超大圆角', value: 'rounded-xl', preview: 'border-radius: 12px' }, { label: '完全圆角', value: 'rounded-full', preview: 'border-radius: 9999px' }, ], width: [ { label: '无边框', value: 'border-0', preview: 'border-width: 0px' }, { label: '细边框', value: 'border', preview: 'border-width: 1px' }, { label: '中等边框', value: 'border-2', preview: 'border-width: 2px' }, { label: '粗边框', value: 'border-4', preview: 'border-width: 4px' }, ] }, typography: { fontSize: [ { label: '很小', value: 'text-xs', preview: 'font-size: 12px' }, { label: '小', value: 'text-sm', preview: 'font-size: 14px' }, { label: '正常', value: 'text-base', preview: 'font-size: 16px' }, { label: '大', value: 'text-lg', preview: 'font-size: 18px' }, { label: '很大', value: 'text-xl', preview: 'font-size: 20px' }, { label: '巨大', value: 'text-2xl', preview: 'font-size: 24px' }, ], fontWeight: [ { label: '细体', value: 'font-thin', preview: 'font-weight: 100' }, { label: '特细', value: 'font-extralight', preview: 'font-weight: 200' }, { label: '细体', value: 'font-light', preview: 'font-weight: 300' }, { label: '正常', value: 'font-normal', preview: 'font-weight: 400' }, { label: '中粗', value: 'font-medium', preview: 'font-weight: 500' }, { label: '半粗', value: 'font-semibold', preview: 'font-weight: 600' }, { label: '粗体', value: 'font-bold', preview: 'font-weight: 700' }, { label: '超粗', value: 'font-black', preview: 'font-weight: 900' }, ] }, layout: { display: [ { label: '块级', value: 'block', preview: 'display: block' }, { label: '内联', value: 'inline', preview: 'display: inline' }, { label: '内联块', value: 'inline-block', preview: 'display: inline-block' }, { label: '弹性', value: 'flex', preview: 'display: flex' }, { label: '网格', value: 'grid', preview: 'display: grid' }, { label: '隐藏', value: 'hidden', preview: 'display: none' }, ], position: [ { label: '静态', value: 'static', preview: 'position: static' }, { label: '相对', value: 'relative', preview: 'position: relative' }, { label: '绝对', value: 'absolute', preview: 'position: absolute' }, { label: '固定', value: 'fixed', preview: 'position: fixed' }, { label: '粘性', value: 'sticky', preview: 'position: sticky' }, ] } }; /** * 样式编辑面板组件 */ export const StylePanel: React.FC = ({ selectedElement, onUpdateStyle, currentClass, onClassChange }) => { const [activeTab, setActiveTab] = useState('presets'); const [customStyles, setCustomStyles] = useState(currentClass); const [colorPickerVisible, setColorPickerVisible] = useState(false); const [customColor, setCustomColor] = useState('#3b82f6'); useEffect(() => { setCustomStyles(currentClass); }, [currentClass]); /** * 添加样式类 */ const addStyleClass = (className: string) => { const newStyles = mergeClasses(customStyles, className); setCustomStyles(newStyles); onClassChange(newStyles); }; /** * 移除样式类 */ const removeStyleClass = (className: string) => { const newStyles = removeClass(customStyles, className); setCustomStyles(newStyles); onClassChange(newStyles); }; /** * 更新样式 */ const updateStyles = () => { if (!selectedElement) return; onUpdateStyle({ sourceInfo: selectedElement.sourceInfo, newClass: customStyles }); }; /** * 清除所有样式 */ const clearAllStyles = () => { setCustomStyles(''); onClassChange(''); }; /** * 合并样式类 */ const mergeClasses = (existing: string, newClass: string): string => { const classes = existing.split(' ').filter(Boolean); const newClasses = newClass.split(' ').filter(Boolean); // 移除冲突的同类样式 const filteredClasses = classes.filter(cls => { return !newClasses.some(newCls => (cls.startsWith('bg-') && newCls.startsWith('bg-')) || (cls.startsWith('text-') && newCls.startsWith('text-')) || (cls.startsWith('p-') && newCls.startsWith('p-')) || (cls.startsWith('m-') && newCls.startsWith('m-')) || (cls.startsWith('rounded') && newCls.startsWith('rounded')) || (cls.startsWith('border-') && newCls.startsWith('border-')) || (cls.startsWith('text-') && newCls.startsWith('text-')) || (cls.startsWith('font-') && newCls.startsWith('font-')) || (cls.startsWith('flex') && newCls.startsWith('flex')) || (cls.startsWith('grid') && newCls.startsWith('grid')) || cls === newCls ); }); return [...filteredClasses, ...newClasses].join(' ').trim(); }; /** * 移除样式类 */ const removeClass = (existing: string, classToRemove: string): string => { return existing.split(' ') .filter(cls => cls !== classToRemove && !cls.startsWith(classToRemove.split('-')[0])) .join(' ') .trim(); }; /** * 获取当前应用的样式类 */ const getAppliedStyles = (): string[] => { return customStyles.split(' ').filter(Boolean); }; if (!selectedElement) { return (

请先选择一个元素

); } return ( 样式编辑 } className="h-full" extra={ } >
{/* 元素信息 */}

当前元素

标签: <{selectedElement.tagName}>
位置: {selectedElement.sourceInfo.fileName.split('/').pop()}:{selectedElement.sourceInfo.lineNumber}
{/* 快速预设标签页 */}
{[ { key: 'presets', label: '预设样式' }, { key: 'colors', label: '颜色' }, { key: 'typography', label: '字体' }, { key: 'layout', label: '布局' }, { key: 'custom', label: '自定义' } ].map(tab => ( ))}
{/* 预设样式 */} {activeTab === 'presets' && (
{/* 背景颜色 */}

背景颜色

{stylePresets.colors.backgrounds.map((color, index) => ( ))}
{/* 圆角 */}

圆角

{stylePresets.border.radius.map((radius, index) => ( ))}
{/* 边距 */}

边距

{stylePresets.spacing.padding.map((padding, index) => ( ))}
)} {/* 颜色面板 */} {activeTab === 'colors' && (
{/* 背景颜色 */}

背景颜色

{stylePresets.colors.backgrounds.map((color, index) => ( ))}
{/* 文字颜色 */}

文字颜色

{stylePresets.colors.text.map((color, index) => ( ))}
{/* 自定义颜色 */}

自定义颜色

setCustomColor(e.target.value)} className="w-8 h-8 border border-gray-300 rounded cursor-pointer" /> setCustomColor(e.target.value)} placeholder="#3b82f6" />
)} {/* 字体面板 */} {activeTab === 'typography' && (
{/* 字体大小 */}

字体大小

{stylePresets.typography.fontSize.map((fontSize, index) => ( ))}
{/* 字体粗细 */}

字体粗细

{stylePresets.typography.fontWeight.map((fontWeight, index) => ( ))}
)} {/* 布局面板 */} {activeTab === 'layout' && (
{/* 显示类型 */}

显示类型

{stylePresets.layout.display.map((display, index) => ( ))}
{/* 定位 */}

定位

{stylePresets.layout.position.map((position, index) => ( ))}
)} {/* 自定义样式 */} {activeTab === 'custom' && (