feat(快捷短语): 添加快捷短语UI组件
QuickPhraseItem: - 快捷短语列表项组件 - 支持图标显示和内容预览 - 提供悬停时的编辑和删除操作 QuickPhrasesPopover: - 快捷短语弹出层组件 - 包含触发按钮和数量徽章 - 支持快速插入、编辑和删除短语 QuickPhrasesModal: - 快捷短语管理模态框 - 左侧列表右侧编辑的双栏布局 - 支持图标选择器和分类设置
This commit is contained in:
parent
4499f7befd
commit
caf19f4c09
123
src/components/features/QuickPhraseItem.tsx
Normal file
123
src/components/features/QuickPhraseItem.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import * as LucideIcons from 'lucide-react';
|
||||
import type { QuickPhrase } from '@/types';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Tooltip } from '@/components/ui/Tooltip';
|
||||
|
||||
interface QuickPhraseItemProps {
|
||||
phrase: QuickPhrase;
|
||||
onSelect: (phrase: QuickPhrase) => void;
|
||||
onEdit?: (phrase: QuickPhrase) => void;
|
||||
onDelete?: (id: string) => void;
|
||||
showActions?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷短语列表项组件
|
||||
*/
|
||||
export function QuickPhraseItem({
|
||||
phrase,
|
||||
onSelect,
|
||||
onEdit,
|
||||
onDelete,
|
||||
showActions = true,
|
||||
}: QuickPhraseItemProps) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
// 获取图标组件
|
||||
const IconComponent = phrase.icon
|
||||
? (LucideIcons[phrase.icon as keyof typeof LucideIcons] as React.ComponentType<{ size?: number; className?: string }>)
|
||||
: LucideIcons.MessageSquare;
|
||||
|
||||
// 截断内容预览(最多显示 50 个字符)
|
||||
const contentPreview = phrase.content.length > 50
|
||||
? phrase.content.substring(0, 50) + '...'
|
||||
: phrase.content;
|
||||
|
||||
const handleSelect = () => {
|
||||
onSelect(phrase);
|
||||
};
|
||||
|
||||
const handleEdit = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onEdit?.(phrase);
|
||||
};
|
||||
|
||||
const handleDelete = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onDelete?.(phrase.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'group relative flex items-start gap-2 px-2.5 py-1.5 rounded-md cursor-pointer transition-all duration-150',
|
||||
'hover:bg-[var(--color-bg-hover)]',
|
||||
'border border-transparent hover:border-[var(--color-border)]'
|
||||
)}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
onClick={handleSelect}
|
||||
>
|
||||
{/* 图标 */}
|
||||
{IconComponent && (
|
||||
<div className="flex-shrink-0 mt-0.5">
|
||||
<IconComponent
|
||||
size={16}
|
||||
className="text-[var(--color-text-tertiary)]"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 内容区域 */}
|
||||
<div className="flex-1 min-w-0">
|
||||
{/* 标题 */}
|
||||
<div className="text-xs font-medium text-[var(--color-text-primary)] mb-0.5 truncate">
|
||||
{phrase.title}
|
||||
</div>
|
||||
|
||||
{/* 内容预览 */}
|
||||
<div className="text-[11px] text-[var(--color-text-tertiary)] line-clamp-2 leading-tight">
|
||||
{contentPreview}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
{showActions && (isHovered || false) && (
|
||||
<div className="flex items-center gap-1 flex-shrink-0">
|
||||
{onEdit && (
|
||||
<Tooltip content="编辑">
|
||||
<button
|
||||
onClick={handleEdit}
|
||||
className={cn(
|
||||
'w-6 h-6 flex items-center justify-center rounded transition-colors',
|
||||
'text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)]',
|
||||
'hover:bg-[var(--color-bg-secondary)]'
|
||||
)}
|
||||
>
|
||||
<LucideIcons.Pencil size={14} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{onDelete && (
|
||||
<Tooltip content="删除">
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
className={cn(
|
||||
'w-6 h-6 flex items-center justify-center rounded transition-colors',
|
||||
'text-[var(--color-text-tertiary)] hover:text-red-500',
|
||||
'hover:bg-red-50 dark:hover:bg-red-500/10'
|
||||
)}
|
||||
>
|
||||
<LucideIcons.Trash2 size={14} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
327
src/components/features/QuickPhrasesModal.tsx
Normal file
327
src/components/features/QuickPhrasesModal.tsx
Normal file
@ -0,0 +1,327 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Plus, Trash2, Check } from 'lucide-react';
|
||||
import { Modal } from '@/components/ui/Modal';
|
||||
import { IconPicker } from '@/components/ui/IconPicker';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { QuickPhrase } from '@/types';
|
||||
|
||||
interface QuickPhrasesModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
phrases: QuickPhrase[];
|
||||
onAdd: (phrase: Omit<QuickPhrase, 'id' | 'createdAt' | 'updatedAt' | 'sortOrder'>) => void;
|
||||
onUpdate: (id: string, updates: Partial<Omit<QuickPhrase, 'id' | 'createdAt'>>) => void;
|
||||
onDelete: (id: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷短语管理模态框
|
||||
*/
|
||||
export function QuickPhrasesModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
phrases,
|
||||
onAdd,
|
||||
onUpdate,
|
||||
onDelete,
|
||||
}: QuickPhrasesModalProps) {
|
||||
const [selectedPhraseId, setSelectedPhraseId] = useState<string | null>(null);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
// 表单状态
|
||||
const [formData, setFormData] = useState({
|
||||
title: '',
|
||||
content: '',
|
||||
icon: 'MessageSquare' as string,
|
||||
category: '',
|
||||
});
|
||||
|
||||
// 当选中短语变化时,更新表单数据
|
||||
useEffect(() => {
|
||||
if (selectedPhraseId) {
|
||||
const phrase = phrases.find((p) => p.id === selectedPhraseId);
|
||||
if (phrase) {
|
||||
setFormData({
|
||||
title: phrase.title,
|
||||
content: phrase.content,
|
||||
icon: phrase.icon || 'MessageSquare',
|
||||
category: phrase.category || '',
|
||||
});
|
||||
setIsEditing(true);
|
||||
}
|
||||
} else {
|
||||
// 重置表单
|
||||
setFormData({
|
||||
title: '',
|
||||
content: '',
|
||||
icon: 'MessageSquare',
|
||||
category: '',
|
||||
});
|
||||
setIsEditing(false);
|
||||
}
|
||||
}, [selectedPhraseId, phrases]);
|
||||
|
||||
const handleSelectPhrase = (id: string) => {
|
||||
setSelectedPhraseId(id);
|
||||
};
|
||||
|
||||
const handleNewPhrase = () => {
|
||||
setSelectedPhraseId(null);
|
||||
setFormData({
|
||||
title: '',
|
||||
content: '',
|
||||
icon: 'MessageSquare',
|
||||
category: '',
|
||||
});
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
// 验证表单
|
||||
if (!formData.title.trim() || !formData.content.trim()) {
|
||||
alert('请填写标题和内容');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEditing && selectedPhraseId) {
|
||||
// 更新现有短语
|
||||
onUpdate(selectedPhraseId, {
|
||||
title: formData.title.trim(),
|
||||
content: formData.content.trim(),
|
||||
icon: formData.icon,
|
||||
category: formData.category.trim() || undefined,
|
||||
});
|
||||
} else {
|
||||
// 添加新短语
|
||||
onAdd({
|
||||
title: formData.title.trim(),
|
||||
content: formData.content.trim(),
|
||||
icon: formData.icon,
|
||||
category: formData.category.trim() || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
handleNewPhrase();
|
||||
};
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
if (confirm('确定要删除这个快捷短语吗?')) {
|
||||
onDelete(id);
|
||||
if (selectedPhraseId === id) {
|
||||
handleNewPhrase();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
maxWidth="1000px"
|
||||
className="overflow-hidden"
|
||||
>
|
||||
{/* 标题 */}
|
||||
<div className="px-6 py-4 border-b border-[var(--color-border)] bg-gradient-to-b from-[var(--color-bg-secondary)] to-[var(--color-bg-primary)]">
|
||||
<h2 className="text-xl font-bold text-[var(--color-text-primary)]">
|
||||
管理快捷短语
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{/* 内容区域 */}
|
||||
<div className="flex h-[600px]">
|
||||
{/* 左侧:短语列表 - 固定300px宽度 */}
|
||||
<div className="w-[300px] border-r border-[var(--color-border)] flex flex-col bg-[var(--color-bg-secondary)]">
|
||||
{/* 新建按钮 */}
|
||||
<div className="p-4">
|
||||
<button
|
||||
onClick={handleNewPhrase}
|
||||
className={cn(
|
||||
'w-full flex items-center justify-center gap-2 px-3 py-2.5 rounded-md transition-all duration-200',
|
||||
'text-sm font-semibold',
|
||||
!isEditing && !selectedPhraseId
|
||||
? 'bg-[var(--color-primary)] text-white hover:shadow-md'
|
||||
: 'text-[var(--color-text-secondary)] bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-hover)] border border-[var(--color-border)]'
|
||||
)}
|
||||
>
|
||||
<Plus size={16} />
|
||||
<span>新建短语</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 短语列表 */}
|
||||
<div className="flex-1 overflow-y-auto px-3 pb-3">
|
||||
{phrases.length === 0 ? (
|
||||
<div className="text-center py-8 text-[var(--color-text-tertiary)]">
|
||||
<p className="text-sm">暂无快捷短语</p>
|
||||
<p className="text-xs mt-1">点击上方按钮添加</p>
|
||||
</div>
|
||||
) : (
|
||||
phrases.map((phrase) => (
|
||||
<div
|
||||
key={phrase.id}
|
||||
className={cn(
|
||||
'group relative flex items-start gap-2.5 px-3 py-2.5 rounded-md cursor-pointer transition-all duration-200 mb-2',
|
||||
'bg-[var(--color-bg-primary)]',
|
||||
selectedPhraseId === phrase.id
|
||||
? 'border-2 border-[var(--color-primary)] shadow-sm'
|
||||
: 'border-2 border-transparent hover:border-[var(--color-border)]'
|
||||
)}
|
||||
onClick={() => handleSelectPhrase(phrase.id)}
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className={cn(
|
||||
"text-sm font-semibold truncate mb-0.5",
|
||||
selectedPhraseId === phrase.id
|
||||
? 'text-[var(--color-primary)]'
|
||||
: 'text-[var(--color-text-primary)]'
|
||||
)}>
|
||||
{phrase.title}
|
||||
</div>
|
||||
<div className="text-xs text-[var(--color-text-tertiary)] truncate">
|
||||
{phrase.content.substring(0, 30)}...
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDelete(phrase.id);
|
||||
}}
|
||||
className={cn(
|
||||
'opacity-0 group-hover:opacity-100 transition-opacity',
|
||||
'w-6 h-6 flex items-center justify-center rounded',
|
||||
'text-[var(--color-text-tertiary)] hover:text-red-500',
|
||||
'hover:bg-red-50 dark:hover:bg-red-500/10'
|
||||
)}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧:编辑表单 - flex-1占据剩余空间 */}
|
||||
<div className="flex-1 flex flex-col">
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<div className="max-w-2xl">
|
||||
{/* 标题输入 */}
|
||||
<div className="mb-5">
|
||||
<label className="block text-sm font-semibold text-[var(--color-text-primary)] mb-2">
|
||||
标题 <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.title}
|
||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
||||
placeholder="输入短语标题"
|
||||
className={cn(
|
||||
'w-full px-3 py-2.5 rounded border-2 border-[var(--color-border)]',
|
||||
'bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]',
|
||||
'placeholder:text-[var(--color-text-placeholder)]',
|
||||
'focus:outline-none focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/10',
|
||||
'transition-all text-sm'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 内容输入 */}
|
||||
<div className="mb-5">
|
||||
<label className="block text-sm font-semibold text-[var(--color-text-primary)] mb-2">
|
||||
内容 <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
value={formData.content}
|
||||
onChange={(e) => setFormData({ ...formData, content: e.target.value })}
|
||||
placeholder="输入短语内容,将插入到输入框中"
|
||||
rows={5}
|
||||
className={cn(
|
||||
'w-full px-3 py-2.5 rounded border-2 border-[var(--color-border)]',
|
||||
'bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]',
|
||||
'placeholder:text-[var(--color-text-placeholder)]',
|
||||
'focus:outline-none focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/10',
|
||||
'transition-all resize-none text-sm leading-relaxed'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 图标选择 */}
|
||||
<div className="mb-5">
|
||||
<label className="block text-sm font-semibold text-[var(--color-text-primary)] mb-2">
|
||||
图标
|
||||
</label>
|
||||
<IconPicker
|
||||
value={formData.icon}
|
||||
onChange={(icon) => setFormData({ ...formData, icon })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 分类输入(可选)*/}
|
||||
<div className="mb-6">
|
||||
<label className="block text-sm font-semibold text-[var(--color-text-primary)] mb-2">
|
||||
分类(可选)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.category}
|
||||
onChange={(e) => setFormData({ ...formData, category: e.target.value })}
|
||||
placeholder="输入分类名称"
|
||||
className={cn(
|
||||
'w-full px-3 py-2.5 rounded border-2 border-[var(--color-border)]',
|
||||
'bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]',
|
||||
'placeholder:text-[var(--color-text-placeholder)]',
|
||||
'focus:outline-none focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/10',
|
||||
'transition-all text-sm'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 提示信息 */}
|
||||
<div className="mt-6 p-4 bg-gradient-to-r from-[var(--color-primary)]/5 to-[var(--color-primary)]/10 rounded">
|
||||
<div className="text-sm font-semibold text-[var(--color-primary)] mb-1">
|
||||
💡 使用提示
|
||||
</div>
|
||||
<div className="text-xs text-[var(--color-text-secondary)] leading-relaxed">
|
||||
快捷短语可以帮助你快速插入常用的提示词模板,提高工作效率。支持自定义图标和分类,让管理更加井然有序。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 底部操作按钮 */}
|
||||
<div className="px-6 py-4 border-t border-[var(--color-border)] flex items-center justify-end gap-3 bg-[var(--color-bg-secondary)]">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
'px-5 py-2.5 rounded-md transition-all',
|
||||
'text-sm font-semibold text-[var(--color-text-secondary)]',
|
||||
'bg-[var(--color-bg-primary)] border-2 border-[var(--color-border)]',
|
||||
'hover:bg-[var(--color-bg-hover)]'
|
||||
)}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={!formData.title.trim() || !formData.content.trim()}
|
||||
className={cn(
|
||||
'px-5 py-2.5 rounded-md transition-all duration-200',
|
||||
'text-sm font-semibold text-white',
|
||||
'flex items-center gap-2',
|
||||
formData.title.trim() && formData.content.trim()
|
||||
? 'bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)]'
|
||||
: 'bg-gray-300 cursor-not-allowed opacity-50'
|
||||
)}
|
||||
>
|
||||
<Check size={16} />
|
||||
<span>{isEditing ? '保存修改' : '添加短语'}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
144
src/components/features/QuickPhrasesPopover.tsx
Normal file
144
src/components/features/QuickPhrasesPopover.tsx
Normal file
@ -0,0 +1,144 @@
|
||||
'use client';
|
||||
|
||||
import { MessageSquareQuote, Plus, Settings2 } from 'lucide-react';
|
||||
import { Tooltip } from '@/components/ui/Tooltip';
|
||||
import { QuickPhraseItem } from './QuickPhraseItem';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { QuickPhrase } from '@/types';
|
||||
|
||||
// 触发按钮组件
|
||||
interface QuickPhrasesTriggerProps {
|
||||
phrasesCount: number;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function QuickPhrasesTrigger({ phrasesCount, onClick }: QuickPhrasesTriggerProps) {
|
||||
return (
|
||||
<div className="relative">
|
||||
<Tooltip content="快捷短语" position="top">
|
||||
<button
|
||||
onClick={onClick}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-md transition-colors cursor-pointer text-[var(--color-text-tertiary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-secondary)]"
|
||||
>
|
||||
<MessageSquareQuote size={20} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
{/* 数量徽章 */}
|
||||
{phrasesCount > 0 && (
|
||||
<span className="absolute -top-0.5 -right-0.5 min-w-4 h-4 bg-[var(--color-primary)] text-white text-[10px] font-semibold rounded-full flex items-center justify-center px-1 pointer-events-none">
|
||||
{phrasesCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 弹出层内容组件
|
||||
interface QuickPhrasesPopoverProps {
|
||||
phrases: QuickPhrase[];
|
||||
isOpen: boolean;
|
||||
onInsert: (content: string) => void;
|
||||
onEdit: (phrase: QuickPhrase) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onManage: () => void;
|
||||
onAdd: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷短语弹出层内容
|
||||
*/
|
||||
export function QuickPhrasesPopover({
|
||||
phrases,
|
||||
isOpen,
|
||||
onInsert,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onManage,
|
||||
onAdd,
|
||||
onClose,
|
||||
}: QuickPhrasesPopoverProps) {
|
||||
const handleSelect = (phrase: QuickPhrase) => {
|
||||
onInsert(phrase.content);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleManage = () => {
|
||||
onManage();
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute bottom-full left-0 right-0 mb-2 bg-[var(--color-bg-primary)] border border-[var(--color-border)] rounded-md shadow-lg z-50',
|
||||
'transition-all duration-150',
|
||||
isOpen
|
||||
? 'opacity-100 visible translate-y-0'
|
||||
: 'opacity-0 invisible translate-y-2 pointer-events-none'
|
||||
)}
|
||||
>
|
||||
{/* 顶部操作栏 */}
|
||||
<div className="flex items-center justify-between px-3 py-2 border-b border-[var(--color-border-light)]">
|
||||
<span className="text-xs font-medium text-[var(--color-text-primary)]">
|
||||
快捷短语
|
||||
</span>
|
||||
<Tooltip content="添加短语">
|
||||
<button
|
||||
onClick={onAdd}
|
||||
className={cn(
|
||||
'w-6 h-6 flex items-center justify-center rounded-md transition-colors',
|
||||
'text-[var(--color-text-tertiary)] hover:text-[var(--color-primary)]',
|
||||
'hover:bg-[var(--color-bg-hover)]'
|
||||
)}
|
||||
>
|
||||
<Plus size={16} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{/* 短语列表 */}
|
||||
<div className="max-h-[320px] overflow-y-auto p-1.5">
|
||||
{phrases.length === 0 ? (
|
||||
<div className="text-center py-8 text-[var(--color-text-tertiary)]">
|
||||
<MessageSquareQuote
|
||||
size={32}
|
||||
className="mx-auto mb-2 opacity-50"
|
||||
/>
|
||||
<p className="text-sm">暂无快捷短语</p>
|
||||
<p className="text-xs mt-1">点击上方 + 添加</p>
|
||||
</div>
|
||||
) : (
|
||||
phrases.map((phrase) => (
|
||||
<QuickPhraseItem
|
||||
key={phrase.id}
|
||||
phrase={phrase}
|
||||
onSelect={handleSelect}
|
||||
onEdit={onEdit}
|
||||
onDelete={onDelete}
|
||||
showActions={true}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 底部操作栏 */}
|
||||
{phrases.length > 0 && (
|
||||
<div className="border-t border-[var(--color-border-light)] p-1.5">
|
||||
<button
|
||||
onClick={handleManage}
|
||||
className={cn(
|
||||
'w-full flex items-center justify-center gap-1.5 px-2.5 py-1.5 rounded-md transition-colors',
|
||||
'text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]',
|
||||
'hover:bg-[var(--color-bg-hover)]'
|
||||
)}
|
||||
>
|
||||
<Settings2 size={14} />
|
||||
<span>管理快捷短语</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user