release: bump version to 0.74.1
This commit is contained in:
@@ -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: '确认删除智能星球品牌配置?',
|
||||
|
||||
@@ -1697,6 +1697,111 @@ body:has(.admin-theme-root[data-theme='dark']) .an-toast {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.an-brand-asset-input {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
height: 34px;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--an-border);
|
||||
border-radius: 6px;
|
||||
background: var(--an-surface);
|
||||
color: var(--an-text);
|
||||
transition: border-color 0.16s ease, box-shadow 0.16s ease, background-color 0.16s ease;
|
||||
}
|
||||
|
||||
.an-brand-asset-input:focus-within {
|
||||
border-color: color-mix(in srgb, var(--an-accent) 24%, var(--an-border-strong));
|
||||
}
|
||||
|
||||
.an-brand-asset-input.is-dragging {
|
||||
border-color: var(--an-border-strong);
|
||||
background: color-mix(in srgb, var(--an-soft) 46%, var(--an-surface));
|
||||
box-shadow: 0 2px 5px rgba(15, 23, 42, 0.1), 0 8px 18px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.an-brand-asset-input__control {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
padding: 0 10px;
|
||||
font: inherit;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.an-brand-asset-input__control:disabled {
|
||||
color: var(--an-muted);
|
||||
}
|
||||
|
||||
.an-brand-asset-input__file {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.an-brand-asset-input__upload {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
min-width: 72px;
|
||||
height: var(--tui-control-height, 28px);
|
||||
margin-right: 3px;
|
||||
padding-inline: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.an-brand-asset-input__drop-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 0 12px;
|
||||
border: 1px dashed var(--an-border-strong);
|
||||
border-radius: 5px;
|
||||
background: color-mix(in srgb, var(--an-soft) 82%, var(--an-surface));
|
||||
color: var(--an-text);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.an-brand-asset-input__drop-overlay > span {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.an-brand-asset-input__drop-overlay > strong {
|
||||
min-width: max-content;
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid var(--an-border);
|
||||
border-radius: 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: var(--an-surface);
|
||||
color: var(--an-text);
|
||||
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.16);
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.admin-theme-root[data-theme='dark'] .an-brand-asset-input__drop-overlay > strong {
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
|
||||
.an-mapping-form {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
|
||||
@@ -556,6 +556,15 @@ export const legacyUiTextEnUS: Record<string, string> = {
|
||||
'描述': 'Description',
|
||||
'每日摘要': 'Daily digest',
|
||||
'Logo 地址': 'Logo URL',
|
||||
'上传 Logo': 'Upload logo',
|
||||
'上传标题图片': 'Upload title image',
|
||||
'上传中': 'Uploading',
|
||||
'将图片拖到这里': 'Drop file here',
|
||||
'复制为 Logo': 'Copy as logo',
|
||||
'复制为标题图': 'Copy as title image',
|
||||
'Logo 图片已上传': 'Logo image uploaded',
|
||||
'标题图片已上传': 'Title image uploaded',
|
||||
'请拖入图片文件': 'Drop an image file',
|
||||
'API 基础地址': 'API base URL',
|
||||
'Firecrawl 抓取路径': 'Firecrawl scrape path',
|
||||
'Firecrawl 搜索路径': 'Firecrawl search path',
|
||||
|
||||
Reference in New Issue
Block a user