feat: 站点媒体库与移动端底部导航,服务测试补强
- 新增媒体库:media_library/media_thumbs 服务(WebP 缩略图)、管理端 media 页面 - 移动端底部导航 MobileTabBar 替换 MobilePostBar - 旧数据导入增强与测试、路由与登录会话/板块侧边栏测试补强 - site-doc 组件精简(移除 Breadcrumb),文档新增迁移公告说明
This commit is contained in:
@@ -88,11 +88,11 @@ export default function AdsAdmin({
|
||||
enabled: true,
|
||||
panel_title: "自助推广",
|
||||
durations: [
|
||||
{ days: 30, label: "1 个月", price_hint: "" },
|
||||
{ days: 60, label: "2 个月", price_hint: "" },
|
||||
{ days: 90, label: "3 个月", price_hint: "" },
|
||||
{ days: 180, label: "6 个月", price_hint: "" },
|
||||
{ days: 365, label: "12 个月", price_hint: "" },
|
||||
{ days: 30, label: "1 个月", price_hint: "", price_hint_text: "" },
|
||||
{ days: 60, label: "2 个月", price_hint: "", price_hint_text: "" },
|
||||
{ days: 90, label: "3 个月", price_hint: "", price_hint_text: "" },
|
||||
{ days: 180, label: "6 个月", price_hint: "", price_hint_text: "" },
|
||||
{ days: 365, label: "12 个月", price_hint: "", price_hint_text: "" },
|
||||
],
|
||||
payments: [],
|
||||
}
|
||||
@@ -489,7 +489,10 @@ export default function AdsAdmin({
|
||||
onClick={() =>
|
||||
setCfg({
|
||||
...cfg,
|
||||
durations: [...cfg.durations, { days: 14, label: "14 天", price_hint: "" }],
|
||||
durations: [
|
||||
...cfg.durations,
|
||||
{ days: 14, label: "14 天", price_hint: "", price_hint_text: "" },
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
@@ -500,43 +503,90 @@ export default function AdsAdmin({
|
||||
{cfg.durations.length === 0 ? (
|
||||
<p className="meta text-[13px]">至少保留一个时长档位。</p>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
{cfg.durations.map((d, i) => (
|
||||
<div key={i} className="j13-ads-row">
|
||||
<input
|
||||
type="number"
|
||||
className="field w-20"
|
||||
value={d.days}
|
||||
min={1}
|
||||
onChange={(e) => {
|
||||
const durations = [...cfg.durations];
|
||||
durations[i] = { ...d, days: Number(e.target.value) || 1 };
|
||||
setCfg({ ...cfg, durations });
|
||||
}}
|
||||
aria-label="天数"
|
||||
/>
|
||||
<input
|
||||
className="field min-w-0 flex-1"
|
||||
value={d.label}
|
||||
placeholder="标签,如 1 个月"
|
||||
onChange={(e) => {
|
||||
const durations = [...cfg.durations];
|
||||
durations[i] = { ...d, label: e.target.value };
|
||||
setCfg({ ...cfg, durations });
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-line btn-sm"
|
||||
aria-label="删除档位"
|
||||
onClick={() =>
|
||||
setCfg({ ...cfg, durations: cfg.durations.filter((_, j) => j !== i) })
|
||||
}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<div className="overflow-x-auto rounded-xl border" style={{ borderColor: "var(--line)" }}>
|
||||
<table className="w-full min-w-[600px] text-left text-[13px]">
|
||||
<thead>
|
||||
<tr className="meta text-[12px]" style={{ background: "var(--surface)" }}>
|
||||
<th className="px-3 py-2 font-medium">天数</th>
|
||||
<th className="px-3 py-2 font-medium">标签</th>
|
||||
<th className="px-3 py-2 font-medium">图片价格</th>
|
||||
<th className="px-3 py-2 font-medium">文字价格</th>
|
||||
<th className="px-3 py-2 text-right font-medium">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{cfg.durations.map((d, i) => (
|
||||
<tr key={i} style={{ borderTop: "1px solid var(--line)" }}>
|
||||
<td className="px-3 py-2">
|
||||
<input
|
||||
type="number"
|
||||
className="field w-20"
|
||||
value={d.days}
|
||||
min={1}
|
||||
onChange={(e) => {
|
||||
const durations = [...cfg.durations];
|
||||
durations[i] = { ...d, days: Number(e.target.value) || 1 };
|
||||
setCfg({ ...cfg, durations });
|
||||
}}
|
||||
aria-label="天数"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<input
|
||||
className="field w-full min-w-0"
|
||||
value={d.label}
|
||||
placeholder="如 1 个月"
|
||||
onChange={(e) => {
|
||||
const durations = [...cfg.durations];
|
||||
durations[i] = { ...d, label: e.target.value };
|
||||
setCfg({ ...cfg, durations });
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<input
|
||||
className="field w-full min-w-0"
|
||||
value={d.price_hint}
|
||||
maxLength={32}
|
||||
placeholder="如 ¥299,可空"
|
||||
onChange={(e) => {
|
||||
const durations = [...cfg.durations];
|
||||
durations[i] = { ...d, price_hint: e.target.value };
|
||||
setCfg({ ...cfg, durations });
|
||||
}}
|
||||
aria-label="图片广告价格"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<input
|
||||
className="field w-full min-w-0"
|
||||
value={d.price_hint_text}
|
||||
maxLength={32}
|
||||
placeholder="如 ¥99,可空"
|
||||
onChange={(e) => {
|
||||
const durations = [...cfg.durations];
|
||||
durations[i] = { ...d, price_hint_text: e.target.value };
|
||||
setCfg({ ...cfg, durations });
|
||||
}}
|
||||
aria-label="文字广告价格"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-right">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-line btn-sm"
|
||||
aria-label="删除档位"
|
||||
onClick={() =>
|
||||
setCfg({ ...cfg, durations: cfg.durations.filter((_, j) => j !== i) })
|
||||
}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</SubBlock>
|
||||
|
||||
Reference in New Issue
Block a user