gongxiangp2/components/upload/LiveClipFields.tsx
WuYongJie d8d843e6ad feat: add live clip material type with date host and platform
Register 直播切片 as a video-only material type. Upload and edit flows
collect required title, live date (defaults to today), optional host with
user link, and live platform presets. Persist the fields on Material and
skip SPU formatting for free-text titles.
2026-09-22 18:59:05 +08:00

152 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 直播切片专用字段:直播日期 / 主播(可关联用户)/ 直播平台
*/
'use client'
import { useEffect, useState } from 'react'
import {
fieldControlClass,
fieldHintClass,
fieldLabelClass,
} from '@/components/upload/upload-field-styles'
import {
getDefaultLiveDate,
LIVE_CLIP_PLATFORMS,
} from '@/lib/live-clip'
export interface LiveClipFieldsProps {
liveDate: string
liveHost: string
liveHostUserId: string
livePlatform: string
onLiveDateChange: (value: string) => void
onLiveHostChange: (value: string) => void
onLiveHostUserIdChange: (value: string) => void
onLivePlatformChange: (value: string) => void
}
interface UserOption {
id: string
displayName: string
username: string
}
export default function LiveClipFields({
liveDate,
liveHost,
liveHostUserId,
livePlatform,
onLiveDateChange,
onLiveHostChange,
onLiveHostUserIdChange,
onLivePlatformChange,
}: LiveClipFieldsProps) {
const [users, setUsers] = useState<UserOption[]>([])
const [usersLoaded, setUsersLoaded] = useState(false)
useEffect(() => {
if (!liveDate) {
onLiveDateChange(getDefaultLiveDate())
}
// 仅在日期为空时补默认值,避免依赖回调导致循环
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(() => {
let cancelled = false
fetch('/api/users/list')
.then((res) => (res.ok ? res.json() : []))
.then((data: UserOption[]) => {
if (!cancelled && Array.isArray(data)) {
setUsers(data)
setUsersLoaded(true)
}
})
.catch(() => {
if (!cancelled) setUsersLoaded(true)
})
return () => {
cancelled = true
}
}, [])
return (
<>
<div>
<label htmlFor="liveDate" className={fieldLabelClass}>
直播日期
</label>
<input
id="liveDate"
type="date"
value={liveDate}
onChange={(e) => onLiveDateChange(e.target.value)}
className={fieldControlClass}
max={getDefaultLiveDate()}
/>
<p className={`mt-2 ${fieldHintClass}`}>默认上传当天,可按日历修改</p>
</div>
<div>
<label htmlFor="liveHost" className={fieldLabelClass}>
主播 <span className={`ml-1 font-normal ${fieldHintClass}`}>(可选)</span>
</label>
<input
id="liveHost"
type="text"
value={liveHost}
onChange={(e) => onLiveHostChange(e.target.value)}
className={fieldControlClass}
maxLength={50}
placeholder="填写主播昵称"
/>
<label htmlFor="liveHostUserId" className={`${fieldLabelClass} mt-4 block`}>
关联用户 <span className={`ml-1 font-normal ${fieldHintClass}`}>(可选,便于按人筛选)</span>
</label>
<select
id="liveHostUserId"
value={liveHostUserId}
onChange={(e) => {
const userId = e.target.value
onLiveHostUserIdChange(userId)
if (userId) {
const matched = users.find((u) => u.id === userId)
if (matched && !liveHost.trim()) {
onLiveHostChange(matched.displayName)
}
}
}}
className={fieldControlClass}
>
<option value="">{usersLoaded ? '不关联用户' : '加载用户…'}</option>
{users.map((user) => (
<option key={user.id} value={user.id}>
{user.displayName}
</option>
))}
</select>
</div>
<div>
<label htmlFor="livePlatform" className={fieldLabelClass}>
直播平台
</label>
<select
id="livePlatform"
value={livePlatform}
onChange={(e) => onLivePlatformChange(e.target.value)}
className={fieldControlClass}
>
<option value="">请选择平台</option>
{LIVE_CLIP_PLATFORMS.map((platform) => (
<option key={platform} value={platform}>
{platform}
</option>
))}
</select>
</div>
</>
)
}