update
This commit is contained in:
@@ -16,19 +16,14 @@ interface DatePickerProps {
|
||||
}
|
||||
|
||||
export function DatePicker({ className, value, onChange }: DatePickerProps) {
|
||||
const [date, setDate] = React.useState<Date | undefined>(
|
||||
value ? new Date(typeof value === 'string' ? value : value.toISOString()) : undefined
|
||||
)
|
||||
const toDate = (val: string | Dayjs | undefined): Date | undefined => {
|
||||
if (!val) return undefined
|
||||
return new Date(typeof val === 'string' ? val : val.toISOString())
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (value) {
|
||||
const dateValue = typeof value === 'string' ? value : value.toISOString()
|
||||
setDate(new Date(dateValue))
|
||||
}
|
||||
}, [value])
|
||||
const date = toDate(value)
|
||||
|
||||
const handleSelect = (selectedDate: Date | undefined) => {
|
||||
setDate(selectedDate)
|
||||
if (selectedDate && onChange) {
|
||||
onChange(dayjs(selectedDate))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,24 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip"
|
||||
|
||||
const LabelWithTooltip = ({ label, tips }: { label: string; tips?: string }) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium">{label}</label>
|
||||
{tips && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-4 w-4 p-0">
|
||||
<HelpCircle size={16} />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="whitespace-pre-wrap">
|
||||
<p>{tips}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const KeyShareSelect = ({ value, onChange }: { value: string; onChange?: (value: string) => void }) => {
|
||||
return (
|
||||
<Select value={value || ''} onValueChange={(val) => onChange?.(val)}>
|
||||
@@ -87,50 +105,22 @@ export const PermissionManager = ({ value, onChange, className }: PermissionMana
|
||||
}
|
||||
};
|
||||
|
||||
const tips = getTips('share');
|
||||
const shareTips = getTips('share');
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<form className={clsx('flex flex-col w-full gap-4', className)}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium">共享</label>
|
||||
{tips && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-4 w-4 p-0">
|
||||
<HelpCircle size={16} />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="whitespace-pre-wrap">
|
||||
<p>{tips}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<LabelWithTooltip label="共享" tips={shareTips} />
|
||||
<KeyShareSelect value={formData?.share} onChange={(value) => onChangeValue('share', value)} />
|
||||
</div>
|
||||
|
||||
{keys.map((item: any) => {
|
||||
const tips = getTips(item);
|
||||
const itemTips = getTips(item);
|
||||
|
||||
return (
|
||||
<div key={item} className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium">{item}</label>
|
||||
{tips && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-4 w-4 p-0">
|
||||
<HelpCircle size={16} />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{tips}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<LabelWithTooltip label={item} tips={itemTips} />
|
||||
{item === 'expiration-time' && (
|
||||
<DatePicker value={formData[item] || ''} onChange={(date) => onChangeValue(item, date)} />
|
||||
)}
|
||||
|
||||
@@ -16,13 +16,16 @@ export function TagsInput({ value, onChange, placeholder = "输入用户名,
|
||||
const [inputValue, setInputValue] = React.useState("")
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Enter" || e.key === ",") {
|
||||
const trimmed = inputValue.trim()
|
||||
|
||||
if ((e.key === "Enter" || e.key === ",") && trimmed) {
|
||||
e.preventDefault()
|
||||
const newValue = inputValue.trim()
|
||||
if (newValue && !value.includes(newValue)) {
|
||||
onChange([...value, newValue])
|
||||
if (!value.includes(trimmed)) {
|
||||
onChange([...value, trimmed])
|
||||
setInputValue("")
|
||||
} else {
|
||||
setInputValue("")
|
||||
}
|
||||
setInputValue("")
|
||||
} else if (e.key === "Backspace" && !inputValue && value.length > 0) {
|
||||
onChange(value.slice(0, -1))
|
||||
}
|
||||
@@ -34,9 +37,9 @@ export function TagsInput({ value, onChange, placeholder = "输入用户名,
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-wrap gap-2 w-full", className)}>
|
||||
{value.map((tag, index) => (
|
||||
{value.map((tag) => (
|
||||
<div
|
||||
key={`${tag}-${index}`}
|
||||
key={tag}
|
||||
className="flex items-center gap-1 px-3 py-1 text-sm bg-secondary text-secondary-foreground rounded-md border border-border"
|
||||
>
|
||||
<span>{tag}</span>
|
||||
@@ -44,6 +47,7 @@ export function TagsInput({ value, onChange, placeholder = "输入用户名,
|
||||
type="button"
|
||||
onClick={() => removeTag(tag)}
|
||||
className="flex items-center justify-center w-4 h-4 rounded hover:bg-muted transition-colors"
|
||||
aria-label={`移除 ${tag}`}
|
||||
>
|
||||
<X className="w-3 h-3" />
|
||||
</button>
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
export const getTips = (key: string, lang?: string) => {
|
||||
const tip = keysTips.find((item) => item.key === key);
|
||||
if (tip) {
|
||||
if (lang === 'en') {
|
||||
return tip.enTips;
|
||||
}
|
||||
return tip.tips;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const keysTips = [
|
||||
{
|
||||
key: 'share',
|
||||
@@ -79,31 +70,32 @@ export const keysTips = [
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// 创建缓存Map以提升查找性能
|
||||
const tipsMap = new Map(keysTips.map(tip => [tip.key, tip]));
|
||||
|
||||
export const getTips = (key: string, lang?: string) => {
|
||||
const tip = tipsMap.get(key);
|
||||
if (tip) {
|
||||
return lang === 'en' ? tip.enTips : tip.tips;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export class KeyParse {
|
||||
static parse(metadata: Record<string, any>) {
|
||||
const keys = Object.keys(metadata);
|
||||
const newMetadata = {};
|
||||
keys.forEach((key) => {
|
||||
const tip = keysTips.find((item) => item.key === key);
|
||||
if (tip && tip.parse) {
|
||||
newMetadata[key] = tip.parse(metadata[key]);
|
||||
} else {
|
||||
newMetadata[key] = metadata[key];
|
||||
}
|
||||
});
|
||||
return newMetadata;
|
||||
return Object.entries(metadata).reduce((acc, [key, value]) => {
|
||||
const tip = tipsMap.get(key);
|
||||
acc[key] = tip?.parse ? tip.parse(value) : value;
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
}
|
||||
|
||||
static stringify(metadata: Record<string, any>) {
|
||||
const keys = Object.keys(metadata);
|
||||
const newMetadata = {};
|
||||
keys.forEach((key) => {
|
||||
const tip = keysTips.find((item) => item.key === key);
|
||||
if (tip && tip.stringify) {
|
||||
newMetadata[key] = tip.stringify(metadata[key]);
|
||||
} else {
|
||||
newMetadata[key] = metadata[key];
|
||||
}
|
||||
});
|
||||
return newMetadata;
|
||||
return Object.entries(metadata).reduce((acc, [key, value]) => {
|
||||
const tip = tipsMap.get(key);
|
||||
acc[key] = tip?.stringify ? tip.stringify(value) : value;
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user