wjx 4 mesi fa
parent
commit
0feb225204

+ 1 - 1
src/pages/launchSystemV3/adqv3/ad/updateAd3.tsx

@@ -49,7 +49,7 @@ const UpdateAd3: React.FC<Props> = ({ visible, type, onClose, onChange, updateDa
 
     const handleOk = (values: any) => {
         console.log(values)
-        let accountAdgroupMaps = [...new Set(updateData?.map(item => item.accountId + ',' + item.adgroupId))]
+        const accountAdgroupMaps = [...new Set(updateData?.map(item => item.accountId + ',' + item.adgroupId))]
         switch (type) {
             case '修改出价':
                 let paramsCj: any = {}

+ 30 - 10
src/pages/launchSystemV3/adqv3/creative/index.tsx

@@ -10,6 +10,7 @@ import { DeleteOutlined, PauseCircleOutlined, PlayCircleOutlined } from "@ant-de
 import '../../tencentAdPutIn/index.less'
 import HandleLog from "./handleLog"
 import moment from "moment"
+import RetriaModal from "./retriaModal"
 
 /** 审核结果 */
 export const AD_STATUS = {
@@ -29,6 +30,7 @@ const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, user
     const [failIdList, setFailIdList] = useState<{ adgroupId: number, code: number, message: string, messageCn: string }[]>([])
     const [failVisible, setFailVisible] = useState<boolean>(false)
     const [logVisible, setLogVisible] = useState<boolean>(false)
+    const [handleType, setHandleType] = useState<number>(1)
 
     const getDynamicCreativeV3List = useAjax((params) => getDynamicCreativeV3ListApi(params), { formatResult: true })
     const delBatchCreative = useAjax((params) => delBatchCreativeApi(params))
@@ -225,21 +227,39 @@ const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, user
             config={txDynamicConfig}
             configName="创意3.0"
             leftChild={<Space>
-                <Button type='primary' style={{ background: '#67c23a', borderColor: '#67c23a' }} loading={updateBatchDynamicCreativesInfo.loading} icon={<PlayCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => dynamicHandle('启动')}>启动</Button>
-                <Button type='primary' style={{ background: '#e6a23c', borderColor: '#e6a23c' }} loading={updateBatchDynamicCreativesInfo.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => dynamicHandle('暂停')}>暂停</Button>
-                <Popconfirm
-                    title="确定删除?"
-                    onConfirm={() => dynamicHandle('删除')}
-                    disabled={selectedRows.length === 0}
-                >
-                    <Button type='primary' danger icon={<DeleteOutlined />} loading={delBatchCreative.loading} disabled={selectedRows.length === 0}>删除</Button>
-                </Popconfirm>
+                <Select
+                    style={{ width: 120 }}
+                    onChange={(e) => {
+                        setHandleType(e)
+                        setSelectedRows([])
+                    }}
+                    value={handleType}
+                    dropdownMatchSelectWidth={false}
+                    options={[{ label: '基本操作', value: 1 }, { label: '复审操作', value: 2 }]}
+                />
+                {handleType === 1 ? <>
+                    <Button type='primary' style={{ background: '#67c23a', borderColor: '#67c23a' }} loading={updateBatchDynamicCreativesInfo.loading} icon={<PlayCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => dynamicHandle('启动')}>启动</Button>
+                    <Button type='primary' style={{ background: '#e6a23c', borderColor: '#e6a23c' }} loading={updateBatchDynamicCreativesInfo.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => dynamicHandle('暂停')}>暂停</Button>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => dynamicHandle('删除')}
+                        disabled={selectedRows.length === 0}
+                    >
+                        <Button type='primary' danger icon={<DeleteOutlined />} loading={delBatchCreative.loading} disabled={selectedRows.length === 0}>删除</Button>
+                    </Popconfirm>
+                </> : <>
+                    <RetriaModal 
+                        selectedRows={selectedRows}
+                        onChange={() => setSelectedRows([])}
+                    />
+                </>}
+
                 <Button type='dashed' onClick={() => { setLogVisible(true) }}>操作记录</Button>
             </Space>}
             rowSelection={{
                 selectedRowKeys: selectedRows.map(item => item.dynamicCreativeId.toString()),
                 getCheckboxProps: (record: any) => ({
-                    disabled: record.isDeleted
+                    disabled: handleType === 2 ? record.isDeleted || record.reviewStatus !== 'AD_STATUS_DENIED' : record.isDeleted
                 }),
                 onSelect: (record: { dynamicCreativeId: number }, selected: boolean) => {
                     if (selected) {

+ 62 - 0
src/pages/launchSystemV3/adqv3/creative/lookResult.tsx

@@ -0,0 +1,62 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getFsResultApi } from "@/services/adqV3/global"
+import { Button, Descriptions, Modal } from "antd"
+import React, { useState } from "react"
+import { APPEAL_STATUS_ENUM, ELEMENT_ENUM } from "../../tencentAdPutIn/const"
+import '../../tencentAdPutIn/index.less'
+
+interface Props {
+    data: {
+        componentInfo: { componentId: number },
+        elementFingerprint: string,
+        elementId: number,
+        accountId: number,
+        dynamicCreativeId: number
+    }
+}
+
+/**
+ * 查看复审结果
+ * @param param0 
+ * @returns 
+ */
+const LookResult: React.FC<Props> = ({ data }) => {
+
+    /********************************/
+    const [result, setResult] = useState<{ visible: boolean, data: { [x: string]: any } }>({ visible: false, data: {} })
+    const getFsResult = useAjax((params) => getFsResultApi(params))
+    /********************************/
+
+    const getRetriaResult = () => {
+        const { componentInfo: { componentId }, elementFingerprint, elementId, accountId, dynamicCreativeId } = data
+        getFsResult.run({ accountId, dynamicCreativeId, componentId, elementFingerPrint: elementFingerprint, elementId }).then(res => {
+            if (res) {
+                setResult({ visible: true, data: res })
+            }
+        })
+    }
+
+    return <>
+        <Button style={{ fontSize: 12, padding: 0, margin: 0 }} type="link" onClick={getRetriaResult} loading={getFsResult.loading}>查询复审结果</ Button>
+
+        {result.visible && <Modal
+            title={<strong>复审结果</strong>}
+            open={result.visible}
+            onCancel={() => setResult({ visible: false, data: {} })}
+            footer={null}
+            className='modalResetCss'
+            width={750}
+        >
+            <Descriptions bordered>
+                <Descriptions.Item label="元素类型">{ELEMENT_ENUM[result?.data?.elementType as keyof typeof ELEMENT_ENUM]}</Descriptions.Item>
+                <Descriptions.Item label="申诉复审需求">{result?.data?.appealDemand || '--'}</Descriptions.Item>
+                <Descriptions.Item label="申诉原因">{result?.data?.appealReason || '--'}</Descriptions.Item>
+                <Descriptions.Item label="申诉状态">{APPEAL_STATUS_ENUM[result?.data?.appealStatus as keyof typeof APPEAL_STATUS_ENUM]}</Descriptions.Item>
+                <Descriptions.Item label="详细描述">{result?.data?.description || '--'}</Descriptions.Item>
+                <Descriptions.Item label="申诉结果">{result?.data?.appealResult || '--'}</Descriptions.Item>
+            </Descriptions>
+        </Modal>}
+    </>
+}
+
+export default React.memo(LookResult)

+ 92 - 0
src/pages/launchSystemV3/adqv3/creative/retriaModal.tsx

@@ -0,0 +1,92 @@
+import { useAjax } from "@/Hook/useAjax"
+import { retrialBatchApi } from "@/services/launchAdq/adqv3"
+import { BlockOutlined } from "@ant-design/icons"
+import { Button, Card, Form, message, Modal, Radio, Space } from "antd"
+import React, { useState } from "react"
+import '../../tencentAdPutIn/index.less'
+
+
+interface Props {
+    selectedRows: any[]
+    onChange?: () => void
+}
+
+/**
+ * 批量复审
+ * @param param0 
+ * @returns 
+ */
+const RetriaModal: React.FC<Props> = ({ selectedRows, onChange }) => {
+
+    /**************************************/
+    const [visible, setVisible] = useState<boolean>(false)
+    const [form] = Form.useForm();
+
+    const retrialBatch = useAjax((params) => retrialBatchApi(params))
+    /**************************************/
+
+    const handleOk = (values: any) => {
+        const creativeReviewDTOs = selectedRows.map(item => ({ accountId: item.accountId, dynamicCreativeId: item.dynamicCreativeId }))
+        retrialBatch.run({ creativeReviewDTOs, ...values }).then(res => {
+            if (res) {
+                message.success('复审任务提交成功,结果请到操作记录中擦查看')
+                onChange?.()
+                setVisible(false)
+            }
+        })
+    }
+
+    return <>
+        <Button type='primary' icon={<BlockOutlined />} disabled={selectedRows.length === 0} onClick={() => setVisible(true)}>批量复审</Button>
+        <Modal
+            title={<strong>批量复审</strong>}
+            open={visible}
+            className='modalResetCss'
+            onCancel={() => setVisible(false)}
+            bodyStyle={{ padding: '0 0 40px', position: 'relative', borderRadius: '0 0 8px 8px' }}
+            footer={null}
+        >
+            <Form
+                form={form}
+                name="retria"
+                colon={false}
+                layout="vertical"
+                style={{ backgroundColor: '#f1f4fc', maxHeight: 600, overflow: 'hidden', overflowY: 'auto', padding: '10px 10px 10px', borderRadius: '0 0 8px 8px' }}
+                scrollToFirstError
+                onFinishFailed={({ errorFields }) => {
+                    message.error(errorFields?.[0]?.errors?.[0])
+                }}
+                onFinish={handleOk}
+                initialValues={{ skipExisting: true }}
+            >
+                <Card
+                    title={<strong style={{ fontSize: 14 }}>复审配置</strong>}
+                    className="cardResetCss"
+                >
+                    <Form.Item
+                        label={<strong>是否跳过已提交复审的创意</strong>}
+                        name='skipExisting'
+                        rules={[
+                            { required: true, message: '请选择是否跳过已提交复审的创意' }
+                        ]}
+                    >
+                        <Radio.Group buttonStyle="solid">
+                            <Radio.Button value={true}>跳过</Radio.Button>
+                            <Radio.Button value={false}>不跳过</Radio.Button>
+                        </Radio.Group>
+                    </Form.Item>
+                </Card>
+                <Form.Item className="submit_pull">
+                    <Space>
+                        <Button onClick={() => setVisible(false)}>取消</Button>
+                        <Button type="primary" htmlType="submit" className="modalResetCss" loading={retrialBatch.loading}>
+                            确定
+                        </Button>
+                    </Space>
+                </Form.Item>
+            </Form>
+        </Modal>
+    </>
+}
+
+export default React.memo(RetriaModal)

+ 2 - 2
src/pages/launchSystemV3/adqv3/creative/reviewDetails.tsx

@@ -1,5 +1,5 @@
 import { useAjax } from "@/Hook/useAjax"
-import { getCreativeReviewDetailApi } from "@/services/adqV3/global"
+import { getCreativeReviewDetailApi, getFsResultApi } from "@/services/adqV3/global"
 import { Card, Drawer, Table } from "antd"
 import React, { useEffect, useState } from "react"
 import { tableConfigDetail } from "./tableConfig"
@@ -34,7 +34,7 @@ const ReviewDetails: React.FC<Props> = ({ dynamic, visible, onClose }) => {
                         }
                     })
                     setTableData(elementResultList?.map((item: any, index: number, array: any[]) => {
-                        let params = { ...item, id: index, rowSpan: 1 }
+                        let params = { ...item, id: index, rowSpan: 1, accountId, dynamicCreativeId }
                         if (item?.componentInfo?.componentType === 'VIDEO') {
                             if (array?.[index + 1]?.componentInfo?.componentId === item?.componentInfo?.componentId) {
                                 params.rowSpan = 2

+ 15 - 2
src/pages/launchSystemV3/adqv3/creative/tableConfig.tsx

@@ -5,6 +5,7 @@ import { copy } from '@/utils/utils'
 import { DELIVERY_MODE, DYNAMIC_CREATIVE_TYPE } from '../const'
 import { ELEMENT_ENUM, SITE_SET_ENUM, creativeTemplate } from '../../tencentAdPutIn/const'
 import { AD_STATUS } from '.'
+import LookResult from './lookResult'
 const { Text } = Typography;
 
 function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle?: (b: any, suspend: '启动' | '暂停') => void): any {
@@ -245,7 +246,6 @@ function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle?:
 
 export const tableConfigDetail = (): TableProps<any>['columns'] => {
 
-
     return [
         {
             title: '创意组件',
@@ -275,7 +275,7 @@ export const tableConfigDetail = (): TableProps<any>['columns'] => {
                 if (!records?.componentInfo) {
                     return null
                 }
-                return <span style={{ fontSize: 12 }}>{AD_STATUS[records?.componentInfo?.reviewStatus  as keyof typeof AD_STATUS]}</span>
+                return <span style={{ fontSize: 12 }}>{AD_STATUS[records?.componentInfo?.reviewStatus as keyof typeof AD_STATUS]}</span>
             }
         },
         {
@@ -332,6 +332,19 @@ export const tableConfigDetail = (): TableProps<any>['columns'] => {
                 return siteSetList ? <Text style={{ fontSize: 12 }}>{siteSetList?.map((item: { siteSet: any }) => SITE_SET_ENUM[item?.siteSet as keyof typeof SITE_SET_ENUM])?.join(',')}</Text> : null
             }
         },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 110,
+            align: 'center',
+            render: (_, records) => {
+                if (records?.componentInfo?.reviewStatus === 'DENIED')
+                    return <LookResult data={records} />
+                else
+                    return '--'
+            }
+        },
     ]
 }
 

+ 7 - 0
src/pages/launchSystemV3/adqv3/typings.d.ts

@@ -65,4 +65,11 @@ declare namespace ADQV3 {
         creativeName?: string,
         userId?: number
     }
+    interface RetrialBatchProps {
+        creativeReviewDTOs: {
+            accountId: number,
+            dynamicCreativeId: number
+        },
+        skipExisting: boolean
+    }
 }

+ 8 - 2
src/pages/launchSystemV3/components/AdgroupTooltip/index.tsx

@@ -1,6 +1,6 @@
 import React from "react"
 import style from '../../tencentAdPutIn/create/index.less'
-import { AD_STATUS_ENUM, BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../tencentAdPutIn/const"
+import { AD_STATUS_ENUM, BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, ROI_ALL_OCATION_MODE, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../tencentAdPutIn/const"
 import { Typography } from "antd"
 import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook"
 import { ShowApplication } from "../../tencenTasset/application"
@@ -66,7 +66,13 @@ const AdgroupTooltip: React.FC<Props> = ({ data: adgroups, taskType }) => {
                     <p style={{ fontWeight: 'bold', color: '#000' }}>深度目标出价:{deepConversionSpec.deepConversionBehaviorSpec.bidAmount}元/{OPTIMIZATIONGOAL_ENUM[deepConversionSpec.deepConversionBehaviorSpec.goal as keyof typeof OPTIMIZATIONGOAL_ENUM] || '优化目标'}</p>
                 </> : <>
                     <p style={{ fontWeight: 'bold', color: '#000' }}>深度优化目标:{GOAL_ROAS_ENUM[deepConversionSpec.deepConversionWorthSpec.goal as keyof typeof GOAL_ROAS_ENUM]}</p>
-                    <p style={{ fontWeight: 'bold', color: '#000' }}>期望ROI:{deepConversionSpec.deepConversionWorthSpec.expectedRoi}</p>
+                    <p style={{ fontWeight: 'bold', color: '#000' }}>ROI分配方式:{ROI_ALL_OCATION_MODE.find(item => item.value === deepConversionSpec?.deepConversionWorthSpec?.roiAllocationMode)?.label}</p>
+                    {[2, 3].includes(deepConversionSpec?.deepConversionWorthSpec?.roiAllocationMode)
+                        ?
+                        <p style={{ fontWeight: 'bold', color: '#000' }}>期望ROI:{deepConversionSpec.deepConversionWorthSpec.expectedRoiMin}-{deepConversionSpec.deepConversionWorthSpec.expectedRoiMax}</p>
+                        :
+                        <p style={{ fontWeight: 'bold', color: '#000' }}>期望ROI:{deepConversionSpec.deepConversionWorthSpec.expectedRoi}</p>
+                    }
                 </>}
             </>}
             <p>一键起量:{autoAcquisitionEnabled ? '开启' : '关闭'}</p>

+ 5 - 2
src/pages/launchSystemV3/material/cloudNew/selectGroupSearch.tsx

@@ -29,9 +29,12 @@ const SelectGroupSearch: React.FC<Props> = ({ type, onSearch }) => {
         let params: any = []
         Object.keys(values).forEach(key => {
             let value = values[key]
-            if (['accountIds', 'adgroupIds', 'dynamicCreativeIds', 'tencentMaterialId', 'descriptionList'].includes(key) && value !== undefined && value !== null) {
+            if (['accountIds', 'adgroupIds', 'dynamicCreativeIds', 'tencentMaterialId'].includes(key) && value !== undefined && value !== null) {
                 let value1 = value.replace(/[,,\s]/g, ',')
                 params[key] = value1.split(',').filter((a: any) => a)
+            } else if (['descriptionList'].includes(key) && value !== undefined && value !== null) {
+                let value1 = value.replace(/[\s]/g, ',')
+                params[key] = value1.split(',').filter((a: any) => a)
             } else if ('uploadTime' === key && value?.length === 2) {
                 params.uploadTimeMin = moment(value?.[0]).format('YYYY-MM-DD')
                 params.uploadTimeMax = moment(value?.[1]).format('YYYY-MM-DD')
@@ -62,7 +65,7 @@ const SelectGroupSearch: React.FC<Props> = ({ type, onSearch }) => {
                     <Input style={{ width: 190 }} allowClear placeholder="请输入备注关键词" />
                 </Form.Item></Col>
                 <Col><Form.Item name={'descriptionList'}>
-                    <Input.TextArea rows={1} style={{ width: 190 }} allowClear placeholder="备注(精准匹配)(多个,,空格换行)" />
+                    <Input.TextArea rows={1} style={{ width: 190 }} allowClear placeholder="备注(精准匹配)(多个空格or换行隔开)" />
                 </Form.Item></Col>
                 <Col><Form.Item name={'sysUserIds'}>
                     <Select

+ 1 - 0
src/pages/launchSystemV3/tencenTasset/accountAssetSharing/modifyAccountGroup.tsx

@@ -78,6 +78,7 @@ const ModifyAccountGroup: React.FC<Props> = ({ initialValues, visible, onChange,
                 <Radio.Group buttonStyle="solid" disabled={initialValues}>
                     <Radio.Button value="material">素材</Radio.Button>
                     <Radio.Button value="conversion">转化归因</Radio.Button>
+                    <Radio.Button value="wechatDownPage">原生落地页</Radio.Button>
                 </Radio.Group>
             </Form.Item>
 

+ 1 - 1
src/pages/launchSystemV3/tencenTasset/accountAssetSharing/tableConfig.tsx

@@ -54,7 +54,7 @@ const columns = (del: (id: number) => void, update: (data: any) => void, handleS
             width: 90,
             align: 'center',
             render: (a) => {
-                return a === 'material' ? <Tag color="#f50">素材</Tag> : a === 'conversion' ? <Tag color="#2db7f5">转化归因</Tag> : '--'
+                return a === 'material' ? <Tag color="#f50">素材</Tag> : a === 'conversion' ? <Tag color="#2db7f5">转化归因</Tag> : a=== 'wechatDownPage' ? <Tag color="#87d068">原生落地页</Tag> : '--'
             }
         },
         {

+ 8 - 0
src/pages/launchSystemV3/tencentAdPutIn/const.ts

@@ -408,6 +408,7 @@ export const SelectTimeList = [
 
 /** 出价分配方式 */
 export const BID_ALL_OCATION_MODE = [{ label: '固定出价', value: 1 }, { label: '随机出价', value: 2 }, { label: '阶梯出价', value: 3 }]
+export const ROI_ALL_OCATION_MODE = [{ label: '固定ROI', value: 1 }, { label: '随机ROI', value: 2 }, { label: '阶梯ROI', value: 3 }]
 
 /** 深度优化类型 */
 export enum DEEP_CONVERSION_ENUM {
@@ -835,4 +836,11 @@ export enum EXCLUDED_DAY_ENUM {
 	EXCLUDED_DAY_ONE_DAY = '1天',
 	EXCLUDED_DAY_SEVEN_DAY = '7天',
 	EXCLUDED_DAY_ONE_MONTH = '1月',
+}
+
+/** 申诉复审状态 */
+export enum APPEAL_STATUS_ENUM {
+	COMPLETED = '申诉复审完成',
+	PENDING = '申诉复审待审',
+	CANCEL = '申诉复审取消'
 }

+ 34 - 9
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsMarketingContent.tsx

@@ -196,8 +196,8 @@ const AdgroupsMarketingContent: React.FC<{ accountIdList: number[], value?: any
                         marketingTargetType: 'MARKETING_TARGET_TYPE_FICTION',
                     })
                 }
-                setOGPparams({ ...OGPParams, marketingGoal: e as string }); 
-                setIsUpdate() 
+                setOGPparams({ ...OGPParams, marketingGoal: e as string });
+                setIsUpdate()
             }} />
         </Form.Item>
         {putInType === 'GAME' && <Form.Item name="marketingSubGoal" label={<strong>营销目的</strong>} rules={[{ required: true, message: '请选择营销目的!' }]}>
@@ -215,7 +215,7 @@ const AdgroupsMarketingContent: React.FC<{ accountIdList: number[], value?: any
             }} />
         </Form.Item>}
 
-        
+
 
         {marketingCarrierTypeList?.length > 0 && <Form.Item name="marketingCarrierType" label={<strong>营销载体类型</strong>} rules={[{ required: true, message: '请选择营销载体类型!' }]}>
             <New1Radio data={marketingCarrierTypeList} onChange={(e) => { setOGPparams({ ...OGPParams, marketingCarrierType: e }); setIsUpdate() }} />
@@ -286,18 +286,43 @@ const AdgroupsMarketingContent: React.FC<{ accountIdList: number[], value?: any
                         unCheckedChildren="关闭"
                         onChange={(e) => {
                             if (e) {
-                                form.setFieldsValue({
-                                    deepConversionSpec: {
-                                        deepConversionType: behaviorList?.length > 0 ? 'DEEP_CONVERSION_BEHAVIOR' : worthList?.length > 0 ? "DEEP_CONVERSION_WORTH" : ''
-                                    }
-                                })
+                                if (behaviorList?.length > 0) {
+                                    form.setFieldsValue({
+                                        deepConversionSpec: {
+                                            deepConversionType: 'DEEP_CONVERSION_BEHAVIOR',
+                                        }
+                                    })
+                                } else if (worthList?.length > 0) {
+                                    form.setFieldsValue({
+                                        deepConversionSpec: {
+                                            deepConversionType: 'DEEP_CONVERSION_WORTH',
+                                            deepConversionWorthSpec: { roiAllocationMode: 1 }
+                                        }
+                                    })
+                                } else {
+                                    form.setFieldsValue({
+                                        deepConversionSpec: {
+                                            deepConversionType: undefined,
+                                        }
+                                    })
+                                }
                             }
                         }}
                     />
                 </Form.Item>
                 {depthConversionEnabled && <>
                     <Form.Item label={<strong>深度优化类型</strong>} name={['deepConversionSpec', 'deepConversionType']} rules={[{ required: true, message: '请选择深度优化类型' }]}>
-                        <New1Radio data={deepConversionData} />
+                        <New1Radio
+                            data={deepConversionData}
+                            onChange={(e) => {
+                                form.setFieldsValue({
+                                    deepConversionSpec: {
+                                        deepConversionType: e,
+                                        ...(e === 'DEEP_CONVERSION_WORTH' ? { deepConversionWorthSpec: { roiAllocationMode: 1 } } : {})
+                                    }
+                                })
+                            }}
+                        />
                     </Form.Item>
                     <Form.Item label={<strong>深度优化目标</strong>} name={['deepConversionSpec', deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? 'deepConversionBehaviorSpec' : 'deepConversionWorthSpec', 'goal']} rules={[{ required: true, message: '请选择深度优化目标' }]}>
                         <Select style={{ width: 480 }} placeholder='请选择'>

+ 54 - 4
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsPrice.tsx

@@ -2,7 +2,7 @@ import { Card, Form, Input, InputNumber, Space, Switch, Tooltip } from "antd"
 import React, { useContext } from "react"
 import { DispatchAd } from "./newCreateAd";
 import New1Radio from "@/pages/launchSystemV3/components/New1Radio";
-import { BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, OPTIMIZATIONGOAL_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
+import { BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, OPTIMIZATIONGOAL_ENUM, ROI_ALL_OCATION_MODE, SMART_BID_TYPE_ENUM } from "../../const";
 import { QuestionCircleFilled } from "@ant-design/icons";
 
 
@@ -18,6 +18,7 @@ const AdgroupsPrice: React.FC = () => {
     const siteSet = Form.useWatch('siteSet', form)
     const bidMode = Form.useWatch('bidMode', form)
     const bidAllocationMode = Form.useWatch('bidAllocationMode', form)
+    const roiAllocationMode = Form.useWatch(['deepConversionSpec', 'deepConversionWorthSpec', 'roiAllocationMode'], form)
     const optimizationGoal = Form.useWatch('optimizationGoal', form)
     const smartBidType = Form.useWatch('smartBidType', form)
     const bidScene = Form.useWatch('bidScene', form)
@@ -110,7 +111,7 @@ const AdgroupsPrice: React.FC = () => {
                     <Form.Item name='bidAmountMax' rules={[{ required: true, message: '请输入价格最大值' }]} noStyle>
                         <Input
                             placeholder={`请输入价格最大值`}
-                            style={{ width: 180 }}
+                            style={{ width: 200 }}
                         />
                     </Form.Item>
                     <span>元/{optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}</span>
@@ -124,7 +125,10 @@ const AdgroupsPrice: React.FC = () => {
                 </Form.Item>
             </> :
                 deepConversionType === 'DEEP_CONVERSION_WORTH' ? <>
-                    <Form.Item
+                    <Form.Item label={<strong>ROI分配方式</strong>} name={['deepConversionSpec', 'deepConversionWorthSpec', 'roiAllocationMode']} rules={[{ required: true, message: '请选择ROI分配方式' }]}>
+                        <New1Radio data={ROI_ALL_OCATION_MODE} />
+                    </Form.Item>
+                    {roiAllocationMode === 1 ? <Form.Item
                         label={<strong>期望ROI</strong>}
                         name={['deepConversionSpec', 'deepConversionWorthSpec', 'expectedRoi']}
                         rules={[
@@ -141,7 +145,53 @@ const AdgroupsPrice: React.FC = () => {
                         ]}
                     >
                         <InputNumber style={{ width: 480 }} placeholder={`期望ROI目标范围${goal === 'GOAL_1DAY_MONETIZATION_ROAS' ? '0.001~50' : '0.001~1000'},输入0.05,表示ROI目标为5%`} />
-                    </Form.Item>
+                    </Form.Item> : <Form.Item label={<strong>{roiAllocationMode === 2 ? '随机出价' : '阶梯出价'}</strong>} required>
+                        <Space>
+                            <Form.Item
+                                name={['deepConversionSpec', 'deepConversionWorthSpec', 'expectedRoiMin']}
+                                rules={[
+                                    { required: true, message: '请输入期望ROI最小值' },
+                                    { type: 'number', ...(goal === 'GOAL_1DAY_MONETIZATION_ROAS' ? { min: 0.001, max: 50, message: '范围0.001~50' } : { min: 0.001, max: 1000, message: '范围0.001~1000' }) },
+                                    {
+                                        validator: (_: any, value: string) => {
+                                            if (!value || /^\d+(\.\d{0,3})?$/.test(value)) {
+                                                return Promise.resolve();
+                                            }
+                                            return Promise.reject(new Error('请输入最多三位小数'));
+                                        }
+                                    }
+                                ]}
+                                noStyle
+                            >
+                                <InputNumber
+                                    placeholder={`请输入期望ROI最小值`}
+                                    style={{ width: 228 }}
+                                />
+                            </Form.Item>
+                            <span>-</span>
+                            <Form.Item
+                                name={['deepConversionSpec', 'deepConversionWorthSpec', 'expectedRoiMax']}
+                                rules={[
+                                    { required: true, message: '请输入期望ROI最大值' },
+                                    { type: 'number', ...(goal === 'GOAL_1DAY_MONETIZATION_ROAS' ? { min: 0.001, max: 50, message: '范围0.001~50' } : { min: 0.001, max: 1000, message: '范围0.001~1000' }) },
+                                    {
+                                        validator: (_: any, value: string) => {
+                                            if (!value || /^\d+(\.\d{0,3})?$/.test(value)) {
+                                                return Promise.resolve();
+                                            }
+                                            return Promise.reject(new Error('请输入最多三位小数'));
+                                        }
+                                    }
+                                ]}
+                                noStyle
+                            >
+                                <InputNumber
+                                    placeholder={`请输入期望ROI最大值`}
+                                    style={{ width: 228 }}
+                                />
+                            </Form.Item>
+                        </Space>
+                    </Form.Item>}
                 </> : null}
 
             {((bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') && (putInType === 'GAME' ? bidScene !== 'BID_SCENE_NORMAL_MAX' : smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC')) && <>

+ 8 - 2
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/index.tsx

@@ -4,7 +4,7 @@ import NewCreateAd from "./newCreateAd"
 import { DispatchAddelivery } from "..";
 import { Button, Modal, Typography } from "antd";
 import { EditOutlined } from "@ant-design/icons";
-import { AD_STATUS_ENUM, BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
+import { AD_STATUS_ENUM, BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, ROI_ALL_OCATION_MODE, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
 import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook";
 import { arraysHaveSameValues } from "@/utils/utils";
 import '../../index.less'
@@ -84,7 +84,13 @@ const Ad: React.FC = () => {
                                 <p style={{ fontWeight: 'bold', color: '#000' }}>深度目标出价:{deepConversionSpec.deepConversionBehaviorSpec.bidAmount}元/{OPTIMIZATIONGOAL_ENUM[deepConversionSpec.deepConversionBehaviorSpec.goal as keyof typeof OPTIMIZATIONGOAL_ENUM] || '优化目标'}</p>
                             </> : <>
                                 <p style={{ fontWeight: 'bold', color: '#000' }}>深度优化目标:{GOAL_ROAS_ENUM[deepConversionSpec.deepConversionWorthSpec.goal as keyof typeof GOAL_ROAS_ENUM]}</p>
-                                <p style={{ fontWeight: 'bold', color: '#000' }}>期望ROI:{deepConversionSpec.deepConversionWorthSpec.expectedRoi}</p>
+                                <p style={{ fontWeight: 'bold', color: '#000' }}>ROI分配方式:{ROI_ALL_OCATION_MODE.find(item => item.value === deepConversionSpec?.deepConversionWorthSpec?.roiAllocationMode)?.label}</p>
+                                {[2, 3].includes(deepConversionSpec?.deepConversionWorthSpec?.roiAllocationMode)
+                                    ?
+                                    <p style={{ fontWeight: 'bold', color: '#000' }}>期望ROI:{deepConversionSpec.deepConversionWorthSpec.expectedRoiMin}-{deepConversionSpec.deepConversionWorthSpec.expectedRoiMax}</p>
+                                    :
+                                    <p style={{ fontWeight: 'bold', color: '#000' }}>期望ROI:{deepConversionSpec.deepConversionWorthSpec.expectedRoi}</p>
+                                }
                             </>}
                         </>}
                         <p>一键起量:{autoAcquisitionEnabled ? '开启' : '关闭'}</p>

+ 2 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/newCreateAd.tsx

@@ -197,11 +197,12 @@ const NewCreateAd: React.FC<Props> = ({ accountIdList, value, visible, onChange,
                 bidMode: 'BID_MODE_OCPM',
                 smartBidType: 'SMART_BID_TYPE_CUSTOM',
                 bidScene: 'BID_SCENE_NORMAL_AVERAGE',
-                optimizationGoal: putInType === 'NOVEL' ? 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER' : 'OPTIMIZATIONGOAL_MOBILE_APP_AD_INCOME',
+                optimizationGoal: putInType === 'NOVEL' ? null : 'OPTIMIZATIONGOAL_MOBILE_APP_AD_INCOME',
                 deepConversionSpec: {
                     deepConversionType: 'DEEP_CONVERSION_WORTH'
                 },
                 bidAllocationMode: 1,
+                roiAllocationMode: 1,
                 // bidAmount: 1000,
                 timeSeries: getTimeSeriesList(),
                 autoAcquisitionEnabled: false,

+ 21 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx

@@ -181,6 +181,15 @@ const Create: React.FC = () => {
                             adgroupDTO.bidAllocationMode = adgroupDTO?.bidAllocationMode || 1
                             if (adgroupDTO?.deepConversionSpec) {
                                 adgroupDTO.depthConversionEnabled = true
+                                if (adgroupDTO?.deepConversionSpec?.deepConversionType === 'DEEP_CONVERSION_WORTH' && !adgroupDTO?.deepConversionSpec?.deepConversionWorthSpec?.roiAllocationMode) {
+                                    adgroupDTO.deepConversionSpec = {
+                                        ...adgroupDTO.deepConversionSpec,
+                                        deepConversionWorthSpec: {
+                                            ...adgroupDTO.deepConversionSpec.deepConversionWorthSpec,
+                                            roiAllocationMode: 1
+                                        }
+                                    }
+                                }
                             } else {
                                 adgroupDTO.depthConversionEnabled = false
                             }
@@ -285,7 +294,7 @@ const Create: React.FC = () => {
                 })
             } else if (adqAdData) {
                 const { addelivery, accountCreateLogs, materialData, textData } = JSON.parse(adqAdData)
-                if (addelivery?.adgroups) {
+                if (addelivery?.adgroups && Object.keys(addelivery.adgroups).length) {
                     if (addelivery?.adgroups?.beginDate && moment(addelivery?.adgroups?.beginDate) < moment()) {
                         addelivery.adgroups.beginDate = moment().format('YYYY-MM-DD')
                         message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
@@ -295,6 +304,17 @@ const Create: React.FC = () => {
                         message.warning('请注意,检测投放结束日期小于今天,已自动改成今天,如需修改,请重新设置')
                     }
                     addelivery.adgroups.bidAllocationMode = addelivery.adgroups?.bidAllocationMode || 1
+                    if (addelivery?.adgroups?.deepConversionSpec) {
+                        if (addelivery?.adgroups?.deepConversionSpec?.deepConversionType === 'DEEP_CONVERSION_WORTH' && !addelivery?.adgroups?.deepConversionSpec?.deepConversionWorthSpec?.roiAllocationMode) {
+                            addelivery.adgroups.deepConversionSpec = {
+                                ...addelivery.adgroups.deepConversionSpec,
+                                deepConversionWorthSpec: {
+                                    ...addelivery.adgroups.deepConversionSpec.deepConversionWorthSpec,
+                                    roiAllocationMode: 1
+                                }
+                            }
+                        }
+                    }
                 }
                 setAddelivery({ ...addelivery })
                 setAccountCreateLogs(accountCreateLogs)

+ 13 - 1
src/services/adqV3/global.ts

@@ -430,6 +430,18 @@ export async function getCreativeReviewDetailApi({ accountId, dynamicCreativeIds
     })
 }
 
+/**
+ * 查询元素组件申诉复审结果
+ * @param data 
+ * @returns 
+ */
+export async function getFsResultApi(data: { accountId: number, componentId: number, dynamicCreativeId: number, elementFingerPrint: string, elementId: number }) {
+    return request(api + `/adq/adgroup/getResult`, {
+        method: 'POST',
+        data
+    })
+}
+
 /**
  * 同步广告
  * @param data 
@@ -905,7 +917,7 @@ export async function delsCopyWritingApi(ids: number[]) {
 
 export type ApplicationTypeProps = 'IOS' | 'ANDROID'
 export interface GetApplicationListProps {
-    pageNum: number, 
+    pageNum: number,
     pageSize: number,
     createBy?: number,
     applicationId?: string

+ 12 - 0
src/services/launchAdq/adqv3.ts

@@ -133,6 +133,18 @@ export async function updateBatchDynamicCreativesInfoApi(data: ADQV3.ModifyStatu
     });
 }
 
+/**
+ * 批量提交复审
+ * @param data 
+ * @returns 
+ */
+export async function retrialBatchApi(data: ADQV3.RetrialBatchProps) {
+    return request(api + '/adq/adgroup/retrialBatch', {
+        method: 'POST',
+        data
+    });
+}
+
 /**
  * 同步创意
  * @param data