2nd version

This commit is contained in:
2026-04-15 17:08:17 +08:00
parent e2a2eb4666
commit cedeb8f8bc
44 changed files with 5378 additions and 346 deletions

View File

@@ -3,10 +3,11 @@ import {
ArrowLeft,
Eye,
Pencil,
RotateCcw,
Search,
Send,
ShieldCheck,
Undo2,
Upload,
Trash2,
XCircle,
} from "lucide-react"
@@ -24,12 +25,22 @@ import {
TableRow,
} from "@/components/ui/table"
type WorkflowStatus = "draft" | "pending" | "prepublished" | "published" | "rejected" | "recalled"
type WorkflowStatus = "draft" | "prepublished" | "published"
type SourceSnapshot = {
title: string
subtitle: string
highlights: string
description: string
ctaText: string
imageUrls: string[]
}
type MiniAppContent = {
id: string
sourceCarId: string
sourceCarName: string
pageType: "车型页" | "活动页" | "专题页"
title: string
subtitle: string
highlights: string
@@ -58,6 +69,7 @@ const CONTENTS: MiniAppContent[] = [
id: "c1",
sourceCarId: "a3",
sourceCarName: "Audi A3 Sportback",
pageType: "车型页",
title: "Audi A3 Sportback",
subtitle: "进取,不负期待",
highlights: "数字座舱\n城市通勤\n智能互联",
@@ -65,9 +77,8 @@ const CONTENTS: MiniAppContent[] = [
ctaText: "立即预约试驾",
scheduledPublishAt: "",
imageUrls: [
"https://images.unsplash.com/photo-1606152421802-db97b9c7a11b?auto=format&fit=crop&q=80&w=1200",
"https://picsum.photos/seed/a3-content-1/900/600",
"https://picsum.photos/seed/a3-content-2/900/600",
"/images/cars/a3-1.jpg",
"/images/cars/a3-2.jpg",
],
workflowStatus: "published",
updatedBy: "内容运营专员",
@@ -77,6 +88,7 @@ const CONTENTS: MiniAppContent[] = [
id: "c2",
sourceCarId: "a4l",
sourceCarName: "Audi A4L",
pageType: "活动页",
title: "Audi A4L 四月限时礼遇",
subtitle: "豪华与性能平衡",
highlights: "quattro\n商务舒适\n限时礼遇",
@@ -84,8 +96,7 @@ const CONTENTS: MiniAppContent[] = [
ctaText: "获取活动详情",
scheduledPublishAt: "2026-04-14T10:00",
imageUrls: [
"https://images.unsplash.com/photo-1614162692292-7ac56d7f7f1e?auto=format&fit=crop&q=80&w=1200",
"https://picsum.photos/seed/a4-content-1/900/600",
"/images/cars/a4-1.jpg",
],
workflowStatus: "prepublished",
updatedBy: "业务/市场负责人",
@@ -95,6 +106,7 @@ const CONTENTS: MiniAppContent[] = [
id: "c3",
sourceCarId: "a6l",
sourceCarName: "Audi A6L",
pageType: "车型页",
title: "Audi A6L",
subtitle: "懂你,更懂未来",
highlights: "行政旗舰\n长轴空间\n智能辅助",
@@ -102,9 +114,10 @@ const CONTENTS: MiniAppContent[] = [
ctaText: "预约顾问回电",
scheduledPublishAt: "",
imageUrls: [
"https://images.unsplash.com/photo-1541348263662-e0c86433610a?auto=format&fit=crop&q=80&w=1200",
"/images/cars/a6-1.jpg",
"/images/cars/a6-2.jpg",
],
workflowStatus: "pending",
workflowStatus: "prepublished",
updatedBy: "内容运营专员",
updatedAt: "2026-04-11 09:30",
},
@@ -112,6 +125,7 @@ const CONTENTS: MiniAppContent[] = [
id: "c4",
sourceCarId: "q3",
sourceCarName: "Audi Q3",
pageType: "专题页",
title: "Audi Q3 城市灵动版",
subtitle: "年轻进阶,灵动出行",
highlights: "紧凑SUV\n智能互联\n都市通勤",
@@ -119,7 +133,7 @@ const CONTENTS: MiniAppContent[] = [
ctaText: "预约试驾",
scheduledPublishAt: "",
imageUrls: [
"https://picsum.photos/seed/q3-content-1/900/600",
"/images/cars/q3-1.jpg",
],
workflowStatus: "draft",
updatedBy: "内容运营专员",
@@ -129,6 +143,7 @@ const CONTENTS: MiniAppContent[] = [
id: "c5",
sourceCarId: "q5l",
sourceCarName: "Audi Q5L",
pageType: "活动页",
title: "Audi Q5L 周末试驾礼遇",
subtitle: "自由,由我定义",
highlights: "四驱性能\n家庭空间\n周末活动",
@@ -136,9 +151,10 @@ const CONTENTS: MiniAppContent[] = [
ctaText: "了解礼遇",
scheduledPublishAt: "",
imageUrls: [
"https://picsum.photos/seed/q5-content-1/900/600",
"/images/cars/q5-1.jpg",
"/images/cars/q5-2.jpg",
],
workflowStatus: "rejected",
workflowStatus: "draft",
updatedBy: "业务/市场负责人",
updatedAt: "2026-04-11 08:10",
},
@@ -146,6 +162,7 @@ const CONTENTS: MiniAppContent[] = [
id: "c6",
sourceCarId: "a8l",
sourceCarName: "Audi A8L",
pageType: "车型页",
title: "Audi A8L 尊享礼宾版",
subtitle: "旗舰格局,沉稳之选",
highlights: "旗舰行政\n豪华座舱\n专属服务",
@@ -153,37 +170,136 @@ const CONTENTS: MiniAppContent[] = [
ctaText: "预约专属顾问",
scheduledPublishAt: "2026-04-15T09:30",
imageUrls: [
"https://picsum.photos/seed/a8-content-1/900/600",
"/images/cars/a8-1.jpg",
],
workflowStatus: "recalled",
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<WorkflowStatus, string> = {
draft: "草稿",
pending: "待审核",
prepublished: "预发布",
published: "已发布",
rejected: "已驳回",
recalled: "已撤回",
}
const SOURCE_SNAPSHOTS: Record<string, SourceSnapshot> = {
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 workflowBadgeClass(status: WorkflowStatus) {
switch (status) {
case "draft":
return "bg-gray-100 text-gray-700 border-gray-200"
case "pending":
return "bg-amber-50 text-amber-700 border-amber-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"
case "rejected":
return "bg-rose-50 text-rose-700 border-rose-200"
case "recalled":
return "bg-zinc-100 text-zinc-700 border-zinc-200"
}
}
@@ -195,7 +311,12 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
const [contents, setContents] = React.useState<MiniAppContent[]>(CONTENTS)
const [viewMode, setViewMode] = React.useState<"list" | "editor">("list")
const [selectedId, setSelectedId] = React.useState<string>(CONTENTS[0].id)
const [query, setQuery] = React.useState("")
const [newImageUrl, setNewImageUrl] = React.useState("")
const [pendingResetPatch, setPendingResetPatch] = React.useState<Record<string, Partial<MiniAppContent>>>({})
const listPageSize = 7
const listTotalPages = 10
const listPage = 1
React.useEffect(() => {
if (!newContentRequest) return
@@ -207,6 +328,7 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
id,
sourceCarId: newContentRequest.sourceCarId,
sourceCarName: newContentRequest.sourceCarName,
pageType: "车型页",
title: newContentRequest.sourceCarName,
subtitle: "",
highlights: "",
@@ -228,6 +350,11 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
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<MiniAppContent>) => {
setContents((prev) =>
@@ -236,46 +363,51 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
}
const saveDraft = (item: MiniAppContent) => {
updateContent(item.id, { workflowStatus: "draft", updatedBy: "内容运营专员" })
onAddAuditLog(`内容运营专员保存 ${item.sourceCarName} 内容草稿`)
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: source.ctaText,
imageUrls: source.imageUrls,
},
}))
onAddAuditLog(`内容运营专员发起 ${item.sourceCarName} 源数据重置(待保存生效)`)
}
const submitForReview = (item: MiniAppContent) => {
updateContent(item.id, { workflowStatus: "pending", updatedBy: "内容运营专员" })
onAddAuditLog(`内容运营专员提交 ${item.sourceCarName} 到审核队列`)
}
const withdrawReview = (item: MiniAppContent) => {
updateContent(item.id, { workflowStatus: "draft", updatedBy: "内容运营专员" })
onAddAuditLog(`内容运营专员撤回 ${item.sourceCarName} 的审核申请`)
updateContent(item.id, { workflowStatus: "prepublished", updatedBy: "内容运营专员" })
onAddAuditLog(`内容运营专员提交 ${item.sourceCarName} 进入预发布待审`)
}
const approve = (item: MiniAppContent) => {
const nextStatus: WorkflowStatus = workflowConfig.enablePrePublish ? "prepublished" : "published"
updateContent(item.id, { workflowStatus: nextStatus, updatedBy: "业务/市场负责人" })
onAddAuditLog(`业务/市场负责人审批通过 ${item.sourceCarName}`)
updateContent(item.id, { workflowStatus: "published", updatedBy: "业务/市场负责人" })
onAddAuditLog(`业务/市场负责人通过 ${item.sourceCarName},状态变为已发布`)
}
const reject = (item: MiniAppContent) => {
updateContent(item.id, { workflowStatus: "rejected", updatedBy: "业务/市场负责人" })
onAddAuditLog(`业务/市场负责人驳回 ${item.sourceCarName} 内容版本`)
}
const publish = (item: MiniAppContent) => {
const actor = roleView === "biz-market" ? "业务/市场负责人" : "内容运营专员"
updateContent(item.id, { workflowStatus: "published", updatedBy: actor })
onAddAuditLog(`${actor}发布 ${item.sourceCarName} 到小程序`)
}
const recall = (item: MiniAppContent) => {
updateContent(item.id, { workflowStatus: "recalled", updatedBy: "业务/市场负责人" })
onAddAuditLog(`业务/市场负责人撤回 ${item.sourceCarName} 已发布内容`)
}
const revive = (item: MiniAppContent) => {
const nextStatus: WorkflowStatus = workflowConfig.enablePrePublish ? "prepublished" : "published"
updateContent(item.id, { workflowStatus: nextStatus, updatedBy: "业务/市场负责人" })
onAddAuditLog(`业务/市场负责人恢复 ${item.sourceCarName} 的发布流程`)
updateContent(item.id, { workflowStatus: "draft", updatedBy: "业务/市场负责人" })
onAddAuditLog(`业务/市场负责人驳回 ${item.sourceCarName},状态退回草稿`)
}
const createNewVersion = (item: MiniAppContent) => {
@@ -283,6 +415,44 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
onAddAuditLog(`内容运营专员基于 ${item.sourceCarName} 发布版本创建新稿`)
}
const createNewContent = () => {
const id = `c${Date.now()}`
const draft: MiniAppContent = {
id,
sourceCarId: "",
sourceCarName: "",
pageType: "车型页",
title: "",
subtitle: "",
highlights: "",
description: "",
ctaText: "立即预约试驾",
scheduledPublishAt: "",
imageUrls: [],
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
@@ -298,24 +468,10 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
const actions: React.ReactNode[] = []
if (canEdit) {
if (item.workflowStatus === "draft" || item.workflowStatus === "rejected" || item.workflowStatus === "recalled") {
if (item.workflowStatus === "draft") {
actions.push(
<Button key="submit" size="sm" variant="outline" onClick={() => submitForReview(item)}>
<Send className="mr-1 h-3.5 w-3.5" />{item.workflowStatus === "draft" ? "提交审核" : "重新提审"}
</Button>
)
}
if (item.workflowStatus === "pending") {
actions.push(
<Button key="withdraw" size="sm" variant="outline" onClick={() => withdrawReview(item)}>
<Undo2 className="mr-1 h-3.5 w-3.5" />
</Button>
)
}
if (item.workflowStatus === "prepublished") {
actions.push(
<Button key="publish" size="sm" variant="outline" onClick={() => publish(item)}>
<Upload className="mr-1 h-3.5 w-3.5" />
<Send className="mr-1 h-3.5 w-3.5" />
</Button>
)
}
@@ -329,7 +485,7 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
}
if (canApprove) {
if (item.workflowStatus === "pending") {
if (item.workflowStatus === "prepublished") {
actions.push(
<Button key="approve" size="sm" variant="outline" onClick={() => approve(item)}>
<ShieldCheck className="mr-1 h-3.5 w-3.5" />
@@ -341,32 +497,23 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
</Button>
)
}
if (workflowConfig.allowRecall && item.workflowStatus === "published") {
actions.push(
<Button key="recall" size="sm" variant="outline" onClick={() => recall(item)}>
<Undo2 className="mr-1 h-3.5 w-3.5" />
</Button>
)
}
if (item.workflowStatus === "recalled") {
actions.push(
<Button key="revive" size="sm" variant="outline" onClick={() => revive(item)}>
<Upload className="mr-1 h-3.5 w-3.5" />
</Button>
)
}
}
actions.push(
<Button key="detail" size="sm" variant="ghost" onClick={() => { setSelectedId(item.id); setViewMode("editor") }}>
<Eye className="mr-1 h-3.5 w-3.5" />
</Button>
)
if (!canApprove) {
actions.push(
<Button key="detail" size="sm" variant="ghost" onClick={() => { setSelectedId(item.id); setViewMode("editor") }}>
<Eye className="mr-1 h-3.5 w-3.5" />
</Button>
)
}
return actions
}
if (viewMode === "editor" && selected) {
const isNewContent = !selected.sourceCarId
const canResetFromSource = Boolean(selected.sourceCarId && SOURCE_SNAPSHOTS[selected.sourceCarId])
const previewHeroImage = selected.imageUrls[0]
const highlights = selected.highlights
.split("\n")
.map((s) => s.trim())
@@ -398,7 +545,54 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
<CardContent className="space-y-4">
<div className="grid gap-2">
<label className="text-xs font-semibold tracking-wide text-muted-foreground uppercase"></label>
<Input value={selected.sourceCarName} disabled />
{isNewContent && canEdit ? (
<select
className="h-10 rounded-lg border border-input bg-background px-3 text-sm"
value={selected.sourceCarId}
onChange={(e) => {
const nextId = e.target.value
const option = SOURCE_CAR_OPTIONS.find((item) => item.id === nextId)
const snapshot = SOURCE_SNAPSHOTS[nextId]
updateContent(selected.id, {
sourceCarId: nextId,
sourceCarName: option?.name ?? "",
title: snapshot?.title ?? selected.title,
subtitle: snapshot?.subtitle ?? selected.subtitle,
highlights: snapshot?.highlights ?? selected.highlights,
description: snapshot?.description ?? selected.description,
ctaText: snapshot?.ctaText ?? selected.ctaText,
imageUrls: snapshot?.imageUrls ?? selected.imageUrls,
})
if (snapshot) {
onAddAuditLog(`内容运营专员选择来源车型 ${option?.name ?? nextId} 并载入官网基线内容`)
}
}}
>
<option value=""></option>
{SOURCE_CAR_OPTIONS.map((option) => (
<option key={option.id} value={option.id}>{option.name}</option>
))}
</select>
) : (
<Input value={selected.sourceCarName} disabled />
)}
</div>
<div className="grid gap-2">
<label className="text-xs font-semibold tracking-wide text-muted-foreground uppercase"></label>
{isNewContent && canEdit ? (
<select
className="h-10 rounded-lg border border-input bg-background px-3 text-sm"
value={selected.pageType}
onChange={(e) => updateContent(selected.id, { pageType: e.target.value as MiniAppContent["pageType"] })}
>
<option value="车型页"></option>
<option value="活动页"></option>
<option value="专题页"></option>
</select>
) : (
<Input value={selected.pageType} disabled />
)}
</div>
<div className="grid gap-2">
@@ -468,13 +662,29 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
</div>
</div>
<div className="flex gap-2 pt-2">
<Button variant="outline" disabled={!canEdit} onClick={() => saveDraft(selected)}>
<Pencil className="mr-1 h-3.5 w-3.5" />稿
</Button>
<Button disabled={!canEdit} onClick={() => submitForReview(selected)}>
<Send className="mr-1 h-3.5 w-3.5" />
</Button>
{pendingResetPatch[selected.id] && (
<div className="rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-700">
稿稿
</div>
)}
<div className="flex items-center justify-between gap-3 pt-2">
<div className="flex gap-2">
<Button variant="outline" disabled={!canEdit} onClick={() => saveDraft(selected)}>
<Pencil className="mr-1 h-3.5 w-3.5" />稿
</Button>
<Button disabled={!canEdit || selected.workflowStatus !== "draft"} onClick={() => submitForReview(selected)}>
<Send className="mr-1 h-3.5 w-3.5" />
</Button>
</div>
<div className="flex gap-2">
<Button variant="outline" disabled={!canEdit || !canResetFromSource} onClick={() => stageResetFromSource(selected)}>
<RotateCcw className="mr-1 h-3.5 w-3.5" />
</Button>
<Button variant="outline" disabled={!canEdit} onClick={() => deleteContent(selected)}>
<Trash2 className="mr-1 h-3.5 w-3.5" />
</Button>
</div>
</div>
</CardContent>
</Card>
@@ -482,13 +692,17 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
<Card className="border-none shadow-sm bg-white">
<CardHeader>
<CardTitle className="text-lg font-bold"></CardTitle>
<CardDescription></CardDescription>
<CardDescription> CRM</CardDescription>
</CardHeader>
<CardContent className="flex justify-center">
<div className="w-[360px] rounded-[28px] border border-gray-200 bg-white shadow-2xl overflow-hidden">
<div className="h-9 bg-black text-white px-4 flex items-center text-[10px] tracking-[0.18em] uppercase">Audi</div>
<div className="relative">
<img src={selected.imageUrls[0]} alt={selected.title} className="h-48 w-full object-cover" />
{previewHeroImage ? (
<img src={previewHeroImage} alt={selected.title} className="h-48 w-full object-cover" />
) : (
<div className="flex h-48 w-full items-center justify-center bg-gray-100 text-xs text-gray-500"></div>
)}
<div className="absolute inset-0 bg-gradient-to-t from-black/65 to-transparent" />
<div className="absolute left-4 right-4 bottom-3 text-white">
<p className="text-lg font-bold tracking-tight">{selected.title}</p>
@@ -548,7 +762,7 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
<label className="flex items-start gap-2 text-[11px] leading-5 text-gray-600">
<input type="checkbox" className="mt-0.5" />
<span>
CRM
</span>
</label>
</div>
@@ -570,20 +784,37 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
<div className="flex items-center justify-between">
<div className="flex flex-col gap-1">
<h1 className="text-3xl font-bold tracking-tight"></h1>
<p className="text-muted-foreground"></p>
<p className="text-muted-foreground"></p>
</div>
</div>
<Card className="border-none shadow-sm bg-white">
<CardHeader>
<CardTitle className="text-lg font-bold"></CardTitle>
<CardDescription></CardDescription>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg font-bold"></CardTitle>
<CardDescription></CardDescription>
</div>
<Button onClick={createNewContent} className="bg-audi-black hover:bg-audi-dark-gray text-white">
<Pencil className="mr-1 h-3.5 w-3.5" />
</Button>
</div>
<div className="flex items-center gap-2 w-full max-w-sm">
<Search className="h-4 w-4 text-muted-foreground absolute ml-3" />
<Input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="搜索官网车型名称或待处理项..."
className="pl-9 bg-gray-50/50 border-none"
/>
</div>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow className="hover:bg-transparent border-b border-gray-100">
<TableHead className="font-bold text-audi-black"></TableHead>
<TableHead className="font-bold text-audi-black"></TableHead>
<TableHead className="font-bold text-audi-black"></TableHead>
<TableHead className="font-bold text-audi-black"></TableHead>
<TableHead className="font-bold text-audi-black"></TableHead>
@@ -591,12 +822,15 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
</TableRow>
</TableHeader>
<TableBody>
{contents.map((item) => (
{pagedContents.map((item) => (
<TableRow key={item.id} className="hover:bg-gray-50/50 border-b border-gray-50 transition-colors">
<TableCell>
<p className="font-medium">{item.sourceCarName}</p>
<p className="text-xs text-muted-foreground">{item.sourceCarId}</p>
</TableCell>
<TableCell>
<Badge variant="outline">{item.pageType}</Badge>
</TableCell>
<TableCell className="font-medium">{item.title}</TableCell>
<TableCell>
<Badge variant="outline" className={workflowBadgeClass(item.workflowStatus)}>
@@ -614,6 +848,25 @@ export function MiniAppContentLibrary({ roleView, workflowConfig, onAddAuditLog,
))}
</TableBody>
</Table>
<div className="mt-4 flex items-center justify-between border-t pt-4">
<p className="text-xs text-muted-foreground"> 7 10 </p>
<div className="flex items-center gap-1">
<Button size="sm" variant="outline" disabled></Button>
{Array.from({ length: listTotalPages }, (_, index) => (
<Button
key={`mini-page-${index + 1}`}
size="sm"
variant={index + 1 === listPage ? "default" : "outline"}
className={index + 1 === listPage ? "bg-audi-black hover:bg-audi-dark-gray text-white" : ""}
disabled
>
{index + 1}
</Button>
))}
<Button size="sm" variant="outline" disabled></Button>
</div>
</div>
</CardContent>
</Card>
</div>