import * as React from "react" import { ArrowLeft, Eye, Pencil, RotateCcw, Search, Send, ShieldCheck, Trash2, XCircle, } from "lucide-react" import type { RoleView, WorkflowConfig } from "@/App" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" type WorkflowStatus = "draft" | "prepublished" | "published" type TopicSection = { id: string title: string imageUrl: string specLeftLabel: string specRightLabel: string } type SourceSnapshot = { title: string subtitle: string highlights: string description: string ctaText: string imageUrls: string[] topicNavTitle?: string topicHeroTitle?: string topicSections?: TopicSection[] } type MiniAppContent = { id: string sourceCarId: string sourceCarName: string pageType: "车型页" | "活动页" | "专题页" title: string subtitle: string highlights: string description: string ctaText: string scheduledPublishAt: string imageUrls: string[] topicNavTitle?: string topicHeroTitle?: string topicSections?: TopicSection[] workflowStatus: WorkflowStatus updatedBy: string updatedAt: string } type MiniAppContentLibraryProps = { roleView: RoleView workflowConfig: WorkflowConfig onAddAuditLog: (message: string) => void newContentRequest?: { sourceCarId: string sourceCarName: string requestId: string } | null } const CONTENTS: MiniAppContent[] = [ { id: "c1", sourceCarId: "a3", sourceCarName: "Audi A3 Sportback", pageType: "车型页", title: "Audi A3 Sportback", subtitle: "进取,不负期待", highlights: "数字座舱\n城市通勤\n智能互联", description: "适合城市用户的豪华紧凑车型,兼顾效率与智能体验。", ctaText: "立即预约试驾", scheduledPublishAt: "", imageUrls: [ "/images/cars/a3-1.jpg", "/images/cars/a3-2.jpg", ], workflowStatus: "published", updatedBy: "内容运营专员", updatedAt: "2026-04-11 10:02", }, { id: "c2", sourceCarId: "a4l", sourceCarName: "Audi A4L", pageType: "活动页", title: "Audi A4L 四月限时礼遇", subtitle: "豪华与性能平衡", highlights: "quattro\n商务舒适\n限时礼遇", description: "面向商务人群的主推内容版本,突出舒适与操控。", ctaText: "获取活动详情", scheduledPublishAt: "2026-04-14T10:00", imageUrls: [ "/images/cars/a4-1.jpg", ], workflowStatus: "prepublished", updatedBy: "业务/市场负责人", updatedAt: "2026-04-11 09:48", }, { id: "c3", sourceCarId: "a6l", sourceCarName: "Audi A6L", pageType: "车型页", title: "Audi A6L", subtitle: "懂你,更懂未来", highlights: "行政旗舰\n长轴空间\n智能辅助", description: "正在进行文案优化,待业务审核。", ctaText: "预约顾问回电", scheduledPublishAt: "", imageUrls: [ "/images/cars/a6-1.jpg", "/images/cars/a6-2.jpg", ], workflowStatus: "prepublished", updatedBy: "内容运营专员", updatedAt: "2026-04-11 09:30", }, { id: "c4", sourceCarId: "q3", sourceCarName: "Audi Q3", pageType: "专题页", title: "Audi Q3 城市灵动版", subtitle: "年轻进阶,灵动出行", highlights: "紧凑SUV\n智能互联\n都市通勤", description: "新建草稿,待进一步补充图文和活动权益信息。", ctaText: "预约试驾", scheduledPublishAt: "", imageUrls: [ "/images/cars/q3-1.jpg", ], topicNavTitle: "Q3详情", topicHeroTitle: "先锋设计", topicSections: [ { id: "q3-topic-1", title: "RS竞速套件", imageUrl: "/images/cars/q3-1.jpg", specLeftLabel: "长", specRightLabel: "宽", }, { id: "q3-topic-2", title: "宽体低趴 超长轴距", imageUrl: "/images/cars/q3-2.jpg", specLeftLabel: "长", specRightLabel: "宽", }, ], workflowStatus: "draft", updatedBy: "内容运营专员", updatedAt: "2026-04-11 08:40", }, { id: "c5", sourceCarId: "q5l", sourceCarName: "Audi Q5L", pageType: "活动页", title: "Audi Q5L 周末试驾礼遇", subtitle: "自由,由我定义", highlights: "四驱性能\n家庭空间\n周末活动", description: "因权益文案与活动规则不一致,被驳回待修订。", ctaText: "了解礼遇", scheduledPublishAt: "", imageUrls: [ "/images/cars/q5-1.jpg", "/images/cars/q5-2.jpg", ], workflowStatus: "draft", updatedBy: "业务/市场负责人", updatedAt: "2026-04-11 08:10", }, { id: "c6", sourceCarId: "a8l", sourceCarName: "Audi A8L", pageType: "车型页", title: "Audi A8L 尊享礼宾版", subtitle: "旗舰格局,沉稳之选", highlights: "旗舰行政\n豪华座舱\n专属服务", description: "已发布后因活动档期调整撤回,待重新排期。", ctaText: "预约专属顾问", scheduledPublishAt: "2026-04-15T09:30", imageUrls: [ "/images/cars/a8-1.jpg", ], workflowStatus: "published", updatedBy: "业务/市场负责人", updatedAt: "2026-04-11 07:55", }, { id: "c7", sourceCarId: "rs7", sourceCarName: "Audi RS7", pageType: "专题页", title: "Audi RS7 Performance", subtitle: "高性能美学,锋芒尽释", highlights: "V8双涡轮\nquattro四驱\nRS专属运动套件", description: "聚焦高性能驾驶体验与豪华运动设计,适配高意向用户内容触达场景。", ctaText: "预约性能试驾", scheduledPublishAt: "", imageUrls: [ "/images/cars/rs7-1.jpg", "/images/cars/rs7-2.jpg", ], workflowStatus: "draft", updatedBy: "内容运营专员", updatedAt: "2026-04-12 10:20", }, ] const WORKFLOW_LABEL: Record = { draft: "草稿", prepublished: "预发布", published: "已发布", } const SOURCE_SNAPSHOTS: Record = { a3: { title: "Audi A3 Sportback", subtitle: "进取,不负期待", highlights: "数字座舱\n城市通勤\n智能互联", description: "来自官网车型库的车型基线内容。", ctaText: "立即预约试驾", imageUrls: [ "/images/cars/a3-1.jpg", "/images/cars/a3-2.jpg", ], }, a4l: { title: "Audi A4L", subtitle: "做更强大的自己", highlights: "quattro\n商务舒适\n智能互联", description: "来自官网车型库的车型基线内容。", ctaText: "了解更多", imageUrls: [ "/images/cars/a4-1.jpg", ], }, a6l: { title: "Audi A6L", subtitle: "懂你,更懂未来", highlights: "行政旗舰\n长轴空间\n智能辅助", description: "来自官网车型库的车型基线内容。", ctaText: "预约顾问回电", imageUrls: [ "/images/cars/a6-1.jpg", "/images/cars/a6-2.jpg", ], }, q3: { title: "Audi Q3", subtitle: "活出生命的辽阔", highlights: "紧凑SUV\n智能互联\n都市通勤", description: "来自官网车型库的车型基线内容。", ctaText: "立即预约试驾", imageUrls: [ "/images/cars/q3-1.jpg", "/images/cars/q3-2.jpg", ], }, q5l: { title: "Audi Q5L", subtitle: "自由,由我定义", highlights: "四驱性能\n家庭空间\n全场景出行", description: "来自官网车型库的车型基线内容。", ctaText: "预约试驾", imageUrls: [ "/images/cars/q5-1.jpg", "/images/cars/q5-2.jpg", ], }, a8l: { title: "Audi A8L", subtitle: "旗舰格局,沉稳之选", highlights: "旗舰行政\n豪华座舱\n专属服务", description: "来自官网车型库的车型基线内容。", ctaText: "预约专属顾问", imageUrls: [ "/images/cars/a8-1.jpg", "/images/cars/a8-2.jpg", ], }, rs7: { title: "Audi RS7 Performance", subtitle: "高性能美学,锋芒尽释", highlights: "V8双涡轮\nquattro四驱\nRS专属运动套件", description: "来自官网车型库的车型基线内容。", ctaText: "预约性能试驾", imageUrls: [ "/images/cars/rs7-1.jpg", "/images/cars/rs7-2.jpg", ], }, } const SOURCE_CAR_OPTIONS = [ { id: "a3", name: "Audi A3 Sportback" }, { id: "a4l", name: "Audi A4L" }, { id: "a6l", name: "Audi A6L" }, { id: "q3", name: "Audi Q3" }, { id: "q5l", name: "Audi Q5L" }, { id: "a8l", name: "Audi A8L" }, { id: "rs7", name: "Audi RS7" }, ] as const function createTopicSectionsFromImages(images: string[]) { const picks = images.slice(0, 2) const fallback = ["/images/cars/a7-1.jpg", "/images/cars/a7-2.jpg"] const selected = picks.length > 0 ? picks : fallback return selected.map((imageUrl, index) => ({ id: `topic-section-${index + 1}`, title: index === 0 ? "RS竞速套件" : "宽体低趴 超长轴距", imageUrl, specLeftLabel: "长", specRightLabel: "宽", })) } function buildTopicPatch(sourceCarName: string, snapshot?: SourceSnapshot): Pick { const hero = snapshot?.topicHeroTitle ?? snapshot?.subtitle ?? "先锋设计" return { topicNavTitle: snapshot?.topicNavTitle ?? `${sourceCarName || "车型"}详情`, topicHeroTitle: hero, topicSections: snapshot?.topicSections ?? createTopicSectionsFromImages(snapshot?.imageUrls ?? []), } } function workflowBadgeClass(status: WorkflowStatus) { switch (status) { case "draft": return "bg-gray-100 text-gray-700 border-gray-200" case "prepublished": return "bg-blue-50 text-blue-700 border-blue-200" case "published": return "bg-emerald-50 text-emerald-700 border-emerald-200" } } function nowString() { return new Date().toLocaleString("zh-CN", { hour12: false }) } export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog, newContentRequest }: MiniAppContentLibraryProps) { const [contents, setContents] = React.useState(CONTENTS) const [viewMode, setViewMode] = React.useState<"list" | "editor">("list") const [selectedId, setSelectedId] = React.useState(CONTENTS[0].id) const [query, setQuery] = React.useState("") const [newImageUrl, setNewImageUrl] = React.useState("") const [pendingResetPatch, setPendingResetPatch] = React.useState>>({}) const listPageSize = 7 const listTotalPages = 10 const listPage = 1 React.useEffect(() => { if (!newContentRequest) return const id = `c${newContentRequest.requestId}` const existing = contents.find((item) => item.id === id) if (!existing) { const draft: MiniAppContent = { id, sourceCarId: newContentRequest.sourceCarId, sourceCarName: newContentRequest.sourceCarName, pageType: "车型页", title: newContentRequest.sourceCarName, subtitle: "", highlights: "", description: "", ctaText: "立即预约试驾", scheduledPublishAt: "", imageUrls: [], topicNavTitle: `${newContentRequest.sourceCarName}详情`, topicHeroTitle: "先锋设计", topicSections: createTopicSectionsFromImages([]), workflowStatus: "draft", updatedBy: "内容运营专员", updatedAt: nowString(), } setContents((prev) => [draft, ...prev]) } setSelectedId(id) setViewMode("editor") }, [newContentRequest?.requestId]) const canEdit = roleView === "content-ops" const canApprove = roleView === "biz-market" const selected = contents.find((item) => item.id === selectedId) ?? null const filteredContents = contents.filter((item) => { const key = `${item.sourceCarName} ${item.sourceCarId} ${item.pageType} ${item.title}`.toLowerCase() return key.includes(query.toLowerCase()) }) const pagedContents = filteredContents.slice((listPage - 1) * listPageSize, listPage * listPageSize) const updateContent = (id: string, patch: Partial) => { setContents((prev) => prev.map((item) => (item.id === id ? { ...item, ...patch, updatedAt: nowString() } : item)) ) } const saveDraft = (item: MiniAppContent) => { const stagedPatch = pendingResetPatch[item.id] ?? {} updateContent(item.id, { ...stagedPatch, workflowStatus: "draft", updatedBy: "内容运营专员" }) setPendingResetPatch((prev) => { if (!prev[item.id]) return prev const next = { ...prev } delete next[item.id] return next }) if (Object.keys(stagedPatch).length > 0) { onAddAuditLog(`内容运营专员保存并重置 ${item.sourceCarName} 到官网源数据(回草稿)`) } else { onAddAuditLog(`内容运营专员保存 ${item.sourceCarName} 内容草稿`) } } const stageResetFromSource = (item: MiniAppContent) => { const source = SOURCE_SNAPSHOTS[item.sourceCarId] if (!source) return setPendingResetPatch((prev) => ({ ...prev, [item.id]: { title: source.title, subtitle: source.subtitle, highlights: source.highlights, description: source.description, ctaText: item.pageType === "专题页" ? "预约体验" : source.ctaText, imageUrls: item.pageType === "专题页" ? [] : source.imageUrls, ...buildTopicPatch(item.sourceCarName, source), }, })) onAddAuditLog(`内容运营专员发起 ${item.sourceCarName} 源数据重置(待保存生效)`) } const submitForReview = (item: MiniAppContent) => { updateContent(item.id, { workflowStatus: "prepublished", updatedBy: "内容运营专员" }) onAddAuditLog(`内容运营专员提交 ${item.sourceCarName} 进入预发布待审`) } const approve = (item: MiniAppContent) => { updateContent(item.id, { workflowStatus: "published", updatedBy: "业务/市场负责人" }) onAddAuditLog(`业务/市场负责人通过 ${item.sourceCarName},状态变为已发布`) } const reject = (item: MiniAppContent) => { updateContent(item.id, { workflowStatus: "draft", updatedBy: "业务/市场负责人" }) onAddAuditLog(`业务/市场负责人驳回 ${item.sourceCarName},状态退回草稿`) } const createNewVersion = (item: MiniAppContent) => { updateContent(item.id, { workflowStatus: "draft", updatedBy: "内容运营专员" }) onAddAuditLog(`内容运营专员基于 ${item.sourceCarName} 发布版本创建新稿`) } const createNewContent = () => { const id = `c${Date.now()}` const draft: MiniAppContent = { id, sourceCarId: "", sourceCarName: "", pageType: "车型页", title: "", subtitle: "", highlights: "", description: "", ctaText: "立即预约试驾", scheduledPublishAt: "", imageUrls: [], topicNavTitle: "车型详情", topicHeroTitle: "先锋设计", topicSections: createTopicSectionsFromImages([]), workflowStatus: "draft", updatedBy: "内容运营专员", updatedAt: nowString(), } setContents((prev) => [draft, ...prev]) setSelectedId(id) setViewMode("editor") onAddAuditLog("内容运营专员新增小程序内容草稿") } const deleteContent = (item: MiniAppContent) => { if (!window.confirm(`确认删除 ${item.sourceCarName || "当前草稿"} 吗?`)) return setContents((prev) => { const next = prev.filter((content) => content.id !== item.id) if (next.length > 0) { setSelectedId(next[0].id) } return next }) setViewMode("list") onAddAuditLog(`内容运营专员删除 ${item.sourceCarName || "未命名"} 内容草稿`) } const addImage = (item: MiniAppContent) => { const next = newImageUrl.trim() if (!next) return updateContent(item.id, { imageUrls: [...item.imageUrls, next] }) setNewImageUrl("") } const removeImage = (item: MiniAppContent, idx: number) => { updateContent(item.id, { imageUrls: item.imageUrls.filter((_, i) => i !== idx) }) } const updateTopicSection = (item: MiniAppContent, sectionId: string, patch: Partial) => { const sections = item.topicSections ?? [] updateContent(item.id, { topicSections: sections.map((section) => (section.id === sectionId ? { ...section, ...patch } : section)), }) } const addTopicSection = (item: MiniAppContent) => { const sections = item.topicSections ?? [] if (sections.length >= 6) return updateContent(item.id, { topicSections: [ ...sections, { id: `${item.id}-topic-${Date.now()}`, title: `专题模块 ${sections.length + 1}`, imageUrl: item.imageUrls[0] ?? "", specLeftLabel: "长", specRightLabel: "宽", }, ], }) } const removeTopicSection = (item: MiniAppContent, sectionId: string) => { const sections = item.topicSections ?? [] if (sections.length <= 1) return updateContent(item.id, { topicSections: sections.filter((section) => section.id !== sectionId), }) } const renderActions = (item: MiniAppContent) => { const actions: React.ReactNode[] = [] if (canEdit) { if (item.workflowStatus === "draft") { actions.push( ) } if (item.workflowStatus === "published") { actions.push( ) } } if (canApprove) { if (item.workflowStatus === "prepublished") { actions.push( ) actions.push( ) } } if (!canApprove) { actions.push( ) } return actions } if (viewMode === "editor" && selected) { const isNewContent = !selected.sourceCarId const canResetFromSource = Boolean(selected.sourceCarId && SOURCE_SNAPSHOTS[selected.sourceCarId]) const isTopicPage = selected.pageType === "专题页" const topicSections = (selected.topicSections && selected.topicSections.length > 0) ? selected.topicSections : createTopicSectionsFromImages(selected.imageUrls) const previewHeroImage = selected.imageUrls[0] const highlights = selected.highlights .split("\n") .map((s) => s.trim()) .filter(Boolean) return (

小程序内容编辑

独立编辑页:左侧字段编辑,右侧手机窗口预览。

当前状态:{WORKFLOW_LABEL[selected.workflowStatus]}
编辑字段 基于官网源车型生成的内容实体(MiniAppContent)。
{isNewContent && canEdit ? ( ) : ( )}
{isNewContent && canEdit ? ( ) : ( )}
{isTopicPage ? ( <>
updateContent(selected.id, { topicNavTitle: e.target.value })} />
updateContent(selected.id, { topicHeroTitle: e.target.value })} />
updateContent(selected.id, { ctaText: e.target.value })} />
{topicSections.map((section, index) => (

模块 {index + 1}

updateTopicSection(selected, section.id, { title: e.target.value })} placeholder="模块标题" /> updateTopicSection(selected, section.id, { imageUrl: e.target.value })} placeholder="模块图片 URL" />
))}
) : ( <>
updateContent(selected.id, { title: e.target.value })} />
updateContent(selected.id, { subtitle: e.target.value })} />