diff --git a/Layout.css b/Layout.css index b1af68a6..e6e6cb0b 100644 --- a/Layout.css +++ b/Layout.css @@ -1,10 +1,10 @@ -.container { - display: flex; - height: 100vh; - flex-direction: column; -} - -.container main { - flex: 1; - overflow: auto; +.container { + display: flex; + height: 100vh; + flex-direction: column; +} + +.container main { + flex: 1; + overflow: auto; } \ No newline at end of file diff --git a/nkebao/src/App.tsx b/nkebao/src/App.tsx index 83c41992..f0cb3afe 100644 --- a/nkebao/src/App.tsx +++ b/nkebao/src/App.tsx @@ -13,9 +13,14 @@ import DeviceDetail from './pages/devices/DeviceDetail'; import WechatAccounts from './pages/wechat-accounts/WechatAccounts'; import WechatAccountDetail from './pages/wechat-accounts/WechatAccountDetail'; import Workspace from './pages/workspace/Workspace'; +import AutoLike from './pages/workspace/auto-like/AutoLike'; +import NewAutoLike from './pages/workspace/auto-like/NewAutoLike'; +import AutoGroup from './pages/workspace/auto-group/AutoGroup'; import AutoGroupDetail from './pages/workspace/auto-group/Detail'; +import GroupPush from './pages/workspace/group-push/GroupPush'; import MomentsSync from './pages/workspace/moments-sync/MomentsSync'; import MomentsSyncDetail from './pages/workspace/moments-sync/Detail'; +import AIAssistant from './pages/workspace/ai-assistant/AIAssistant'; import TrafficDistribution from './pages/workspace/traffic-distribution/TrafficDistribution'; import TrafficDistributionDetail from './pages/workspace/traffic-distribution/Detail'; import Scenarios from './pages/scenarios/Scenarios'; @@ -51,9 +56,14 @@ function App() { } /> } /> } /> + } /> + } /> + } /> } /> + } /> } /> } /> + } /> } /> } /> } /> diff --git a/nkebao/src/components/Layout.css b/nkebao/src/components/Layout.css index b1af68a6..e6e6cb0b 100644 --- a/nkebao/src/components/Layout.css +++ b/nkebao/src/components/Layout.css @@ -1,10 +1,10 @@ -.container { - display: flex; - height: 100vh; - flex-direction: column; -} - -.container main { - flex: 1; - overflow: auto; +.container { + display: flex; + height: 100vh; + flex-direction: column; +} + +.container main { + flex: 1; + overflow: auto; } \ No newline at end of file diff --git a/nkebao/src/components/ui/card.tsx b/nkebao/src/components/ui/card.tsx index 87695c98..e214121d 100644 --- a/nkebao/src/components/ui/card.tsx +++ b/nkebao/src/components/ui/card.tsx @@ -26,6 +26,19 @@ export function CardHeader({ children, className = '' }: CardHeaderProps) { ); } +interface CardTitleProps { + children: React.ReactNode; + className?: string; +} + +export function CardTitle({ children, className = '' }: CardTitleProps) { + return ( +

+ {children} +

+ ); +} + interface CardContentProps { children: React.ReactNode; className?: string; diff --git a/nkebao/src/components/ui/checkbox.tsx b/nkebao/src/components/ui/checkbox.tsx new file mode 100644 index 00000000..87aa708f --- /dev/null +++ b/nkebao/src/components/ui/checkbox.tsx @@ -0,0 +1,28 @@ +import React from 'react'; + +interface CheckboxProps { + checked?: boolean; + onChange?: (checked: boolean) => void; + disabled?: boolean; + className?: string; + id?: string; +} + +export function Checkbox({ + checked = false, + onChange, + disabled = false, + className = '', + id +}: CheckboxProps) { + return ( + onChange?.(e.target.checked)} + disabled={disabled} + className={`w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-2 ${className}`} + /> + ); +} \ No newline at end of file diff --git a/nkebao/src/components/ui/select.tsx b/nkebao/src/components/ui/select.tsx new file mode 100644 index 00000000..c16f9f3a --- /dev/null +++ b/nkebao/src/components/ui/select.tsx @@ -0,0 +1,184 @@ +import React, { useState, useRef, useEffect } from 'react'; + +interface SelectProps { + value?: string; + onValueChange?: (value: string) => void; + disabled?: boolean; + className?: string; + placeholder?: string; + children: React.ReactNode; +} + +interface SelectTriggerProps { + children: React.ReactNode; + className?: string; +} + +interface SelectContentProps { + children: React.ReactNode; + className?: string; +} + +interface SelectItemProps { + value: string; + children: React.ReactNode; + className?: string; +} + +interface SelectValueProps { + placeholder?: string; + className?: string; +} + +export function Select({ + value, + onValueChange, + disabled = false, + className = '', + placeholder, + children +}: SelectProps) { + const [isOpen, setIsOpen] = useState(false); + const [selectedValue, setSelectedValue] = useState(value || ''); + const [selectedLabel, setSelectedLabel] = useState(''); + const selectRef = useRef(null); + + useEffect(() => { + setSelectedValue(value || ''); + }, [value]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (selectRef.current && !selectRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + const handleSelect = (value: string, label: string) => { + setSelectedValue(value); + setSelectedLabel(label); + onValueChange?.(value); + setIsOpen(false); + }; + + return ( +
+ {React.Children.map(children, (child) => { + if (React.isValidElement(child)) { + if (child.type === SelectTrigger) { + return React.cloneElement(child as any, { + onClick: () => !disabled && setIsOpen(!isOpen), + disabled, + selectedValue: selectedValue, + selectedLabel: selectedLabel, + placeholder, + isOpen + }); + } + if (child.type === SelectContent && isOpen) { + return React.cloneElement(child as any, { + onSelect: handleSelect + }); + } + } + return child; + })} +
+ ); +} + +export function SelectTrigger({ + children, + className = '', + onClick, + disabled, + selectedValue, + selectedLabel, + placeholder, + isOpen +}: SelectTriggerProps & { + onClick?: () => void; + disabled?: boolean; + selectedValue?: string; + selectedLabel?: string; + placeholder?: string; + isOpen?: boolean; +}) { + return ( + + ); +} + +export function SelectContent({ + children, + className = '', + onSelect +}: SelectContentProps & { + onSelect?: (value: string, label: string) => void; +}) { + return ( +
+ {React.Children.map(children, (child) => { + if (React.isValidElement(child) && child.type === SelectItem) { + return React.cloneElement(child as any, { + onSelect + }); + } + return child; + })} +
+ ); +} + +export function SelectItem({ + value, + children, + className = '', + onSelect +}: SelectItemProps & { + onSelect?: (value: string, label: string) => void; +}) { + return ( + + ); +} + +export function SelectValue({ + placeholder, + className = '' +}: SelectValueProps) { + return ( + + {placeholder} + + ); +} \ No newline at end of file diff --git a/nkebao/src/components/ui/textarea.tsx b/nkebao/src/components/ui/textarea.tsx new file mode 100644 index 00000000..90ca7776 --- /dev/null +++ b/nkebao/src/components/ui/textarea.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +interface TextareaProps { + value?: string; + onChange?: (e: React.ChangeEvent) => void; + onKeyDown?: (e: React.KeyboardEvent) => void; + placeholder?: string; + className?: string; + disabled?: boolean; + rows?: number; +} + +export function Textarea({ + value, + onChange, + onKeyDown, + placeholder, + className = '', + disabled = false, + rows = 3 +}: TextareaProps) { + return ( +