|
|
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
|
|
Trash2,
|
|
|
|
|
X,
|
|
|
|
|
} from 'lucide-react'
|
|
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent, type ReactNode } from 'react'
|
|
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState, type ChangeEvent, type CSSProperties, type DragEvent as ReactDragEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from 'react'
|
|
|
|
|
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
|
|
|
|
import MarkdownRenderer from '../../components/MarkdownRenderer/MarkdownRenderer'
|
|
|
|
|
import Scrollbar from '../../components/Scrollbar/Scrollbar'
|
|
|
|
|
@@ -132,6 +132,8 @@ type DatasourceMetricBaseline = {
|
|
|
|
|
count: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BrandAssetTargetKey = 'logo_src' | 'title_src'
|
|
|
|
|
|
|
|
|
|
const DEFAULT_DATASOURCE_FILTERS: DatasourceFilters = {
|
|
|
|
|
product: '',
|
|
|
|
|
module: '',
|
|
|
|
|
@@ -871,6 +873,14 @@ type FieldConfig = {
|
|
|
|
|
placeholder?: string
|
|
|
|
|
disabled?: boolean
|
|
|
|
|
help?: string
|
|
|
|
|
renderInput?: (props: {
|
|
|
|
|
disabled?: boolean
|
|
|
|
|
displayValue: unknown
|
|
|
|
|
fieldKey: string
|
|
|
|
|
onChange: (value: unknown) => void
|
|
|
|
|
placeholder?: string
|
|
|
|
|
value: unknown
|
|
|
|
|
}) => ReactNode
|
|
|
|
|
inputAction?: {
|
|
|
|
|
ariaLabel?: string
|
|
|
|
|
disabled?: boolean
|
|
|
|
|
@@ -2301,6 +2311,9 @@ function FieldGrid({
|
|
|
|
|
const className = field.wide ? 'an-field an-field--wide' : 'an-field'
|
|
|
|
|
const searchTarget = searchGroupKey ? `${searchGroupKey}:field:${field.key}` : `field:${field.key}`
|
|
|
|
|
const searchText = [label, field.key, text(displayValue, '')].filter(Boolean).join(' ')
|
|
|
|
|
const commitFieldValue = (nextValue: unknown) => {
|
|
|
|
|
onDraftChange(setNestedDraftField(draft, record, field.key, nextValue))
|
|
|
|
|
}
|
|
|
|
|
if (field.type === 'boolean') {
|
|
|
|
|
return (
|
|
|
|
|
<label key={field.key} className="an-checkbox-row an-field--wide" data-admin-search-target={searchTarget} data-admin-search-field={field.key} data-admin-search-text={searchText}>
|
|
|
|
|
@@ -2308,7 +2321,7 @@ function FieldGrid({
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={Boolean(value)}
|
|
|
|
|
disabled={field.disabled}
|
|
|
|
|
onChange={(event) => onDraftChange(setNestedDraftField(draft, record, field.key, event.target.checked))}
|
|
|
|
|
onChange={(event) => commitFieldValue(event.target.checked)}
|
|
|
|
|
/>
|
|
|
|
|
{label}
|
|
|
|
|
{help ? <small>{help}</small> : null}
|
|
|
|
|
@@ -2323,7 +2336,7 @@ function FieldGrid({
|
|
|
|
|
className="an-input"
|
|
|
|
|
value={text(value, '')}
|
|
|
|
|
disabled={field.disabled}
|
|
|
|
|
onChange={(event) => onDraftChange(setNestedDraftField(draft, record, field.key, event.target.value))}
|
|
|
|
|
onChange={(event) => commitFieldValue(event.target.value)}
|
|
|
|
|
>
|
|
|
|
|
{(field.options || []).map((option) => (
|
|
|
|
|
<option key={option.value} value={option.value}>
|
|
|
|
|
@@ -2346,9 +2359,9 @@ function FieldGrid({
|
|
|
|
|
onChange={(event) => {
|
|
|
|
|
const nextValue = event.target.value
|
|
|
|
|
try {
|
|
|
|
|
onDraftChange(setNestedDraftField(draft, record, field.key, JSON.parse(nextValue)))
|
|
|
|
|
commitFieldValue(JSON.parse(nextValue))
|
|
|
|
|
} catch {
|
|
|
|
|
onDraftChange(setNestedDraftField(draft, record, field.key, nextValue))
|
|
|
|
|
commitFieldValue(nextValue)
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
spellCheck={false}
|
|
|
|
|
@@ -2401,6 +2414,22 @@ function FieldGrid({
|
|
|
|
|
</label>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
if (field.renderInput) {
|
|
|
|
|
return (
|
|
|
|
|
<div key={field.key} className={className} data-admin-search-target={searchTarget} data-admin-search-field={field.key} data-admin-search-text={searchText}>
|
|
|
|
|
<span>{label}</span>
|
|
|
|
|
{field.renderInput({
|
|
|
|
|
disabled: field.disabled,
|
|
|
|
|
displayValue,
|
|
|
|
|
fieldKey: field.key,
|
|
|
|
|
onChange: commitFieldValue,
|
|
|
|
|
placeholder,
|
|
|
|
|
value,
|
|
|
|
|
})}
|
|
|
|
|
{help ? <small>{help}</small> : null}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
const inputAction = field.inputAction
|
|
|
|
|
? {
|
|
|
|
|
...field.inputAction,
|
|
|
|
|
@@ -2421,12 +2450,7 @@ function FieldGrid({
|
|
|
|
|
value={text(displayValue, '')}
|
|
|
|
|
placeholder={placeholder}
|
|
|
|
|
disabled={field.disabled}
|
|
|
|
|
onChange={(event) => onDraftChange(setNestedDraftField(
|
|
|
|
|
draft,
|
|
|
|
|
record,
|
|
|
|
|
field.key,
|
|
|
|
|
field.type === 'number' ? Number(event.target.value) : event.target.value,
|
|
|
|
|
))}
|
|
|
|
|
onChange={(event) => commitFieldValue(field.type === 'number' ? Number(event.target.value) : event.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</ConnectionTestInput>
|
|
|
|
|
) : (
|
|
|
|
|
@@ -2436,12 +2460,7 @@ function FieldGrid({
|
|
|
|
|
value={text(displayValue, '')}
|
|
|
|
|
placeholder={placeholder}
|
|
|
|
|
disabled={field.disabled}
|
|
|
|
|
onChange={(event) => onDraftChange(setNestedDraftField(
|
|
|
|
|
draft,
|
|
|
|
|
record,
|
|
|
|
|
field.key,
|
|
|
|
|
field.type === 'number' ? Number(event.target.value) : event.target.value,
|
|
|
|
|
))}
|
|
|
|
|
onChange={(event) => commitFieldValue(field.type === 'number' ? Number(event.target.value) : event.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
{help ? <small>{help}</small> : null}
|
|
|
|
|
@@ -2545,6 +2564,25 @@ function GroupList({
|
|
|
|
|
const PLAYGROUND_SESSION_KEY = 'default'
|
|
|
|
|
const BRAND_ASSET_ACCEPT = '.png,.jpg,.jpeg,.webp,.svg'
|
|
|
|
|
const BRAND_ASSET_SUFFIXES = ['png', 'jpg', 'jpeg', 'webp', 'svg']
|
|
|
|
|
const BRAND_ASSET_TARGET_META: Record<BrandAssetTargetKey, {
|
|
|
|
|
copyLabel: string
|
|
|
|
|
dropLabel: string
|
|
|
|
|
uploadLabel: string
|
|
|
|
|
uploadTitle: string
|
|
|
|
|
}> = {
|
|
|
|
|
logo_src: {
|
|
|
|
|
copyLabel: '复制为 Logo',
|
|
|
|
|
dropLabel: '将图片拖到这里',
|
|
|
|
|
uploadLabel: '上传',
|
|
|
|
|
uploadTitle: '上传 Logo',
|
|
|
|
|
},
|
|
|
|
|
title_src: {
|
|
|
|
|
copyLabel: '复制为标题图',
|
|
|
|
|
dropLabel: '将图片拖到这里',
|
|
|
|
|
uploadLabel: '上传',
|
|
|
|
|
uploadTitle: '上传标题图片',
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
const PLAYGROUND_PRESETS = [
|
|
|
|
|
{
|
|
|
|
|
key: 'bgp-brief',
|
|
|
|
|
@@ -2572,6 +2610,121 @@ const PLAYGROUND_PRESETS = [
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
function isBrandAssetTargetKey(value: string): value is BrandAssetTargetKey {
|
|
|
|
|
return value === 'logo_src' || value === 'title_src'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isFileDrag(event: ReactDragEvent<HTMLElement>) {
|
|
|
|
|
return Array.from(event.dataTransfer.types).includes('Files')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function BrandAssetInput({
|
|
|
|
|
disabled,
|
|
|
|
|
onChange,
|
|
|
|
|
onFile,
|
|
|
|
|
placeholder,
|
|
|
|
|
target,
|
|
|
|
|
uploading,
|
|
|
|
|
value,
|
|
|
|
|
}: {
|
|
|
|
|
disabled?: boolean
|
|
|
|
|
onChange: (value: string) => void
|
|
|
|
|
onFile: (file: File) => void
|
|
|
|
|
placeholder?: string
|
|
|
|
|
target: BrandAssetTargetKey
|
|
|
|
|
uploading: boolean
|
|
|
|
|
value: string
|
|
|
|
|
}) {
|
|
|
|
|
const { locale } = useLocale()
|
|
|
|
|
const [dragging, setDragging] = useState(false)
|
|
|
|
|
const dragCounter = useRef(0)
|
|
|
|
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
|
|
|
const meta = BRAND_ASSET_TARGET_META[target]
|
|
|
|
|
const suffixHelp = BRAND_ASSET_SUFFIXES.join(' / ')
|
|
|
|
|
|
|
|
|
|
const resetDrag = () => {
|
|
|
|
|
dragCounter.current = 0
|
|
|
|
|
setDragging(false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDragEnter = (event: ReactDragEvent<HTMLDivElement>) => {
|
|
|
|
|
if (!isFileDrag(event)) return
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
dragCounter.current += 1
|
|
|
|
|
setDragging(true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDragOver = (event: ReactDragEvent<HTMLDivElement>) => {
|
|
|
|
|
if (!isFileDrag(event)) return
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDragLeave = (event: ReactDragEvent<HTMLDivElement>) => {
|
|
|
|
|
if (!isFileDrag(event)) return
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
dragCounter.current = Math.max(0, dragCounter.current - 1)
|
|
|
|
|
if (dragCounter.current === 0) setDragging(false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDrop = (event: ReactDragEvent<HTMLDivElement>) => {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
resetDrag()
|
|
|
|
|
const file = event.dataTransfer.files?.[0]
|
|
|
|
|
if (file) onFile(file)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
const file = event.target.files?.[0]
|
|
|
|
|
event.target.value = ''
|
|
|
|
|
if (file) onFile(file)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={dragging ? 'an-brand-asset-input is-dragging' : 'an-brand-asset-input'}
|
|
|
|
|
onDragEnter={handleDragEnter}
|
|
|
|
|
onDragOver={handleDragOver}
|
|
|
|
|
onDragLeave={handleDragLeave}
|
|
|
|
|
onDrop={handleDrop}
|
|
|
|
|
>
|
|
|
|
|
<input
|
|
|
|
|
className="an-brand-asset-input__control"
|
|
|
|
|
disabled={disabled || uploading}
|
|
|
|
|
onChange={(event) => onChange(event.target.value)}
|
|
|
|
|
placeholder={placeholder}
|
|
|
|
|
type="text"
|
|
|
|
|
value={value}
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
ref={fileInputRef}
|
|
|
|
|
accept={BRAND_ASSET_ACCEPT}
|
|
|
|
|
className="an-brand-asset-input__file"
|
|
|
|
|
disabled={disabled || uploading}
|
|
|
|
|
onChange={handleFileChange}
|
|
|
|
|
type="file"
|
|
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
className="an-brand-asset-input__upload"
|
|
|
|
|
disabled={disabled || uploading}
|
|
|
|
|
onClick={() => fileInputRef.current?.click()}
|
|
|
|
|
size="sm"
|
|
|
|
|
tactile={{ height: 28, radius: 5, shadowSize: 0.55 }}
|
|
|
|
|
title={`${localizeAdminText(meta.uploadTitle, locale)} · ${suffixHelp}`}
|
|
|
|
|
type="button"
|
|
|
|
|
variant="primary"
|
|
|
|
|
>
|
|
|
|
|
{uploading ? localizeAdminText('上传中', locale) : localizeAdminText(meta.uploadLabel, locale)}
|
|
|
|
|
</Button>
|
|
|
|
|
{dragging ? (
|
|
|
|
|
<div className="an-brand-asset-input__drop-overlay" aria-hidden="true">
|
|
|
|
|
<span>{localizeAdminText(meta.dropLabel, locale)}</span>
|
|
|
|
|
<strong>{localizeAdminText(meta.copyLabel, locale)}</strong>
|
|
|
|
|
</div>
|
|
|
|
|
) : null}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PlaygroundLite() {
|
|
|
|
|
const { toast } = useToast()
|
|
|
|
|
const { locale } = useLocale()
|
|
|
|
|
@@ -2848,7 +3001,7 @@ function ModuleConsole({ config }: { config: ModuleConfig }) {
|
|
|
|
|
const [revealedSecrets, setRevealedSecrets] = useState<Record<string, AnyRecord>>({})
|
|
|
|
|
const [visibleSecretFields, setVisibleSecretFields] = useState<Record<string, boolean>>({})
|
|
|
|
|
const [smtpTestEmail, setSmtpTestEmail] = useState('')
|
|
|
|
|
const [brandUploadFile, setBrandUploadFile] = useState<File | null>(null)
|
|
|
|
|
const [brandAssetUploadingTarget, setBrandAssetUploadingTarget] = useState<BrandAssetTargetKey | null>(null)
|
|
|
|
|
const [newsImportFile, setNewsImportFile] = useState<File | null>(null)
|
|
|
|
|
const newsImportInputRef = useRef<HTMLInputElement>(null)
|
|
|
|
|
const [resolveTarget, setResolveTarget] = useState<TableRecord | null>(null)
|
|
|
|
|
@@ -4002,51 +4155,46 @@ function ModuleConsole({ config }: { config: ModuleConfig }) {
|
|
|
|
|
setResolveTarget(null)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uploadBrandAsset = async () => {
|
|
|
|
|
if (!brandUploadFile) {
|
|
|
|
|
toast({ title: '请选择上传文件', tone: 'error' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const suffix = brandUploadFile.name.split('.').pop()?.toLowerCase() || ''
|
|
|
|
|
if (!BRAND_ASSET_SUFFIXES.includes(suffix)) {
|
|
|
|
|
toast({ title: '文件类型不支持', description: `仅支持 ${BRAND_ASSET_SUFFIXES.join(', ')}。`, tone: 'error' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const formData = new FormData()
|
|
|
|
|
formData.append('file', brandUploadFile)
|
|
|
|
|
setActionLoading(true)
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.post(apiPath('/earth/brand/assets'), formData, {
|
|
|
|
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
|
|
|
})
|
|
|
|
|
replaceSelectedWithPayload('brand-upload', '品牌资产上传', [{
|
|
|
|
|
...response.data,
|
|
|
|
|
__title: '品牌资产上传结果',
|
|
|
|
|
__module: '品牌资产',
|
|
|
|
|
__status: '已上传',
|
|
|
|
|
__metric: brandUploadFile.name,
|
|
|
|
|
}])
|
|
|
|
|
setBrandUploadFile(null)
|
|
|
|
|
toast({ title: '品牌资产已上传', tone: 'success' })
|
|
|
|
|
await load()
|
|
|
|
|
} catch (error) {
|
|
|
|
|
toast({ title: '品牌资产上传失败', description: error instanceof Error ? error.message : '接口请求失败', tone: 'error' })
|
|
|
|
|
} finally {
|
|
|
|
|
setActionLoading(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const selectBrandUploadFile = (file: File | null | undefined) => {
|
|
|
|
|
const uploadBrandAssetFile = async (file: File | null | undefined, target: BrandAssetTargetKey) => {
|
|
|
|
|
if (!file) {
|
|
|
|
|
setBrandUploadFile(null)
|
|
|
|
|
return
|
|
|
|
|
toast({ title: '请拖入图片文件', tone: 'error' })
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
const suffix = file.name.split('.').pop()?.toLowerCase() || ''
|
|
|
|
|
if (!BRAND_ASSET_SUFFIXES.includes(suffix)) {
|
|
|
|
|
toast({ title: '文件类型不支持', description: `仅支持 ${BRAND_ASSET_SUFFIXES.join(', ')}。`, tone: 'error' })
|
|
|
|
|
return
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
const formData = new FormData()
|
|
|
|
|
formData.append('file', file)
|
|
|
|
|
setActionLoading(true)
|
|
|
|
|
setBrandAssetUploadingTarget(target)
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.post(apiPath('/earth/brand/assets'), formData, {
|
|
|
|
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
|
|
|
})
|
|
|
|
|
const assetUrl = text(response.data?.url, '')
|
|
|
|
|
if (!assetUrl) throw new Error('上传接口未返回资产 URL')
|
|
|
|
|
replaceSelectedWithPayload('brand-upload', '品牌资产上传', [{
|
|
|
|
|
...response.data,
|
|
|
|
|
__title: '品牌资产上传结果',
|
|
|
|
|
__module: target === 'logo_src' ? 'Logo' : '标题图片',
|
|
|
|
|
__status: '已上传',
|
|
|
|
|
__metric: file.name,
|
|
|
|
|
}])
|
|
|
|
|
toast({
|
|
|
|
|
title: target === 'logo_src' ? 'Logo 图片已上传' : '标题图片已上传',
|
|
|
|
|
description: assetUrl,
|
|
|
|
|
tone: 'success',
|
|
|
|
|
})
|
|
|
|
|
return assetUrl
|
|
|
|
|
} catch (error) {
|
|
|
|
|
toast({ title: '品牌资产上传失败', description: actionErrorMessage(error), tone: 'error' })
|
|
|
|
|
return null
|
|
|
|
|
} finally {
|
|
|
|
|
setActionLoading(false)
|
|
|
|
|
setBrandAssetUploadingTarget(null)
|
|
|
|
|
}
|
|
|
|
|
setBrandUploadFile(file)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const createManualNewsGroup = async () => {
|
|
|
|
|
@@ -5413,6 +5561,32 @@ function ModuleConsole({ config }: { config: ModuleConfig }) {
|
|
|
|
|
{ key: 'location_label', label: '位置标签' },
|
|
|
|
|
]
|
|
|
|
|
const fields = newsSourceFields.length ? newsSourceFields : newsItemFields.length ? newsItemFields : [...scalarFields, ...objectFields]
|
|
|
|
|
const uploadBrandAssetToDraft = async (file: File, target: BrandAssetTargetKey) => {
|
|
|
|
|
if (!activeGroup) return
|
|
|
|
|
const assetUrl = await uploadBrandAssetFile(file, target)
|
|
|
|
|
if (!assetUrl) return
|
|
|
|
|
setHierarchyDraft((current) => setNestedDraftField(current, activeGroup.record, target, assetUrl))
|
|
|
|
|
}
|
|
|
|
|
const editableFields = config === configs.earthContent && activeSection.key === 'brand'
|
|
|
|
|
? fields.map((field): FieldConfig => {
|
|
|
|
|
if (!isBrandAssetTargetKey(field.key)) return field
|
|
|
|
|
const target = field.key
|
|
|
|
|
return {
|
|
|
|
|
...field,
|
|
|
|
|
renderInput: ({ disabled, displayValue, onChange, placeholder }) => (
|
|
|
|
|
<BrandAssetInput
|
|
|
|
|
disabled={disabled}
|
|
|
|
|
onChange={(nextValue) => onChange(nextValue)}
|
|
|
|
|
onFile={(file) => void uploadBrandAssetToDraft(file, target)}
|
|
|
|
|
placeholder={placeholder}
|
|
|
|
|
target={target}
|
|
|
|
|
uploading={brandAssetUploadingTarget === target}
|
|
|
|
|
value={text(displayValue, '')}
|
|
|
|
|
/>
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
: fields
|
|
|
|
|
const renderNewsFeedEditor = () => {
|
|
|
|
|
if (!activeGroup) return null
|
|
|
|
|
const currentSource = draftRecord(hierarchyDraft, activeGroup.record)
|
|
|
|
|
@@ -5951,20 +6125,6 @@ function ModuleConsole({ config }: { config: ModuleConfig }) {
|
|
|
|
|
<div className="an-toolbar">
|
|
|
|
|
{config === configs.earthContent && activeSection.key === 'brand' ? (
|
|
|
|
|
<>
|
|
|
|
|
<label
|
|
|
|
|
className="an-file-action an-file-action--drop"
|
|
|
|
|
onDragOver={(event) => event.preventDefault()}
|
|
|
|
|
onDrop={(event) => {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
selectBrandUploadFile(event.dataTransfer.files?.[0])
|
|
|
|
|
}}
|
|
|
|
|
title={`支持 ${BRAND_ASSET_SUFFIXES.join(', ')}`}
|
|
|
|
|
>
|
|
|
|
|
<input type="file" accept={BRAND_ASSET_ACCEPT} onChange={(event) => selectBrandUploadFile(event.target.files?.[0])} />
|
|
|
|
|
<span><ImageUp size={15} />{brandUploadFile ? brandUploadFile.name : '选择/拖入资产'}</span>
|
|
|
|
|
<small>{BRAND_ASSET_SUFFIXES.join(' / ')}</small>
|
|
|
|
|
</label>
|
|
|
|
|
<Button variant="primary" onClick={() => void uploadBrandAsset()} loading={actionLoading} disabled={!brandUploadFile}><ImageUp size={15} />上传</Button>
|
|
|
|
|
<Button size="icon" variant="subtle" title="重置品牌配置" aria-label="重置品牌配置" onClick={() => setConfirmAction({
|
|
|
|
|
title: '重置品牌配置',
|
|
|
|
|
description: '确认恢复默认智能星球品牌配置?当前自定义配置会被清空。',
|
|
|
|
|
@@ -6309,7 +6469,7 @@ function ModuleConsole({ config }: { config: ModuleConfig }) {
|
|
|
|
|
</section>
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<FieldGrid record={activeGroup.record} draft={hierarchyDraft} onDraftChange={setHierarchyDraft} fields={fields} searchGroupKey={activeGroup.key} />
|
|
|
|
|
<FieldGrid record={activeGroup.record} draft={hierarchyDraft} onDraftChange={setHierarchyDraft} fields={editableFields} searchGroupKey={activeGroup.key} />
|
|
|
|
|
)}
|
|
|
|
|
{config === configs.collection && activeSection.key === 'collection_history' ? null : (
|
|
|
|
|
<details className="an-advanced-editor">
|
|
|
|
|
@@ -6463,11 +6623,6 @@ function ModuleConsole({ config }: { config: ModuleConfig }) {
|
|
|
|
|
|
|
|
|
|
if (config === configs.earthContent && endpointKey === 'brand') {
|
|
|
|
|
actions.push(
|
|
|
|
|
<label key="brand-upload" className="an-file-action">
|
|
|
|
|
<input type="file" onChange={(event) => setBrandUploadFile(event.target.files?.[0] ?? null)} />
|
|
|
|
|
<span><ImageUp size={15} />{brandUploadFile ? brandUploadFile.name : '选择资产'}</span>
|
|
|
|
|
</label>,
|
|
|
|
|
<Button key="brand-upload-run" variant="primary" onClick={() => void uploadBrandAsset()} loading={actionLoading} disabled={!brandUploadFile}><ImageUp size={15} />上传</Button>,
|
|
|
|
|
<Button key="delete-brand" variant="danger" onClick={() => setConfirmAction({
|
|
|
|
|
title: '删除品牌配置',
|
|
|
|
|
description: '确认删除智能星球品牌配置?',
|
|
|
|
|
|