重构代码,修改统计字数
This commit is contained in:
parent
97efa6ccdd
commit
bb60f67316
@ -23,9 +23,10 @@ import {
|
|||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
} from "@/components/ui/alert-dialog"
|
} from "@/components/ui/alert-dialog"
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||||
import { FileText, Trash2, Menu, Plus, Save } from 'lucide-react'
|
import { FileText, Trash2, Menu, Plus, Save, Edit2, Check } from 'lucide-react'
|
||||||
import { useToast } from '@/components/ui/use-toast'
|
import { useToast } from '@/components/ui/use-toast'
|
||||||
import { ToastAction } from '@/components/ui/toast'
|
import { ToastAction } from '@/components/ui/toast'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
|
||||||
interface Article {
|
interface Article {
|
||||||
id: string
|
id: string
|
||||||
@ -46,6 +47,9 @@ export function ArticleList({ onSelect, currentContent, onNew }: ArticleListProp
|
|||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
const [articles, setArticles] = useState<Article[]>([])
|
const [articles, setArticles] = useState<Article[]>([])
|
||||||
const [articleToDelete, setArticleToDelete] = useState<Article | null>(null)
|
const [articleToDelete, setArticleToDelete] = useState<Article | null>(null)
|
||||||
|
const [editingId, setEditingId] = useState<string | null>(null)
|
||||||
|
const [editingTitle, setEditingTitle] = useState('')
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
||||||
// 加载文章列表
|
// 加载文章列表
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -119,6 +123,7 @@ export function ArticleList({ onSelect, currentContent, onNew }: ArticleListProp
|
|||||||
// 如果有外部传入的新建处理函数,优先使用
|
// 如果有外部传入的新建处理函数,优先使用
|
||||||
if (onNew) {
|
if (onNew) {
|
||||||
onNew()
|
onNew()
|
||||||
|
setIsOpen(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,23 +131,92 @@ export function ArticleList({ onSelect, currentContent, onNew }: ArticleListProp
|
|||||||
const newArticle: Article = {
|
const newArticle: Article = {
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
title: '新文章',
|
title: '新文章',
|
||||||
content: '# 新文章\n\n开始写作...',
|
content: `# 新文章
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
在这里写文章的简介...
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
开始写作你的精彩内容...
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
在这里总结文章的主要观点...
|
||||||
|
|
||||||
|
---
|
||||||
|
> 作者:[你的名字]
|
||||||
|
> 日期:${new Date().toLocaleDateString()}
|
||||||
|
`,
|
||||||
template: 'default',
|
template: 'default',
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
updatedAt: Date.now()
|
updatedAt: Date.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存新文章到本地存储
|
||||||
|
const updatedArticles = [newArticle, ...articles]
|
||||||
|
setArticles(updatedArticles)
|
||||||
|
localStorage.setItem('wechat_articles', JSON.stringify(updatedArticles))
|
||||||
|
|
||||||
|
// 选中新文章并关闭列表
|
||||||
onSelect(newArticle)
|
onSelect(newArticle)
|
||||||
|
setIsOpen(false)
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "新建成功",
|
title: "新建成功",
|
||||||
description: "已创建新文章",
|
description: "已创建新文章,开始写作吧!",
|
||||||
duration: 2000
|
duration: 2000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 开始重命名
|
||||||
|
const startRename = (article: Article) => {
|
||||||
|
setEditingId(article.id)
|
||||||
|
setEditingTitle(article.title)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存重命名
|
||||||
|
const saveRename = (article: Article) => {
|
||||||
|
if (!editingTitle.trim()) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "重命名失败",
|
||||||
|
description: "文章标题不能为空",
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedArticles = articles.map(a => {
|
||||||
|
if (a.id === article.id) {
|
||||||
|
return {
|
||||||
|
...a,
|
||||||
|
title: editingTitle.trim(),
|
||||||
|
updatedAt: Date.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
})
|
||||||
|
|
||||||
|
setArticles(updatedArticles)
|
||||||
|
localStorage.setItem('wechat_articles', JSON.stringify(updatedArticles))
|
||||||
|
setEditingId(null)
|
||||||
|
setEditingTitle('')
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "重命名成功",
|
||||||
|
description: `文章已重命名为"${editingTitle.trim()}"`,
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消重命名
|
||||||
|
const cancelRename = () => {
|
||||||
|
setEditingId(null)
|
||||||
|
setEditingTitle('')
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Sheet>
|
<Sheet open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<SheetTrigger asChild>
|
<SheetTrigger asChild>
|
||||||
<Button variant="ghost" size="icon" className="relative">
|
<Button variant="ghost" size="icon" className="relative">
|
||||||
<Menu className="h-5 w-5" />
|
<Menu className="h-5 w-5" />
|
||||||
@ -175,27 +249,66 @@ export function ArticleList({ onSelect, currentContent, onNew }: ArticleListProp
|
|||||||
key={article.id}
|
key={article.id}
|
||||||
className="flex items-center justify-between p-2 rounded-md hover:bg-muted group"
|
className="flex items-center justify-between p-2 rounded-md hover:bg-muted group"
|
||||||
>
|
>
|
||||||
<button
|
{editingId === article.id ? (
|
||||||
onClick={() => onSelect(article)}
|
<div className="flex items-center gap-2 flex-1">
|
||||||
className="flex items-center gap-2 flex-1 text-left"
|
<Input
|
||||||
>
|
value={editingTitle}
|
||||||
<FileText className="h-4 w-4 text-muted-foreground" />
|
onChange={(e) => setEditingTitle(e.target.value)}
|
||||||
<div className="flex-1 min-w-0">
|
onKeyDown={(e) => {
|
||||||
<div className="font-medium truncate">{article.title}</div>
|
if (e.key === 'Enter') {
|
||||||
<div className="text-xs text-muted-foreground">
|
saveRename(article)
|
||||||
{new Date(article.updatedAt).toLocaleString()}
|
} else if (e.key === 'Escape') {
|
||||||
</div>
|
cancelRename()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="h-8"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => saveRename(article)}
|
||||||
|
className="h-8 w-8"
|
||||||
|
>
|
||||||
|
<Check className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
) : (
|
||||||
<Button
|
<>
|
||||||
variant="ghost"
|
<button
|
||||||
size="icon"
|
onClick={() => onSelect(article)}
|
||||||
className="shrink-0 hover:bg-destructive/10 hover:text-destructive transition-colors"
|
className="flex items-center gap-2 flex-1 text-left"
|
||||||
onClick={() => deleteArticle(article)}
|
>
|
||||||
>
|
<FileText className="h-4 w-4 text-muted-foreground" />
|
||||||
<Trash2 className="h-4 w-4 text-destructive" />
|
<div className="flex-1 min-w-0">
|
||||||
<span className="sr-only">删除</span>
|
<div className="font-medium truncate">{article.title}</div>
|
||||||
</Button>
|
<div className="text-xs text-muted-foreground">
|
||||||
|
{new Date(article.updatedAt).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||||
|
onClick={() => startRename(article)}
|
||||||
|
>
|
||||||
|
<Edit2 className="h-4 w-4" />
|
||||||
|
<span className="sr-only">重命名</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8 shrink-0 hover:bg-destructive/10 hover:text-destructive transition-colors"
|
||||||
|
onClick={() => deleteArticle(article)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 text-destructive" />
|
||||||
|
<span className="sr-only">删除</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{articles.length === 0 && (
|
{articles.length === 0 && (
|
@ -6,7 +6,7 @@ import { cn } from '@/lib/utils'
|
|||||||
import { WechatStylePicker } from '../../template/WechatStylePicker'
|
import { WechatStylePicker } from '../../template/WechatStylePicker'
|
||||||
import { TemplateManager } from '../../template/TemplateManager'
|
import { TemplateManager } from '../../template/TemplateManager'
|
||||||
import { StyleConfigDialog } from '../StyleConfigDialog'
|
import { StyleConfigDialog } from '../StyleConfigDialog'
|
||||||
import { ArticleList } from '../ArticleList'
|
import { ArticleList } from '@/components/ArticleList'
|
||||||
import { type Article } from '../constants'
|
import { type Article } from '../constants'
|
||||||
import { type RendererOptions } from '@/lib/markdown'
|
import { type RendererOptions } from '@/lib/markdown'
|
||||||
import { ThemeToggle } from '@/components/theme/ThemeToggle'
|
import { ThemeToggle } from '@/components/theme/ThemeToggle'
|
||||||
|
Loading…
Reference in New Issue
Block a user