feat: 媒体库 blob 去重存储与品牌/注册等前端调整
新增: - blob_store 二进制去重存储 + blob_dedup 测试 其它: - 后端 settings/auth/media_library/upload/operations 服务与 handler 调整 - 前端品牌(BrandLockup/BrandCropModal/BrandSeoPanel)、注册、媒体库、分析详情页、Header/SiteChrome 等更新
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Check, ImagePlus, Images, Link2, X } from "lucide-react";
|
||||
import { Check, Crop, ImagePlus, Images, Link2, X } from "lucide-react";
|
||||
import { useEffect, useRef, useState, type Dispatch, type SetStateAction } from "react";
|
||||
import {
|
||||
AdminSegmented,
|
||||
@@ -11,8 +11,6 @@ import BrandCropModal from "@/components/BrandCropModal";
|
||||
import BrandLockup from "@/components/BrandLockup";
|
||||
import { apiBrandFromMedia, apiUpdateSiteSettings, apiUploadBrand, type PublicSettings } from "@/lib/api";
|
||||
import {
|
||||
BRAND_LOGO_FITS,
|
||||
BRAND_LOGO_SIZES,
|
||||
brandAccept,
|
||||
brandAliasPath,
|
||||
brandFileOk,
|
||||
@@ -313,6 +311,17 @@ export default function BrandSeoPanel({
|
||||
>
|
||||
<Images size={14} /> 库
|
||||
</button>
|
||||
{preview && !/\.(svg|ico)(\?.*)?$/i.test(preview) ? (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-line btn-sm"
|
||||
disabled={saving}
|
||||
title="调整画幅、铺法或重新裁剪"
|
||||
onClick={() => openCrop(slot, null, preview)}
|
||||
>
|
||||
<Crop size={14} /> 调整
|
||||
</button>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className="admin-icon-btn"
|
||||
@@ -361,7 +370,7 @@ export default function BrandSeoPanel({
|
||||
frame
|
||||
name={initial.site_name}
|
||||
wordmark={initial.site_wordmark}
|
||||
slogan={initial.site_slogan}
|
||||
slogan={initial.brand_slogan_visible ? initial.site_slogan : ""}
|
||||
logoLight={previewOf(light)}
|
||||
logoDark={previewOf(dark)}
|
||||
/>
|
||||
@@ -388,46 +397,9 @@ export default function BrandSeoPanel({
|
||||
/>
|
||||
</div>
|
||||
{mark !== "text" ? (
|
||||
<>
|
||||
<div className="admin-brand-studio-field">
|
||||
<span className="admin-brand-studio-kicker">比例</span>
|
||||
<div className="admin-brand-size-row" role="group" aria-label="Logo 比例">
|
||||
{BRAND_LOGO_SIZES.map((item) => {
|
||||
const on = logoSize === item.id;
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
type="button"
|
||||
className={`admin-brand-size-chip${on ? " is-on" : ""}`}
|
||||
onClick={() => {
|
||||
setLogoSize(item.id);
|
||||
if (saveError) setSaveError("");
|
||||
}}
|
||||
>
|
||||
<span className="admin-brand-size-chip-label">{item.label}</span>
|
||||
<span className="admin-brand-size-chip-px">
|
||||
{item.w}×{item.h}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="admin-brand-studio-field">
|
||||
<span className="admin-brand-studio-kicker">铺法</span>
|
||||
<AdminSegmented
|
||||
role="group"
|
||||
size="sm"
|
||||
ariaLabel="适配方式"
|
||||
value={logoFit}
|
||||
onChange={(v) => {
|
||||
setLogoFit(v);
|
||||
if (saveError) setSaveError("");
|
||||
}}
|
||||
options={BRAND_LOGO_FITS.map((item) => ({ key: item.id, label: item.label }))}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
<p className="admin-brand-studio-field text-[11.5px] self-center mb-0.5" style={{ color: "var(--ink-3)" }}>
|
||||
画幅与铺法在上传/调整图片的裁剪弹窗中设置
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -471,6 +443,16 @@ export default function BrandSeoPanel({
|
||||
slot={cropSlot}
|
||||
seedFile={cropSeedFile}
|
||||
seedUrl={cropSeedUrl}
|
||||
logoSize={logoSize}
|
||||
logoFit={logoFit}
|
||||
onSizeChange={(s) => {
|
||||
setLogoSize(s);
|
||||
if (saveError) setSaveError("");
|
||||
}}
|
||||
onFitChange={(f) => {
|
||||
setLogoFit(f);
|
||||
if (saveError) setSaveError("");
|
||||
}}
|
||||
onClose={closeCrop}
|
||||
onSeedConsumed={() => {
|
||||
setCropSeedFile(null);
|
||||
|
||||
Reference in New Issue
Block a user