@@ -8,14 +8,14 @@
*/
import { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
import {
Alert , App , AutoComplete , Button , Card , Checkbox , Col , Descriptions , Divider , Drawer , Dropdown ,
Alert , App , Button , Card , Checkbox , Col , Descriptions , Divider , Drawer , Dropdown ,
Empty , Form , Image , Input , Modal , Popconfirm , Popover , Progress , Radio , Row , Select , Space , Statistic ,
Switch , Table , Tag , Timeline , Tooltip , Typography , Upload ,
} from 'antd' ;
import type { TableProps } from 'antd' ;
import {
CheckCircleOutlined , CloudUploadOutlined , CopyOutlined , DeleteOutlined , EditOutlined , FileExcelOutlined ,
LinkOutlined , PlusOutlined , ReloadOutlined , SafetyCertificateOutlined , ShopOutlined ,
CheckCircleOutlined , CloudUploadOutlined , CopyOutlined , DeleteOutlined , EditOutlined , FileAddOutlined ,
FileExcelOutlined , LinkOutlined , PlusOutlined , ReloadOutlined , SafetyCertificateOutlined , ShopOutlined ,
StopOutlined , SwapOutlined , SyncOutlined , UndoOutlined ,
} from '@ant-design/icons' ;
import {
@@ -267,6 +267,68 @@ function hasDraftContent(draft: Omit<CreateDraft, 'saved_at'>): boolean {
) ;
}
// 「补资料」草稿:按登录用户 + 订单 id 分开存。填一半关掉 / 刷新后,
// 再点这一行的「补资料」会原样恢复(含刚贴的 CK 和刚上传的截图 key)。
const SUPPLEMENT_DRAFT_KEY_PREFIX = 'huya.collab.supplementDraft' ;
interface SupplementDraft {
shop : string ;
ck_text : string ;
gift_name : string ;
remark : string ;
home_images : string [ ] ;
saved_at : number ;
}
interface SupplementFormValues {
shop : string ;
ck_text? : string ;
gift_name? : string ;
remark? : string ;
home_images? : string [ ] ;
}
function supplementDraftKey ( userId? : number | null , orderId? : number ) : string {
return userId && orderId ? ` ${ SUPPLEMENT_DRAFT_KEY_PREFIX } . ${ userId } . ${ orderId } ` : '' ;
}
function readSupplementDraft ( userId : number | null | undefined , orderId : number ) : SupplementDraft | null {
const key = supplementDraftKey ( userId , orderId ) ;
if ( ! key ) return null ;
try {
const raw = window . localStorage . getItem ( key ) ;
if ( ! raw ) return null ;
const parsed = JSON . parse ( raw ) as Partial < SupplementDraft > ;
const savedAt = Number ( parsed . saved_at ) || 0 ;
if ( ! savedAt || Date . now ( ) - savedAt > 7 * 24 * 60 * 60 * 1000 ) {
window . localStorage . removeItem ( key ) ;
return null ;
}
return {
shop : typeof parsed . shop === 'string' ? parsed . shop : '' ,
ck_text : typeof parsed . ck_text === 'string' ? parsed . ck_text : '' ,
gift_name : typeof parsed . gift_name === 'string' ? parsed . gift_name : '' ,
remark : typeof parsed . remark === 'string' ? parsed . remark : '' ,
home_images : Array.isArray ( parsed . home_images )
? parsed . home_images . filter ( ( item ) : item is string = > typeof item === 'string' )
: [ ] ,
saved_at : savedAt ,
} ;
} catch {
return null ;
}
}
function clearSupplementDraft ( userId : number | null | undefined , orderId : number ) : void {
const key = supplementDraftKey ( userId , orderId ) ;
if ( ! key ) return ;
try {
window . localStorage . removeItem ( key ) ;
} catch {
// 忽略:清不掉也不影响当前表单
}
}
function formatTime ( value : string | null | undefined ) : string {
if ( ! value ) return '-' ;
const time = new Date ( value ) ;
@@ -410,6 +472,8 @@ export default function HuyaCollabPage() {
const canManage = can ( 'huya:collab:manage' ) ;
// 导出 CK 是把凭据带出系统,单独一个权限点(默认只给运营,见 permissions.py)
const canExportCk = can ( 'huya:collab:ck_export' ) ;
// 「复制 CK」是单行一键带走凭据,按用户要求只给超级管理员(后端也会再校验一次)
const isAdmin = user ? . role === 'super_admin' ;
const [ rows , setRows ] = useState < HuyaCollabOrder [ ] > ( [ ] ) ;
const [ total , setTotal ] = useState ( 0 ) ;
@@ -438,6 +502,16 @@ export default function HuyaCollabPage() {
const [ createForm ] = Form . useForm < Record < string , unknown > > ( ) ;
const [ draftRestoredAt , setDraftRestoredAt ] = useState < number | null > ( null ) ;
const draftSaveTimer = useRef < number | null > ( null ) ;
// 「补资料」:对待补资料的单补 CK / 礼包 / 主页截图 / 备注,草稿按订单存在本机
const [ supplementTarget , setSupplementTarget ] = useState < HuyaCollabOrder | null > ( null ) ;
const [ supplementForm ] = Form . useForm < SupplementFormValues > ( ) ;
const [ supplementDraftAt , setSupplementDraftAt ] = useState < number | null > ( null ) ;
const supplementDraftTimer = useRef < number | null > ( null ) ;
const createGiftValue = Form . useWatch ( 'gift_name' , createForm ) as string | undefined ;
const supplementGiftValue = Form . useWatch ( 'gift_name' , supplementForm ) as string | undefined ;
const supplementCkText = Form . useWatch ( 'ck_text' , supplementForm ) as string | undefined ;
const supplementHomeImages = Form . useWatch ( 'home_images' , supplementForm ) as string [ ] | undefined ;
const [ ckPreview , setCkPreview ] = useState < HuyaCollabCkPreview | null > ( null ) ;
const [ previewing , setPreviewing ] = useState ( false ) ;
// 这次预览有没有带「拉礼包」(没有就是纯探针 + 用本地礼包缓存),只影响提示文案
@@ -521,7 +595,7 @@ export default function HuyaCollabPage() {
const realtimeEnabled = can ( 'huya:collab:view' ) ;
const collabModalOpen = Boolean (
createOpen || detail || statusAction || batchStatusOpen || ckTarget || ckExportOpen || shopModalOpen ,
createOpen || supplementTarget || detail || statusAction || batchStatusOpen || ckTarget || ckExportOpen || shopModalOpen ,
) ;
const handleRealtimeInvalidate = useCallback ( ( event : CollabRealtimeEvent ) = > {
@@ -1019,6 +1093,203 @@ export default function HuyaCollabPage() {
}
} ;
/** 单行「复制 CK」:只有超级管理员能看到按钮,后端还会再校验一次角色。 */
const copyOneCk = async ( row : HuyaCollabOrder ) = > {
setBusy ( true ) ;
try {
const result = await huyaApi . copyCollabOrderCk ( row . id ) ;
const item = result . items [ 0 ] ;
if ( ! item ) {
message . warning ( result . skipped [ 0 ] ? . reason || '这行还没有可复制的 CK' ) ;
return ;
}
await copyTextToClipboard ( item . ck_text ) ;
message . success ( ` 已复制 ${ row . order_no } 的 CK ` ) ;
} catch ( e : unknown ) {
message . error ( ` 复制失败: ${ getErrorMessage ( e ) } ` ) ;
} finally {
setBusy ( false ) ;
}
} ;
/** 把「补资料」表单写进本机草稿:仍按用户 + 订单分键,最多留 7 天。 */
const writeSupplementDraft = useCallback ( ( values : SupplementFormValues , target : HuyaCollabOrder ) = > {
const userId = user ? . id ;
const key = supplementDraftKey ( userId , target . id ) ;
if ( ! key ) return ;
const images = Array . isArray ( values . home_images )
? values . home_images . filter ( ( item ) : item is string = > typeof item === 'string' )
: target . home_images ;
const payload : Omit < SupplementDraft , 'saved_at' > = {
shop : String ( values . shop ? ? '' ) ,
ck_text : String ( values . ck_text ? ? '' ) ,
gift_name : String ( values . gift_name ? ? '' ) ,
remark : String ( values . remark ? ? '' ) ,
home_images : images ,
} ;
const changed = Boolean (
payload . ck_text . trim ( )
|| payload . shop !== target . shop
|| payload . gift_name !== target . gift_name
|| payload . remark !== target . remark
|| payload . home_images . join ( '\n' ) !== target . home_images . join ( '\n' ) ,
) ;
try {
if ( ! changed ) {
window . localStorage . removeItem ( key ) ;
return ;
}
window . localStorage . setItem ( key , JSON . stringify ( { . . . payload , saved_at : Date.now ( ) } ) ) ;
} catch {
// 隐私模式 / 配额满:暂存失败不影响提交
}
} , [ user ? . id ] ) ;
const persistSupplementDraft = useCallback ( ( values : SupplementFormValues , target : HuyaCollabOrder ) = > {
if ( supplementDraftTimer . current ) window . clearTimeout ( supplementDraftTimer . current ) ;
supplementDraftTimer . current = window . setTimeout ( ( ) = > writeSupplementDraft ( values , target ) , 400 ) ;
} , [ writeSupplementDraft ] ) ;
const openSupplement = ( row : HuyaCollabOrder ) = > {
if ( supplementDraftTimer . current ) {
window . clearTimeout ( supplementDraftTimer . current ) ;
supplementDraftTimer . current = null ;
}
const draft = readSupplementDraft ( user ? . id , row . id ) ;
const base : SupplementFormValues = {
shop : row.shop ,
ck_text : '' ,
gift_name : row.gift_name ,
remark : row.remark ,
home_images : row.home_images ,
} ;
supplementForm . setFieldsValue ( draft ? { . . . base , . . . draft } : base ) ;
setSupplementDraftAt ( draft ? . saved_at ? ? null ) ;
setSupplementTarget ( row ) ;
} ;
const closeSupplement = useCallback ( ( ) = > {
if ( supplementDraftTimer . current ) {
window . clearTimeout ( supplementDraftTimer . current ) ;
supplementDraftTimer . current = null ;
}
if ( supplementTarget ) {
writeSupplementDraft (
supplementForm . getFieldsValue ( true ) as SupplementFormValues ,
supplementTarget ,
) ;
}
setSupplementTarget ( null ) ;
setSupplementDraftAt ( null ) ;
supplementForm . resetFields ( ) ;
} , [ supplementForm , supplementTarget , writeSupplementDraft ] ) ;
const clearSupplement = ( ) = > {
if ( ! supplementTarget ) return ;
if ( supplementDraftTimer . current ) {
window . clearTimeout ( supplementDraftTimer . current ) ;
supplementDraftTimer . current = null ;
}
clearSupplementDraft ( user ? . id , supplementTarget . id ) ;
setSupplementDraftAt ( null ) ;
supplementForm . setFieldsValue ( {
shop : supplementTarget.shop ,
ck_text : '' ,
gift_name : supplementTarget.gift_name ,
remark : supplementTarget.remark ,
home_images : supplementTarget.home_images ,
} ) ;
} ;
/**
* 保存「补资料」:字段 / 新 CK / 截图分别走各自接口(每个都有乐观锁),
* 新 CK 保存后立即跑一次只读探针;用户点「保存并进入待处理」时再校验资料完整性。
*/
const submitSupplement = async ( markPending = false ) = > {
if ( ! supplementTarget ) return ;
let values : SupplementFormValues ;
try {
values = await supplementForm . validateFields ( ) ;
} catch {
return ;
}
setBusy ( true ) ;
try {
let current = supplementTarget ;
const payload : HuyaCollabOrderUpdate = { } ;
const shop = String ( values . shop ? ? '' ) . trim ( ) ;
const giftName = String ( values . gift_name ? ? '' ) . trim ( ) ;
const remark = String ( values . remark ? ? '' ) ;
if ( shop !== current . shop ) payload . shop = shop ;
if ( giftName !== current . gift_name ) payload . gift_name = giftName ;
if ( remark !== current . remark ) payload . remark = remark ;
if ( Object . keys ( payload ) . length > 0 ) {
current = await huyaApi . updateCollabOrder ( current . id , {
. . . payload ,
expected_version : current.version ,
} ) ;
}
const ckText = String ( values . ck_text ? ? '' ) . trim ( ) ;
if ( ckText ) {
current = await huyaApi . updateCollabOrderCk ( current . id , {
ck_text : ckText ,
action : 'paste' ,
expected_version : current.version ,
} ) ;
current = await huyaApi . verifyCollabOrder ( current . id ) ;
}
const images = values . home_images ? ? current . home_images ;
if ( images . join ( '\n' ) !== current . home_images . join ( '\n' ) ) {
current = await huyaApi . updateCollabOrderImages ( current . id , {
slot : 'home_images' ,
images ,
expected_version : current.version ,
} ) ;
}
// 没有换 CK、但准备进「待处理」时,先跑一次只读探针;避免拿一份没验证的旧 CK 往前推
if ( markPending && ! ckText && current . has_cookie && current . ck_status !== 'valid' ) {
current = await huyaApi . verifyCollabOrder ( current . id ) ;
}
if ( markPending && current . ck_status !== 'valid' ) {
applyUpdated ( current ) ;
setSupplementTarget ( current ) ;
supplementForm . setFieldValue ( 'ck_text' , '' ) ;
void load ( ) ;
message . warning (
'资料已保存,但这份 CK 还没验证通过,暂时不能进入「待处理」;请先补 / 换一份有效 CK' ,
) ;
return ;
}
if ( markPending ) {
current = await huyaApi . updateCollabOrderStatus ( current . id , {
status : 'pending' ,
expected_version : current.version ,
} ) ;
}
applyUpdated ( current ) ;
clearSupplementDraft ( user ? . id , supplementTarget . id ) ;
setSupplementDraftAt ( null ) ;
supplementForm . resetFields ( ) ;
setSupplementTarget ( null ) ;
message . success (
markPending
? ` 已补齐资料, ${ current . order_no } 进入「待处理」 `
: ` 已保存 ${ current . order_no } 的资料 ` ,
) ;
void load ( ) ;
} catch ( e : unknown ) {
message . error ( getErrorMessage ( e ) ) ;
void load ( ) ;
} finally {
setBusy ( false ) ;
}
} ;
const commitSlot = async ( row : HuyaCollabOrder , slot : HuyaCollabImageSlot , keys : string [ ] ) = > {
const updated = await huyaApi . updateCollabOrderImages ( row . id , {
slot , images : keys , expected_version : row.version ,
@@ -1111,6 +1382,7 @@ export default function HuyaCollabPage() {
useEffect ( ( ) = > ( ) = > {
if ( draftSaveTimer . current ) window . clearTimeout ( draftSaveTimer . current ) ;
if ( supplementDraftTimer . current ) window . clearTimeout ( supplementDraftTimer . current ) ;
} , [ ] ) ;
/** 清掉草稿并恢复空表单(「清空草稿」入口用) */
@@ -1229,6 +1501,27 @@ export default function HuyaCollabPage() {
? ` 礼包用本地缓存( ${ giftOptions . length } 个 ${ giftCacheLabel ? ` · ${ giftCacheLabel } ` : '' } ),要更新点「强制刷新礼包」 `
: ( ckPreview ? . gifts_message || '' ) ;
/**
* 礼包下拉选项:只允许点选缓存里的礼包,不接受手输。
* 当前值是历史/已下线礼包时补一个只读选项,保证界面能显示,但依然只能从下拉里选。
*/
const giftSelectOptions = ( current? : string | null ) = > {
const value = String ( current ? ? '' ) . trim ( ) ;
const options = giftOptions . map ( ( gift ) = > ( {
value : gift.name ,
label : [
gift . category_name ? ` [ ${ gift . category_name } ] ` : '' ,
gift . name ,
gift . price ? ` · ${ gift . price } 积分 ` : '' ,
gift . remain_text ? ` · 剩 ${ gift . remain_text } ` : '' ,
] . filter ( Boolean ) . join ( ' ' ) ,
} ) ) ;
if ( value && ! giftOptions . some ( ( gift ) = > gift . name === value ) ) {
options . unshift ( { value , label : ` ${ value } (历史值) ` } ) ;
}
return options ;
} ;
// 表头排序:一次正序、再点反序(Table 上限制 sortDirections,不会出现「第三次取消」)。
const sortProps = ( key : CollabSortKey ) = > ( {
key ,
@@ -1392,8 +1685,21 @@ export default function HuyaCollabPage() {
. . . sortProps ( 'gift_name' ) ,
render : ( _ , row ) = > (
< Space direction = "vertical" size = { 0 } style = { { width : '100%' } } >
< EditableText value = { row . gift_name } disabled = { ! canEdit } placeholder = "+ 礼包名"
onCommit = { ( next ) = > void patchOrder ( row , { gift_name : next } ) } / >
{ /* 礼包名只能从缓存列表点选:手输会出现“看起来一样、接口拉不到”的脏值 */ }
< Select
size = "small"
variant = "borderless"
style = { { width : '100%' } }
showSearch
optionFilterProp = "label"
options = { giftSelectOptions ( row . gift_name ) }
value = { row . gift_name || undefined }
disabled = { ! canEdit }
placeholder = "+ 点选礼包"
popupMatchSelectWidth = { false }
notFoundContent = { giftOptions . length === 0 ? '礼包缓存为空:先在「新建」里贴 CK 拉一次' : '没有匹配的礼包' }
onChange = { ( next : string ) = > void patchOrder ( row , { gift_name : next } ) }
/ >
< EditableText value = { row . remark } disabled = { ! canEdit } placeholder = "+ 备注"
onCommit = { ( next ) = > void patchOrder ( row , { remark : next } ) } / >
< / Space >
@@ -1483,9 +1789,24 @@ export default function HuyaCollabPage() {
) ,
} ,
{
title : '操作' , width : 150 , fixed : 'right' , align : 'center' ,
title : '操作' , width : 300 , fixed : 'right' , align : 'center' ,
render : ( _ , row ) = > (
< Space size = { 4 } >
< Space size = { 4 } wrap >
{ canEdit && row . status === 'incomplete' && (
< Button size = "small" type = "primary" ghost icon = { < FileAddOutlined / > }
onClick = { ( ) = > openSupplement ( row ) } >
补 资 料
< / Button >
) }
{ isAdmin && (
< Tooltip title = { row . has_cookie ? '复制这一行的 CK 明文(仅管理员)' : '这行还没贴 CK' } >
< Button size = "small" icon = { < CopyOutlined / > }
disabled = { busy || ! row . has_cookie }
onClick = { ( ) = > void copyOneCk ( row ) } >
复 制 CK
< / Button >
< / Tooltip >
) }
< Button size = "small" icon = { < EditOutlined / > } onClick = { ( ) = > void openDetail ( row ) } > 详 情 < / Button >
{ canEdit && (
< Dropdown
@@ -1710,16 +2031,31 @@ export default function HuyaCollabPage() {
const draft = readCreateDraft ( user ? . id ) ;
if ( draft && hasDraftContent ( draft ) ) {
// 上次填了一半:原样恢复(CK / 礼包 / 截图 / 备注都在),并重新验证暂存的 CK
createForm . setFieldsValue ( { . . . draft } ) ;
const restored = { . . . draft } ;
const restoredGift = String ( restored . gift_name ? ? '' ) . trim ( ) ;
if (
restoredGift
&& giftOptions . length > 0
&& ! giftOptions . some ( ( gift ) = > gift . name === restoredGift )
) {
restored . gift_name = '' ;
setGiftHint ( ` 草稿里的「 ${ restoredGift } 」不在当前礼包缓存里,已清空,请重新点选 ` ) ;
} else {
setGiftHint ( '' ) ;
}
createForm . setFieldsValue ( { . . . restored } ) ;
setDraftRestoredAt ( draft . saved_at || Date . now ( ) ) ;
setCreateOpen ( true ) ;
if ( draft . ck_text . trim ( ) ) scheduleCkPreview ( draft . ck_text , true ) ;
return ;
}
setDraftRestoredAt ( null ) ;
// 记住最近选的礼包:同一个礼包连开几单时不用反复选
// 记住最近选的礼包:只在当前缓存里还有它时才自动带上(礼包名不接受手输)
const lastGift = window . localStorage . getItem ( LAST_GIFT_KEY ) ;
if ( lastGift ) createForm . setFieldsValue ( { gift_name : lastGift } ) ;
if ( lastGift && giftOptions . some ( ( gift ) = > gift . name === lastGift ) ) {
createForm . setFieldsValue ( { gift_name : lastGift } ) ;
}
setGiftHint ( '' ) ;
setCreateOpen ( true ) ;
} }
>
@@ -1736,7 +2072,7 @@ export default function HuyaCollabPage() {
loading = { loading }
columns = { columns }
dataSource = { rows }
scroll = { { x : 1800 , y : 'calc(100vh - 460px)' } }
scroll = { { x : 2200 , y : 'calc(100vh - 460px)' } }
sortDirections = { [ 'ascend' , 'descend' ] }
onChange = { ( _pagination , _filters , sorter , extra ) = > {
if ( extra . action !== 'sort' ) return ;
@@ -1890,7 +2226,19 @@ export default function HuyaCollabPage() {
< / Form.Item >
< / Col >
< Col span = { 12 } >
< Form.Item name = "gift_name" label = "礼包名" > < Input maxLength = { 128 } / > < / Form.Item >
< Form.Item name = "gift_name" label = "礼包名"
tooltip = "只接受礼包缓存里的点选结果,可输入关键字搜索;不接受手工输入" >
< Select
allowClear
showSearch
optionFilterProp = "label"
placeholder = "输入关键字搜索,点选礼包"
options = { giftSelectOptions ( detail . gift_name ) }
notFoundContent = { giftOptions . length === 0
? '礼包缓存为空:先在「新建」里贴 CK 拉一次'
: '没有匹配的礼包' }
/ >
< / Form.Item >
< / Col >
< Col span = { 24 } >
< Form.Item name = "remark" label = "备注" > < Input.TextArea rows = { 2 } maxLength = { 512 } / > < / Form.Item >
@@ -2184,26 +2532,20 @@ export default function HuyaCollabPage() {
< Form.Item
name = "gift_name"
label = "礼包名(用户购买的物品,可稍后补)"
tooltip = "先贴有效 CK,再从礼包列表里点选(列表有本地缓存,不用每次重拉;拉不到时可以直接手输)。 不选也能先建单,之后在详情里补 。"
tooltip = "只接受礼包缓存里的点选结果,可输入关键字搜索。先贴有效 CK 拉一次礼包; 不选也能先建单,之后点「补资料」再选 。"
extra = { giftHint || ( giftsCachedAt && giftOptions . length > 0
? ` 礼包列表缓存于 ${ giftCacheLabel } ( ${ giftOptions . length } 个) `
: undefined ) }
validateStatus = { giftHint ? 'warning' : undefined }
>
< AutoComplete
< Select
allowClear
placeholder = { ckReady ? '从礼包里点选(可直接输)' : '可先空着,之后在详情里补' }
options = { giftOptions . map ( ( gift ) = > ( {
value : gift.name ,
label : [
gift . category_name ? ` [ ${ gift . category_name } ] ` : '' ,
gift . name ,
gift . price ? ` · ${ gift . price } 积分 ` : '' ,
gift . remain_text ? ` · 剩 ${ gift . remain_text } ` : '' ,
] . filter ( Boolean ) . join ( ' ' ) ,
} ) ) }
filterOption = { ( input , option ) = >
String ( option ? . value ? ? '' ) . toLowerCase ( ) . includes ( input . toLowerCase ( ) ) }
showSearch
optionFilterProp = "label"
placeholder = { giftOptions . length > 0 ? '输入关键字搜索,点选礼包' : '礼包缓存为空:先贴 CK 拉取' }
options = { giftSelectOptions ( createGiftValue ) }
disabled = { giftOptions . length === 0 && ! createGiftValue }
notFoundContent = { giftOptions . length === 0 ? '先在左侧贴一份有效 CK 拉取礼包列表' : '没有匹配的礼包' }
onChange = { ( value : string ) = > {
// 记住最近选的礼包:连开几单同一个礼包就不用反复点
setGiftHint ( '' ) ;
@@ -2246,6 +2588,118 @@ export default function HuyaCollabPage() {
< / Form >
< / Modal >
< Modal
title = { supplementTarget ? ` 补资料 · ${ supplementTarget . order_no } ` : '补资料' }
open = { ! ! supplementTarget }
width = { 760 }
onCancel = { closeSupplement }
destroyOnHidden
styles = { { body : { maxHeight : '74vh' , overflowY : 'auto' , paddingRight : 12 } } }
footer = { [
< Button key = "cancel" onClick = { closeSupplement } disabled = { busy } >
取 消
< / Button > ,
< Button key = "save" onClick = { ( ) = > void submitSupplement ( false ) } loading = { busy } >
保 存 资 料
< / Button > ,
< Button key = "pending" type = "primary" onClick = { ( ) = > void submitSupplement ( true ) } loading = { busy } >
保 存 并 进 入 待 处 理
< / Button > ,
] }
>
{ supplementTarget && (
< >
{ supplementDraftAt && (
< Alert
type = "info"
showIcon
style = { { marginBottom : 12 } }
message = { ` 已恢复上次没填完的补资料草稿( ${ formatTime ( new Date ( supplementDraftAt ) . toISOString ( ) ) } 暂存) ` }
action = { < Button size = "small" type = "link" onClick = { clearSupplement } > 清 空 草 稿 < / Button > }
/ >
) }
< Alert
type = "warning"
showIcon
style = { { marginBottom : 12 } }
message = "还缺什么就补什么;填到一半可以直接关掉,下次再点「补资料」会从这里继续"
description = { ( ( ) = > {
const hasCk = supplementTarget . has_cookie || Boolean ( supplementCkText ? . trim ( ) ) ;
const images = supplementHomeImages ? ? supplementTarget . home_images ;
const missing = [
! hasCk ? '外部 CK' : '' ,
! supplementGiftValue ? '礼包名' : '' ,
images . length === 0 ? '主页截图' : '' ,
] . filter ( Boolean ) ;
return missing . length > 0 ? ` 当前还缺: ${ missing . join ( '、' ) } ` : '资料已齐,可以直接「保存并进入待处理」。' ;
} ) ( ) }
/ >
< Form
form = { supplementForm }
layout = "vertical"
size = "middle"
onValuesChange = { ( _changed , allValues ) = > {
persistSupplementDraft ( allValues as SupplementFormValues , supplementTarget ) ;
} }
>
< Row gutter = { 16 } >
< Col span = { 12 } >
< Form.Item name = "shop" label = "店铺"
rules = { [ { required : true , message : '请点一下选店铺' } ] }
tooltip = "点一下选;历史订单的店铺即使已停用也会保留" >
< HuyaCollabShopPicker shops = { shops } allowClear = { false } size = "large" / >
< / Form.Item >
< / Col >
< Col span = { 12 } >
< Form.Item name = "gift_name" label = "礼包名"
tooltip = "只接受礼包缓存里的点选结果,可输入关键字搜索;不接受手工输入" >
< Select
allowClear
showSearch
optionFilterProp = "label"
placeholder = { giftOptions . length > 0 ? '输入关键字搜索,点选礼包' : '礼包缓存为空:先在「新建」里贴 CK 拉一次' }
options = { giftSelectOptions ( supplementGiftValue ? ? supplementTarget . gift_name ) }
disabled = { giftOptions . length === 0 && ! supplementGiftValue }
notFoundContent = { giftOptions . length === 0
? '礼包缓存为空:先在「新建」里贴 CK 拉一次'
: '没有匹配的礼包' }
/ >
< / Form.Item >
< / Col >
< / Row >
< Form.Item
name = "ck_text"
label = "外部 CK(不换就留空)"
tooltip = "支持「纯CK」或「账号----密码----CK」。贴新 CK 会覆盖并立即验证;这行已经存过 CK 时,不换就留空"
extra = { supplementTarget . has_cookie
? ` 这行已存 CK(当前: ${ supplementTarget . ck_status_label } ;最近验证 ${ formatTime ( supplementTarget . ck_checked_at ) } ) `
: '这行还没有 CK,可以现在补' }
>
< Input.TextArea
autoSize = { { minRows : 3 , maxRows : 7 } }
placeholder = { supplementTarget . has_cookie
? '已存 CK;要换就贴一份新的,不换留空'
: '粘贴外部 CK,保存后自动验证' }
/ >
< / Form.Item >
< Row gutter = { 16 } >
< Col span = { 14 } >
< Form.Item name = "home_images" label = "主页截图(第一张作封面)"
tooltip = "支持点击选择、拖拽或 Ctrl/Cmd+V 粘贴;补资料时也可以只补截图" >
< HuyaCollabHomeImageUploader max = { MAX_IMAGES_PER_SLOT } optional / >
< / Form.Item >
< / Col >
< Col span = { 10 } >
< Form.Item name = "remark" label = "备注(可选)" >
< Input.TextArea rows = { 3 } maxLength = { 512 } / >
< / Form.Item >
< / Col >
< / Row >
< / Form >
< / >
) }
< / Modal >
< Modal
title = "粘贴导入(Excel 复制出来的 TSV) "
open = { importOpen }