Bläddra i källkod

深度优化,分版位出价

wjx 2 år sedan
förälder
incheckning
f0b0cb2d91

+ 127 - 0
src/pages/launchSystemNew/launchManage/createAd/ad/modal/adPositionList.tsx

@@ -0,0 +1,127 @@
+import { SiteSetEnum } from "@/services/launchAdq/enum"
+import { ColumnHeightOutlined } from "@ant-design/icons"
+import { useDrag } from "ahooks"
+import { Button, Modal } from "antd"
+import React, { useEffect, useState } from "react"
+import arrayMove from "array-move";
+import { Dropping, DroppingCon } from "./dropping"
+import './index.less'
+
+interface Props {
+    value: { siteSet: string[], bidCoefficient?: number, deepBidCoefficient?: number }[],   // 版位组合
+    onChange?: (data: { siteSet: string[], bidCoefficient?: number, deepBidCoefficient?: number }[]) => void
+}
+/**
+ * 广告版位组合设置
+ * @returns 
+ */
+const AdPositionList: React.FC<Props> = (props) => {
+
+    /*************************/
+    const { value, onChange } = props
+    const [visible, setVisible] = useState<boolean>(false)
+    const [dragging, setDragging] = useState<string | null>(null);
+    const [siteSetPackageData, setSiteSetPackageData] = useState<{ siteSet: string[], bidCoefficient?: number, deepBidCoefficient?: number }[]>([])
+    /*************************/
+
+    useEffect(() => {
+        setSiteSetPackageData(value || [])
+    }, [value])
+
+    const getDragProps = useDrag({
+        onDragStart: (data) => {
+            setDragging(data);
+        },
+        onDragEnd: () => {
+            setDragging(null);
+        },
+    });
+
+    const handleOk = () => {
+        console.log('siteSetPackageData---->', siteSetPackageData);
+        onChange && onChange(JSON.parse(JSON.stringify(siteSetPackageData)))
+        setVisible(false)
+    }
+
+    const setHandle = (to: string, name: string) => {
+        let newSiteSetPackageData: { siteSet: string[], bidCoefficient?: number, deepBidCoefficient?: number }[] = JSON.parse(JSON.stringify(siteSetPackageData))
+        setDragging(null);
+        let data = name?.split('_')
+        let type = data[0]
+        let index = data[1]
+        let oldIndex = newSiteSetPackageData?.findIndex(item => item.siteSet?.includes(to))
+        let oldSiteSetPackage = newSiteSetPackageData?.find(item => item.siteSet?.includes(to))
+        switch (type) {
+            case 'dropping':
+                if ((oldSiteSetPackage && oldSiteSetPackage?.siteSet?.length === 1) && index && Number(index) === oldIndex) {
+                    return
+                }
+                if (oldSiteSetPackage && oldSiteSetPackage?.siteSet?.length > 1) {
+                    newSiteSetPackageData = newSiteSetPackageData.map((item, index) => {
+                        if (oldIndex === index) {
+                            let newsiteSet = item.siteSet?.filter(item => item !== to)
+                            return { ...item, siteSet: newsiteSet }
+                        } else {
+                            return item
+                        }
+                    })
+                    newSiteSetPackageData.splice(Number(index), 0, { siteSet: [to], bidCoefficient: 1, deepBidCoefficient: 1 })
+                    setSiteSetPackageData(newSiteSetPackageData)
+                } else {
+                    setSiteSetPackageData(() => arrayMove(newSiteSetPackageData, oldIndex, Number(index)))
+                }
+                break
+            case 'con':
+                newSiteSetPackageData = newSiteSetPackageData?.map((item, i) => {
+                    if (Number(index) === i + 1) {
+                        let siteSet = item.siteSet
+                        siteSet.push(to)
+                        return { bidCoefficient: 1, deepBidCoefficient: 1, siteSet }
+                    }
+                    return { ...item, bidCoefficient: 1, deepBidCoefficient: 1 }
+                })
+                if (oldSiteSetPackage && oldSiteSetPackage?.siteSet?.length > 1) {
+                    newSiteSetPackageData = newSiteSetPackageData?.map((item, index) => {
+                        if (oldIndex === index) {
+                            let newsiteSet = item.siteSet?.filter(item => item !== to)
+                            return { ...item, siteSet: newsiteSet }
+                        } else {
+                            return item
+                        }
+                    })
+                } else {
+                    newSiteSetPackageData = newSiteSetPackageData?.filter((item, index) => index !== oldIndex)
+                }
+                setSiteSetPackageData(newSiteSetPackageData)
+                break
+        }
+    }
+
+    return <>
+        <Button onClick={() => setVisible(true)}>修改广告版位组合</Button>
+        {visible && <Modal
+            title={<div>
+                <h3><strong>修改版位组合</strong></h3>
+                <div style={{ color: '#939599', fontSize: 12 }}>你可拖拽广告版位进行重新组合或拆分新的组合</div>
+            </div>}
+            visible={visible}
+            onOk={handleOk}
+            bodyStyle={{ paddingTop: 0, paddingBottom: 0 }}
+            onCancel={() => setVisible(false)}
+        >
+            {siteSetPackageData?.map((item, index) => {
+                return <div key={index}>
+                    {index === 0 && <Dropping name={'dropping_0'} className='wait' onChange={setHandle} />}
+                    <div className='myUl'>
+                        <div className='myTitle'>组合{index + 1}</div>
+                        <DroppingCon siteSet={item?.siteSet} name={`con_${index + 1}`} onChange={setHandle} getDragProps={getDragProps} dragging={dragging}/>
+                    </div>
+                    <Dropping name={`dropping_${index + 1}`} className='wait' onChange={setHandle} />
+                </div>
+            })}
+        </Modal>}
+    </>
+}
+
+
+export default React.memo(AdPositionList)

+ 169 - 0
src/pages/launchSystemNew/launchManage/createAd/ad/modal/bidAdjustment.tsx

@@ -0,0 +1,169 @@
+import { OptimizationGoalEnum, SiteSetEnum } from "@/services/launchAdq/enum"
+import { EditOutlined } from "@ant-design/icons"
+import { Button, InputNumber, Space } from "antd"
+import React, { useEffect, useRef, useState } from "react"
+import './index.less'
+
+interface Props {
+    value?: { siteSet: string[], bidCoefficient?: number, deepBidCoefficient?: number }[],   // 版位
+    bidAmount: number,   // 出价
+    onChange?: (data: any[]) => void,
+    deepConversionType?: string,  // DEEP_CONVERSION_BEHAVIOR 优化转化行为  DEEP_CONVERSION_WORTH 优化 ROI  BID_MODE 出价
+    goal?: string,
+    optimizationGoal?: string,
+    children?: JSX.Element
+}
+/**
+ * 分版位深度目标出价
+ * @returns 
+ */
+const BidAdjustment: React.FC<Props> = (props) => {
+
+    /*****************************/
+    const { bidAmount, value, onChange, deepConversionType, goal, optimizationGoal, children } = props
+    const [tableData, setTableData] = useState<{ siteSet: string[], bidCoefficient?: number, deepBidCoefficient?: number, bidAmount: number | string, openEditBid: boolean }[]>([])
+    const [bidModeName, setBidModeName] = useState<string>('')
+    const ref: { current: any } = useRef(null)
+    /*****************************/
+
+    // 处理表格数据
+    useEffect(() => {
+        if (value && deepConversionType) {
+            setTableData(value?.map((item: any, index) => {
+                if (deepConversionType === 'BID_MODE') {
+                    return { ...item, bidAmount: bidAmount ? (item.bidCoefficient * bidAmount).toFixed(3) : '--', openEditBid: tableData[index]?.openEditBid || false }
+                } else {
+                    return { ...item, bidAmount: bidAmount ? (item.deepBidCoefficient * bidAmount).toFixed(3) : '--', openEditBid: tableData[index]?.openEditBid || false }
+                }
+            }))
+        } else {
+            setTableData([])
+        }
+    }, [value, deepConversionType, bidAmount])
+
+    // 处理名称
+    useEffect(() => {
+        switch (deepConversionType) {
+            case 'DEEP_CONVERSION_BEHAVIOR':
+                setBidModeName(`深度目标出价(元/${goal ? OptimizationGoalEnum[goal] : '优化目标'})`)
+                break
+            case 'DEEP_CONVERSION_WORTH':
+                setBidModeName(`期望ROI (首日付费ROI)`)
+                break
+            case 'BID_MODE':
+                setBidModeName(`出价(元/${optimizationGoal ? OptimizationGoalEnum[optimizationGoal] : ''})`)
+                break
+        }
+    }, [deepConversionType, goal])
+
+    // 设置系数
+    const setCoefficient = (num: number, index: number) => {
+        let data: any[] = []
+        if (deepConversionType === 'BID_MODE') {
+            data = tableData?.map((item, index1) => {
+                if (index === index1) {
+                    return { ...item, bidCoefficient: num, bidAmount: (bidAmount * num).toFixed(3) }
+                } else {
+                    return item
+                }
+            })
+        } else {
+            data = tableData?.map((item, index1) => {
+                if (index === index1) {
+                    return { ...item, deepBidCoefficient: num, bidAmount: (bidAmount * num).toFixed(3) }
+                } else {
+                    return item
+                }
+            })
+        }
+        setTableData(data)
+        onChange && onChange(data?.map(item => ({ siteSet: item.siteSet, bidCoefficient: item.bidCoefficient, deepBidCoefficient: item.deepBidCoefficient })))
+    }
+    // 设置价格
+    const setBidAmount = (amount: number, index: number) => {
+        let data: any[] = []
+        if (deepConversionType === 'BID_MODE') {
+            let newBidCoefficient = amount / bidAmount
+            if (newBidCoefficient < 0.01) {
+                newBidCoefficient = 0.01
+            } else if (newBidCoefficient > 5) {
+                newBidCoefficient = 5
+            }
+            newBidCoefficient = Math.round(newBidCoefficient * 100) / 100
+            data = tableData?.map((item, index1) => {
+                if (index === index1) {
+                    return { ...item, bidCoefficient: newBidCoefficient, bidAmount: (bidAmount * newBidCoefficient).toFixed(3) }
+                } else {
+                    return item
+                }
+            })
+
+        } else {
+            let newDeepBidCoefficient = amount / bidAmount
+            if (newDeepBidCoefficient < 0.01) {
+                newDeepBidCoefficient = 0.01
+            } else if (newDeepBidCoefficient > 5) {
+                newDeepBidCoefficient = 5
+            }
+            newDeepBidCoefficient = Math.round(newDeepBidCoefficient * 100) / 100
+            data = tableData?.map((item, index1) => {
+                if (index === index1) {
+                    return { ...item, deepBidCoefficient: newDeepBidCoefficient, bidAmount: (bidAmount * newDeepBidCoefficient).toFixed(3) }
+                } else {
+                    return item
+                }
+            })
+        }
+        setTableData(data)
+        onChange && onChange(data?.map(item => ({ siteSet: item.siteSet, bidCoefficient: item.bidCoefficient, deepBidCoefficient: item.deepBidCoefficient })))
+    }
+
+    // 设置出价
+    const setOpenEditBid = (index: number) => {
+        setTableData(tableData?.map((item, index1) => {
+            if (index === index1) {
+                if (item.openEditBid) {
+                    ref.current = null
+                } else {
+                    setTimeout(() => {
+                        ref.current?.focus()
+                    }, 100)
+                }
+                return { ...item, openEditBid: !item.openEditBid }
+            } else {
+                return item
+            }
+        }))
+    }
+
+    return <div>
+        <div className="myTable">
+            <div className="tableUl" style={{ height: 40 }}>
+                <div>广告版位组合</div>
+                <div>系数 (0.01~5)</div>
+                <div style={{ textAlign: 'right' }}>{bidModeName}</div>
+            </div>
+            {tableData?.map((item, index) => {
+                return <div className="tableUl tr" key={'table' + index}>
+                    <div>
+                        {item?.siteSet?.map(item => <div key={item}>{SiteSetEnum[item]}</div>)}
+                    </div>
+                    <div className="coefficient">
+                        <InputNumber min={0.01} max={5} step={0.1} disabled={!bidAmount} value={deepConversionType === 'BID_MODE' ? item.bidCoefficient : item.deepBidCoefficient} onChange={(e) => setCoefficient(e, index)} />
+                        <div className="tips">
+                            {((deepConversionType === 'BID_MODE' ? item.bidCoefficient : item.deepBidCoefficient) || 1)?.toString()?.split('.')[1]?.length > 2 ? '系数仅支持2位小数' : ''}
+                        </div>
+                    </div>
+                    <div style={{ textAlign: 'right' }}><Space>
+                        {!item.openEditBid ? item.bidAmount : <InputNumber ref={ref} value={Number(item.bidAmount)} onBlur={() => { setOpenEditBid(index) }} onChange={(e) => setBidAmount(e, index)} />}
+                        {!item.openEditBid && <Button icon={<EditOutlined />} type='link' onClick={() => { setOpenEditBid(index) }} style={{ padding: 0 }} disabled={!bidAmount}></Button>}
+                    </Space></div>
+                </div>
+            })}
+        </div>
+        {children && <div style={{ marginTop: 10 }}>{children}</div>}
+    </div>
+}
+
+
+export default React.memo(BidAdjustment)

+ 70 - 0
src/pages/launchSystemNew/launchManage/createAd/ad/modal/dropping.tsx

@@ -0,0 +1,70 @@
+import { SiteSetEnum } from "@/services/launchAdq/enum";
+import { ColumnHeightOutlined } from "@ant-design/icons";
+import { useDrop } from "ahooks";
+import React from "react"
+
+interface PropsCon {
+    siteSet: string[],
+    name: string,    // 接收区名字区分接收区
+    onChange?: (to: string, name: string) => void,
+    getDragProps: any,
+    dragging?: any,
+}
+
+/**
+ * 带内容的接收区
+ * @param props 
+ * @returns 
+ */
+const DroppingCon: React.FC<PropsCon> = (props) => {
+
+    /********************************/
+    const { siteSet, name, onChange, getDragProps, dragging } = props
+    /********************************/
+
+    const [dropProps, { isHovering }] = useDrop({
+        onDom: (con: string, e) => { // 头部
+            onChange && onChange(con, name)
+        },
+    });
+
+    return <div className={`myLi ${isHovering ? 'dragging' : ''}`}>
+        {(dragging && !siteSet?.includes(dragging)) && <div className="hoverDiv" {...dropProps}></div>}
+        {siteSet?.map((item1, index1) => {
+            return <div {...getDragProps(item1)} className="dropping">
+                <ColumnHeightOutlined />
+                <span style={{ color: '#313233' }}>{SiteSetEnum[item1]}</span>
+            </div>
+        })}
+    </div>
+}
+
+interface Props {
+    name: string,    // 接收区名字区分接收区
+    className?: string,
+    onChange?: (to: string, name: string) => void
+}
+
+/**
+ * 拖拽接收区
+ * @returns 
+ */
+const Dropping: React.FC<Props> = (props) => {
+
+    /************************/
+    const { className, name, onChange } = props
+    /************************/
+
+    const [dropProps, { isHovering }] = useDrop({
+        onDom: (con: string, e) => { // 头部
+            onChange && onChange(con, name)
+        },
+    });
+
+    return <div className={`${className} dropping ${isHovering ? 'dragging' : ''}`} {...dropProps}></div>
+}
+
+export {
+    DroppingCon,
+    Dropping
+}

+ 91 - 0
src/pages/launchSystemNew/launchManage/createAd/ad/modal/index.less

@@ -0,0 +1,91 @@
+.myUl {
+  display: flex;
+  justify-content: flex-start;
+  width: 100%;
+  align-items: center;
+}
+
+.wait {
+  height: 20px;
+  margin: 6px 0;
+  border-radius: 4px;
+}
+
+.myTitle {
+  width: 55px;
+}
+
+.myLi {
+  flex: 1;
+  border: 1px solid #dfe1e6;
+  display: flex;
+  border-radius: 8px;
+  flex-direction: column;
+  cursor: pointer;
+  position: relative;
+
+  >div:not(:first-child) {
+    border-top: 1px solid rgb(230, 232, 237);
+  }
+}
+.dropping {
+  position: relative;
+  padding: 10px 15px;
+  border-radius: 8px;
+}
+.dragging {
+    background-color: #e4f2ff !important;
+    border: 1px dashed #1890ff !important;
+}
+.hoverDiv {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 10;
+  border: none,
+}
+
+
+.myTable {
+  width: 95%;
+  border: 1px solid rgb(196, 196, 196);
+  border-radius: 8px;
+  overflow: hidden;
+
+  >div:nth-child(odd) {
+    background-color: #fff;
+  }
+
+  >div:nth-child(even) {
+    background-color: rgb(250, 251, 252);
+  }
+
+  .tableUl {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+
+    >div {
+      padding: 5px 10px;
+      box-sizing: border-box;
+      width: 33.3%;
+    }
+  }
+  .tr {
+    min-height: 80px;
+  }
+}
+
+.coefficient {
+  position: relative;
+  .tips {
+    position: absolute;
+    bottom: -15px;
+    color: red;
+    font-size: 12px;
+  }
+}

+ 276 - 15
src/pages/launchSystemNew/launchManage/createAd/ad/modal/leadAd.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback, useEffect, useState } from 'react'
 import { Modal, Form, Input, Divider, Select, Radio, DatePicker, Switch, Checkbox, message, Tooltip, Row, Col, Space, TimePicker, Button } from 'antd'
-import { BidModeEnum, OptimizationGoalEnum, BidStrategyEnum, AdStatus } from '@/services/launchAdq/enum'
+import { BidModeEnum, OptimizationGoalEnum, BidStrategyEnum, AdStatus, GoalRoasEnum } from '@/services/launchAdq/enum'
 import { ModalConfig } from '../index'
 import moment from 'moment';
 import { useAjax } from '@/Hook/useAjax';
@@ -8,6 +8,8 @@ import { getSceneTagsList } from '@/services/launchAdq/global';
 import { ExclamationCircleOutlined } from '@ant-design/icons';
 import { CreateAdProps } from '@/services/launchAdq/createAd';
 import { createSysAdgroups } from '@/services/launchAdq/localAd';
+import AdPositionList from './adPositionList';
+import BidAdjustment from './bidAdjustment';
 const { RangePicker }: { RangePicker: any } = DatePicker;
 let DatePickers: any = DatePicker
 interface Props {
@@ -20,6 +22,23 @@ interface Props {
     dataInfo?: any,
     ajax: any
 }
+
+const siteSetData = [
+    { siteSet: 'SITE_SET_MOMENTS', type: 1 },
+    { siteSet: 'SITE_SET_MOBILE_UNION', type: 3 },
+    { siteSet: 'SITE_SET_TENCENT_NEWS', type: 2 },
+    { siteSet: 'SITE_SET_TENCENT_VIDEO', type: 2 },
+    { siteSet: 'SITE_SET_KANDIAN', type: 2 },
+    { siteSet: 'SITE_SET_QQ_MUSIC_GAME', type: 2 },
+    { siteSet: 'SITE_SET_WECHAT', type: 4 },
+]
+
+export interface SiteSetPackageDataProps {
+    siteSet: string[],
+    bidCoefficient?: number,
+    deepBidCoefficient?: number
+}
+
 /**收集线索广告弹窗*/
 function LeadAdModal(props: Props) {
     let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm, ajax } = props
@@ -40,6 +59,21 @@ function LeadAdModal(props: Props) {
     let wechatSceneType = Form.useWatch('wechatSceneType', form)
     let automaticSiteEnabled = Form.useWatch('automaticSiteEnabled', form)
     let optimizationGoal = Form.useWatch('optimizationGoal', form)
+
+    let depthConversionEnabled = Form.useWatch('depthConversionEnabled', form)
+    let deepConversionType = Form.useWatch('deepConversionType', form)
+    let goal = Form.useWatch('goal', form)
+    let bidAdjustmentEnabled = Form.useWatch('bidAdjustmentEnabled', form)
+    let siteSetPackage = Form.useWatch('siteSetPackage', form)
+    let deepBidAmount = Form.useWatch('deepBidAmount', form)
+    let bidAmountAdjustmentEnabled = Form.useWatch('bidAmountAdjustmentEnabled', form)
+    let bidAmount = Form.useWatch('bidAmount', form)
+
+    const [behaviorList, setBehaviorList] = useState<string[]>([])
+    const [worthList, setWorthList] = useState<string[]>([])
+
+
+
     // 确定事件
     const handleOk = useCallback(() => {
         form.validateFields().then(values => {
@@ -72,8 +106,50 @@ function LeadAdModal(props: Props) {
                                 }
                             }
                         }
-
                         break;
+                        case 'siteSetPackage':   // 处理分版位出价
+                        if (newValues[key]?.length > 0) {
+                            let newSiteSetPackage: SiteSetPackageDataProps[] = JSON.parse(JSON.stringify(newValues[key]))
+                            if (!newValues?.bidAmountAdjustmentEnabled) {
+                                newSiteSetPackage = newSiteSetPackage.map(item => {
+                                    let { bidCoefficient, ...newItem } = item
+                                    return { ...newItem }
+                                })
+                            }
+                            if (!newValues?.bidAdjustmentEnabled) {
+                                newSiteSetPackage = newSiteSetPackage.map(item => {
+                                    let { deepBidCoefficient, ...newItem } = item
+                                    return { ...newItem }
+                                })
+                            }
+                            newValues.bidAdjustment = {
+                                siteSetPackage: newSiteSetPackage
+                            }
+                            delete newValues.siteSetPackage
+                        }
+                        break
+                    case 'deepConversionType': // 处理深度优化
+                        let deepConversionSpec: any = {
+                            deepConversionType: newValues[key]
+                        }
+                        if (newValues[key] === 'DEEP_CONVERSION_WORTH') { // 优化 ROI
+                            deepConversionSpec.deepConversionWorthSpec = {
+                                goal: newValues.goal,
+                                expectedroi: newValues.deepBidAmount
+                            }
+                        } else if (newValues[key] === 'DEEP_CONVERSION_BEHAVIOR') { // 优化转化行为
+                            deepConversionSpec.deepConversionBehaviorSpec = {
+                                goal: newValues.goal,
+                                bidAmount: newValues.deepBidAmount * 100
+                            }
+                        }
+                        newValues.deepConversionSpec = deepConversionSpec
+                        delete newValues.deepBidAmount
+                        delete newValues.goal
+                        delete newValues.optimizationMode
+                        delete newValues.deepConversionType
+                        delete newValues.depthConversionEnabled
+                        break
                 }
             })
             if (newValues.sceneSpec.wechatPosition?.length === 0) {
@@ -89,6 +165,8 @@ function LeadAdModal(props: Props) {
             }
             delete newValues.officialAccountMediaCategory
             delete newValues.miniProgramAndMiniGame
+            delete newValues?.bidAmountAdjustmentEnabled
+            delete newValues?.bidAdjustmentEnabled
             delete newValues.payScene
             delete newValues.wechatPositionType
             delete newValues.wechatPosition
@@ -118,7 +196,7 @@ function LeadAdModal(props: Props) {
     // 数据回填
     useEffect(() => {
         if (dataInfo) {
-            form.setFieldsValue({
+            let formData: any = {
                 adgroupName: dataInfo?.adgroupName,//广告名称
                 promotedObjectType: dataInfo?.promotedObjectType,//推广目标
                 siteSet: dataInfo?.siteSet,//广告版位
@@ -140,7 +218,49 @@ function LeadAdModal(props: Props) {
                 payScene: dataInfo?.sceneSpec?.wechatScene?.payScene,//订单详情页消费场景
                 firstDayBeginTime: dataInfo?.firstDayBeginTime ? moment(`${dataInfo?.beginDate} ${dataInfo?.firstDayBeginTime}`) : undefined,//首日开始时间
                 configuredStatus: dataInfo?.configuredStatus || 'AD_STATUS_SUSPEND',//广告启停
+            }
+            Object.keys(dataInfo).forEach(key => {
+                switch (key) {
+                    case 'bidAdjustment': // 处理分版位出价
+                        if (dataInfo[key]?.siteSetPackage && dataInfo[key]?.siteSetPackage?.length > 0) {
+                            let siteSetPackage: SiteSetPackageDataProps[] = dataInfo[key].siteSetPackage
+                            if (siteSetPackage.some(item => item.bidCoefficient)) { // 判断出价是否开启了分版位
+                                formData.bidAmountAdjustmentEnabled = true
+                            } else {
+                                siteSetPackage = siteSetPackage.map(item => ({ ...item, bidCoefficient: 1 }))
+                            }
+                            if (siteSetPackage.some(item => item.deepBidCoefficient)) { // 判断深度出价是否开启了分版位
+                                formData.bidAdjustmentEnabled = true
+                            } else {
+                                siteSetPackage = siteSetPackage.map(item => ({ ...item, deepBidCoefficient: 1 }))
+                            }
+                            formData.siteSetPackage = siteSetPackage
+                        }
+                        break
+                    case 'deepConversionSpec': // 处理深度优化
+                        if (dataInfo[key]?.deepConversionType === 'DEEP_CONVERSION_WORTH') {
+                            formData = {
+                                ...formData,
+                                optimizationMode: 'DEEP_CONVERSION_TARGET',
+                                depthConversionEnabled: true,
+                                deepConversionType: dataInfo[key]?.deepConversionType,
+                                goal: dataInfo[key]?.deepConversionWorthSpec?.goal,
+                                deepBidAmount: dataInfo[key]?.deepConversionWorthSpec?.expectedroi,
+                            }
+                        } else if (dataInfo[key]?.deepConversionType === 'DEEP_CONVERSION_BEHAVIOR') {
+                            formData = {
+                                ...formData,
+                                optimizationMode: 'DEEP_CONVERSION_TARGET',
+                                depthConversionEnabled: true,
+                                deepConversionType: dataInfo[key]?.deepConversionType,
+                                goal: dataInfo[key]?.deepConversionBehaviorSpec?.goal,
+                                deepBidAmount: dataInfo[key]?.deepConversionBehaviorSpec?.bidAmount / 100,
+                            }
+                        }
+                        break
+                }
             })
+            form.setFieldsValue(formData)
             if (dataInfo?.firstDayBeginTime) {//存在首日开始时间,选中开关
                 setState({ ...state, isShowTime: ['1'] })
             }
@@ -171,6 +291,71 @@ function LeadAdModal(props: Props) {
             })
         }
     }, [bidMode, siteSet])
+
+    // 处理深度转化优化
+    useEffect(() => {
+        if (optimizationGoal && ajax?.data) {
+            let { deepBehaviorOptimizationGoalPermissionList, deepWorthOptimizationGoalPermissionList } = ajax?.data
+            // deepBehaviorOptimizationGoalPermissionList 优化转化行为
+            // deepWorthOptimizationGoalPermissionList 优化ROI
+            let behavior = deepBehaviorOptimizationGoalPermissionList?.find((item: { optimizationGoal: string }) => item.optimizationGoal === optimizationGoal)
+            let worth = deepWorthOptimizationGoalPermissionList?.find((item: { optimizationGoal: string }) => item.optimizationGoal === optimizationGoal)
+            setBehaviorList(behavior?.deepBehaviorOptimizationGoalList || [])
+            setWorthList(worth?.deepWorthOptimizationGoalList || [])
+        }
+    }, [optimizationGoal, ajax?.data])
+
+    /**处理分版位数据 */
+    const setSiteSetHandle = (siteSet: string[]) => {
+        let data: SiteSetPackageDataProps[] = []
+        if (siteSet && siteSet?.length > 0) {
+            let data1: string[] = []
+            let data2: string[] = []
+            let data3: string[] = []
+            let data4: string[] = []
+            siteSet.forEach((item: string) => {
+                let siteData = siteSetData?.find(item1 => item1.siteSet === item)
+                if (siteData) {
+                    switch (siteData.type) {
+                        case 1:
+                            data1.push(item)
+                            break
+                        case 2:
+                            data2.push(item)
+                            break
+                        case 3:
+                            data3.push(item)
+                            break
+                        case 4:
+                            data4.push(item)
+                            break
+                    }
+                }
+            });
+            let newSiteSetPackageData: SiteSetPackageDataProps[] = []
+            if (data1?.length > 0) {
+                newSiteSetPackageData.push({ siteSet: data1, bidCoefficient: 1, deepBidCoefficient: 1 })
+            }
+            if (data2?.length > 0) {
+                newSiteSetPackageData.push({ siteSet: data2, bidCoefficient: 1, deepBidCoefficient: 1 })
+            }
+            if (data3?.length > 0) {
+                newSiteSetPackageData.push({ siteSet: data3, bidCoefficient: 1, deepBidCoefficient: 1 })
+            }
+            if (data4?.length > 0) {
+                newSiteSetPackageData.push({ siteSet: data4, bidCoefficient: 1, deepBidCoefficient: 1 })
+            }
+            data = newSiteSetPackageData
+        } else {
+            data = []
+        }
+        form.setFieldsValue({
+            siteSetPackage: data
+        })
+    }
+
+    console.log('siteSetPackage--->', siteSetPackage, bidAdjustmentEnabled, siteSet);
+
     return <Modal
         visible={visible}
         title={type === 'add' ? '新建广告' : type === 'look' ? '广告详情' : '编辑广告'}
@@ -206,7 +391,8 @@ function LeadAdModal(props: Props) {
                     wechatSceneType: '0',
                     wechatPositionType: '0',
                     // optimizationGoal: 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER',
-                    configuredStatus: 'AD_STATUS_SUSPEND'
+                    configuredStatus: 'AD_STATUS_SUSPEND',
+                    siteSetPackage: [{ siteSet: ["SITE_SET_MOMENTS"], bidCoefficient: 1, deepBidCoefficient: 1 }, { siteSet: ["SITE_SET_WECHAT"], bidCoefficient: 1, deepBidCoefficient: 1 }]
                 }
             }
         >
@@ -223,7 +409,7 @@ function LeadAdModal(props: Props) {
                     </Radio.Group>
                 </Form.Item>
                 {!automaticSiteEnabled && <Form.Item name='siteSet' noStyle rules={[{ required: true, message: '请输入选择广告版位!' }]}>
-                    <Checkbox.Group style={{ width: '100%' }} onChange={() => { bidModeChange({ bidMode: 'BID_MODE_OCPM' }) }}>
+                    <Checkbox.Group style={{ width: '100%' }} onChange={(e) => { bidModeChange({ bidMode: 'BID_MODE_OCPM' }); setSiteSetHandle(e as string[]) }}>
                         <Row>
                             <Col span={4}>
                                 <Checkbox value="SITE_SET_MOMENTS">微信朋友圈</Checkbox>
@@ -238,7 +424,7 @@ function LeadAdModal(props: Props) {
                                 <Checkbox value="SITE_SET_TENCENT_VIDEO">腾讯视频</Checkbox>
                             </Col>
                             <Col span={4}>
-                                <Checkbox value="SITE_SET_KANDIAN">腾讯看点</Checkbox>
+                                <Checkbox value="SITE_SET_KANDIAN">QQ 浏览器</Checkbox>
                             </Col>
                             <Col span={6}>
                                 <Checkbox value="SITE_SET_QQ_MUSIC_GAME">QQ、腾讯音乐及游戏</Checkbox>
@@ -358,15 +544,28 @@ function LeadAdModal(props: Props) {
                 </>
             }
             {/* 出价类型为手动出价才展示 */}
-            {
-                smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC' && <>
-                    <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
-                        <Input placeholder={`输入价格 元/${bidMode === 'BID_MODE_CPM' ? '千次曝光' : bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[optimizationGoal]}`} style={{ width: 300 }} />
-                    </Form.Item>
+            {smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC' && <>
+                <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
+                    <Input placeholder={`输入价格 元/${bidMode === 'BID_MODE_CPM' ? '千次曝光' : bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[optimizationGoal]}`} style={{ width: 300 }} />
+                </Form.Item>
+                {(bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') && <>
                     {/* 当版位选择大于1时才出现 */}
-                    {/* {siteSet?.length > 1 &&<Form.Item label={<strong>分版位出价</strong>} name='bidAdjustment'>
+                    {siteSet?.length > 1 && <Form.Item label={<strong>分版位出价</strong>} name='bidAmountAdjustmentEnabled' valuePropName="checked">
                         <Switch checkedChildren="开启" unCheckedChildren="关闭" />
-                    </Form.Item>} */}
+                    </Form.Item>}
+                    {bidAmountAdjustmentEnabled && <Form.Item
+                        label={<strong>分版位出价</strong>}
+                        name='siteSetPackage'
+                        rules={[{ required: bidAmountAdjustmentEnabled ? true : false, message: '请设置系数' }]}
+                    >
+                        <BidAdjustment bidAmount={bidAmount} deepConversionType='BID_MODE' goal={goal}>
+                            <AdPositionList value={siteSetPackage} onChange={(data) => {
+                                form.setFieldsValue({
+                                    siteSetPackage: data
+                                })
+                            }} />
+                        </BidAdjustment>
+                    </Form.Item>}
                     <Form.Item label={<strong>一键起量</strong>} name='autoAcquisitionEnabled' valuePropName="checked">
                         <Switch checkedChildren="开启" unCheckedChildren="关闭" />
                     </Form.Item>
@@ -374,8 +573,70 @@ function LeadAdModal(props: Props) {
                     {autoAcquisitionEnabled && <Form.Item label={<strong>起量预算</strong>} name='autoAcquisitionBudget' rules={[{ required: true, message: '请输入起量预算' }]}>
                         <Input placeholder='起量预算' style={{ width: 300 }} />
                     </Form.Item>}
-                </>
-            }
+
+                    {/* 深度优化 */}
+                    {(behaviorList?.length > 0 || worthList?.length > 0) && <Form.Item label={<strong>深度转化优化</strong>} name='depthConversionEnabled' valuePropName="checked">
+                        <Switch checkedChildren="开启" unCheckedChildren="关闭" onChange={(e) => {
+                            if (e) {
+                                form.setFieldsValue({
+                                    optimizationMode: 'DEEP_CONVERSION_TARGET',
+                                    deepConversionType: behaviorList?.length > 0 ? 'DEEP_CONVERSION_BEHAVIOR' : worthList?.length > 0 ? 'DEEP_CONVERSION_WORTH' : ''
+                                })
+                            }
+                        }} />
+                    </Form.Item>}
+                    {depthConversionEnabled && <div style={{ backgroundColor: 'rgb(247, 248, 250)', padding: '18px 0 3px', marginBottom: 10, borderRadius: 8 }}>
+                        <Form.Item label={<strong>深度优化方式</strong>} name='optimizationMode' rules={[{ required: true, message: '请选择深度优化方式' }]}>
+                            <Radio.Group>
+                                <Radio.Button value="DEEP_CONVERSION_TARGET">深度目标优化</Radio.Button>
+                            </Radio.Group>
+                        </Form.Item>
+                        <Form.Item label={<strong>深度优化类型</strong>} name='deepConversionType' rules={[{ required: true, message: '请选择深度优化类型' }]}>
+                            <Radio.Group onChange={() => {
+                                form.setFieldsValue({
+                                    goal: undefined,
+                                    deepBidAmount: undefined
+                                })
+                            }}>
+                                {behaviorList?.length > 0 && <Radio.Button value="DEEP_CONVERSION_BEHAVIOR">优化转化行为</Radio.Button>}
+                                {worthList?.length > 0 && <Radio.Button value="DEEP_CONVERSION_WORTH">优化 ROI</Radio.Button>}
+                            </Radio.Group>
+                        </Form.Item>
+                        <Form.Item label={<strong>深度优化目标</strong>} name='goal' rules={[{ required: true, message: '请选择深度优化目标' }]}>
+                            <Select style={{ width: 380 }} placeholder='请选择'>
+                                {deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? Object.keys(OptimizationGoalEnum).filter(key => behaviorList?.includes(key)).map(key => <Select.Option value={key} key={key}>{OptimizationGoalEnum[key]}</Select.Option>) : deepConversionType === 'DEEP_CONVERSION_WORTH' ?
+                                    Object.keys(GoalRoasEnum).filter(key => worthList?.includes(key)).map(key => <Select.Option value={key} key={key}>{GoalRoasEnum[key]}</Select.Option>) : null}
+                            </Select>
+                        </Form.Item>
+                        {deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? <>
+                            <Form.Item label={<strong>深度目标出价</strong>} name='deepBidAmount' rules={[{ required: true, message: '请输入深度目标出价' }]}>
+                                <Input style={{ width: 380 }} suffix={`元/${OptimizationGoalEnum[goal] || '优化目标'}`} placeholder={`请输入深度目标出价,范围0.1~10000`} />
+                            </Form.Item>
+                        </> :
+                            deepConversionType === 'DEEP_CONVERSION_WORTH' ? <>
+                                <Form.Item label={<strong>期望ROI</strong>} name='deepBidAmount' rules={[{ required: true, message: '请输入期望ROI' }]}>
+                                    <Input style={{ width: 380 }} placeholder={`期望ROI目标范围0.001~1000,输入0.05,表示ROI目标为5%`} />
+                                </Form.Item>
+                            </> : null}
+                        {siteSet?.length > 1 && <Form.Item label={<strong>开启分版位深度目标出价</strong>} name='bidAdjustmentEnabled' valuePropName="checked">
+                            <Switch checkedChildren="开启" unCheckedChildren="关闭" />
+                        </Form.Item>}
+                        {bidAdjustmentEnabled && <Form.Item
+                            label={<strong>分版位深度目标出价</strong>}
+                            name='siteSetPackage'
+                            rules={[{ required: bidAdjustmentEnabled ? true : false, message: '请设置系数' }]}
+                        >
+                            <BidAdjustment bidAmount={deepBidAmount} deepConversionType={deepConversionType} goal={goal}>
+                                {!(bidAmountAdjustmentEnabled) ? <AdPositionList value={siteSetPackage} onChange={(data) => {
+                                    form.setFieldsValue({
+                                        siteSetPackage: data
+                                    })
+                                }} /> : <></>}
+                            </BidAdjustment>
+                        </Form.Item>}
+                    </div>}
+                </>}
+            </>}
             <Form.Item label={<strong>广告日预算</strong>} name='dailyBudget'>
                 <Input placeholder='不填默认为不限' style={{ width: 300 }} />
             </Form.Item>

+ 229 - 21
src/pages/launchSystemNew/launchManage/createAd/ad/modal/wechat.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback, useEffect, useState } from 'react'
 import { Modal, Form, Input, Divider, Select, Radio, DatePicker, Switch, Checkbox, message, Tooltip, Row, Col, Space, TimePicker, Button } from 'antd'
-import { BidModeEnum, OptimizationGoalEnum, BidStrategyEnum, AdStatus } from '@/services/launchAdq/enum'
+import { BidModeEnum, OptimizationGoalEnum, BidStrategyEnum, AdStatus, GoalRoasEnum } from '@/services/launchAdq/enum'
 import { ModalConfig } from '../index'
 import moment from 'moment';
 import { useAjax } from '@/Hook/useAjax';
@@ -8,6 +8,9 @@ import { getSceneTagsList } from '@/services/launchAdq/global';
 import { ExclamationCircleOutlined } from '@ant-design/icons';
 import { CreateAdProps } from '@/services/launchAdq/createAd';
 import { createSysAdgroups } from '@/services/launchAdq/localAd';
+import AdPositionList from './adPositionList';
+import { SiteSetPackageDataProps } from './leadAd';
+import BidAdjustment from './bidAdjustment';
 const { RangePicker }: { RangePicker: any } = DatePicker;
 let DatePickers: any = DatePicker
 interface Props {
@@ -22,11 +25,11 @@ interface Props {
 }
 /**微信公众号广告弹窗*/
 function WeChatAdModal(props: Props) {
+
+    /*******************************/
     let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm, ajax } = props
     const createSysAdgroup = useAjax((params) => createSysAdgroups(params))
-    let [state, setState] = useState<any>({
-        isShowTime: []
-    })
+    let [state, setState] = useState<any>({ isShowTime: [] })
     let [template_checked, settemplate_checked] = useState<boolean>(dataInfo?.isTemplate || false)
     let arg = type === 'look' ? { footer: null } : {}
     const sceneTagsList = useAjax((params) => getSceneTagsList(params))
@@ -40,6 +43,18 @@ function WeChatAdModal(props: Props) {
     let wechatSceneType = Form.useWatch('wechatSceneType', form)
     let automaticSiteEnabled = Form.useWatch('automaticSiteEnabled', form)
     let optimizationGoal = Form.useWatch('optimizationGoal', form)
+    let depthConversionEnabled = Form.useWatch('depthConversionEnabled', form)
+    let deepConversionType = Form.useWatch('deepConversionType', form)
+    let goal = Form.useWatch('goal', form)
+    let bidAdjustmentEnabled = Form.useWatch('bidAdjustmentEnabled', form)
+    let siteSetPackage = Form.useWatch('siteSetPackage', form)
+    let deepBidAmount = Form.useWatch('deepBidAmount', form)
+    let bidAmountAdjustmentEnabled = Form.useWatch('bidAmountAdjustmentEnabled', form)
+    let bidAmount = Form.useWatch('bidAmount', form)
+
+    const [behaviorList, setBehaviorList] = useState<string[]>([])
+    const [worthList, setWorthList] = useState<string[]>([])
+    /*******************************/
 
     // 确定事件
     const handleOk = useCallback(() => {
@@ -72,8 +87,50 @@ function WeChatAdModal(props: Props) {
                                 }
                             }
                         }
-
                         break;
+                    case 'siteSetPackage':   // 处理分版位出价
+                        if (newValues[key]?.length > 0) {
+                            let newSiteSetPackage: SiteSetPackageDataProps[] = JSON.parse(JSON.stringify(newValues[key]))
+                            if (!newValues?.bidAmountAdjustmentEnabled) {
+                                newSiteSetPackage = newSiteSetPackage.map(item => {
+                                    let { bidCoefficient, ...newItem } = item
+                                    return { ...newItem }
+                                })
+                            }
+                            if (!newValues?.bidAdjustmentEnabled) {
+                                newSiteSetPackage = newSiteSetPackage.map(item => {
+                                    let { deepBidCoefficient, ...newItem } = item
+                                    return { ...newItem }
+                                })
+                            }
+                            newValues.bidAdjustment = {
+                                siteSetPackage: newSiteSetPackage
+                            }
+                            delete newValues.siteSetPackage
+                        }
+                        break
+                    case 'deepConversionType': // 处理深度优化
+                        let deepConversionSpec: any = {
+                            deepConversionType: newValues[key]
+                        }
+                        if (newValues[key] === 'DEEP_CONVERSION_WORTH') { // 优化 ROI
+                            deepConversionSpec.deepConversionWorthSpec = {
+                                goal: newValues.goal,
+                                expectedroi: newValues.deepBidAmount
+                            }
+                        } else if (newValues[key] === 'DEEP_CONVERSION_BEHAVIOR') { // 优化转化行为
+                            deepConversionSpec.deepConversionBehaviorSpec = {
+                                goal: newValues.goal,
+                                bidAmount: newValues.deepBidAmount * 100
+                            }
+                        }
+                        newValues.deepConversionSpec = deepConversionSpec
+                        delete newValues.deepBidAmount
+                        delete newValues.goal
+                        delete newValues.optimizationMode
+                        delete newValues.deepConversionType
+                        delete newValues.depthConversionEnabled
+                        break
                 }
             })
             if (newValues.sceneSpec.wechatPosition?.length === 0) {
@@ -89,6 +146,8 @@ function WeChatAdModal(props: Props) {
             }
             delete newValues.officialAccountMediaCategory
             delete newValues.miniProgramAndMiniGame
+            delete newValues?.bidAmountAdjustmentEnabled
+            delete newValues?.bidAdjustmentEnabled
             delete newValues.payScene
             delete newValues.wechatPositionType
             delete newValues.wechatPosition
@@ -118,7 +177,7 @@ function WeChatAdModal(props: Props) {
     // 数据回填
     useEffect(() => {
         if (dataInfo) {
-            form.setFieldsValue({
+            let formData: any = {
                 adgroupName: dataInfo?.adgroupName,//广告名称
                 promotedObjectType: dataInfo?.promotedObjectType,//推广目标
                 siteSet: dataInfo?.siteSet,//广告版位
@@ -140,7 +199,50 @@ function WeChatAdModal(props: Props) {
                 payScene: dataInfo?.sceneSpec?.wechatScene?.payScene,//订单详情页消费场景
                 firstDayBeginTime: dataInfo?.firstDayBeginTime ? moment(`${dataInfo?.beginDate} ${dataInfo?.firstDayBeginTime}`) : undefined,//首日开始时间
                 configuredStatus: dataInfo?.configuredStatus || 'AD_STATUS_SUSPEND',//广告启停
+            }
+            Object.keys(dataInfo).forEach(key => {
+                switch (key) {
+                    case 'bidAdjustment': // 处理分版位出价
+                        if (dataInfo[key]?.siteSetPackage && dataInfo[key]?.siteSetPackage?.length > 0) {
+                            let siteSetPackage: SiteSetPackageDataProps[] = dataInfo[key].siteSetPackage
+                            if (siteSetPackage.some(item => item.bidCoefficient)) { // 判断出价是否开启了分版位
+                                formData.bidAmountAdjustmentEnabled = true
+                            } else {
+                                siteSetPackage = siteSetPackage.map(item => ({ ...item, bidCoefficient: 1 }))
+                            }
+                            if (siteSetPackage.some(item => item.deepBidCoefficient)) { // 判断深度出价是否开启了分版位
+                                formData.bidAdjustmentEnabled = true
+                            } else {
+                                siteSetPackage = siteSetPackage.map(item => ({ ...item, deepBidCoefficient: 1 }))
+                            }
+                            formData.siteSetPackage = siteSetPackage
+                        }
+                        break
+                    case 'deepConversionSpec': // 处理深度优化
+                        if (dataInfo[key]?.deepConversionType === 'DEEP_CONVERSION_WORTH') {
+                            formData = {
+                                ...formData,
+                                optimizationMode: 'DEEP_CONVERSION_TARGET',
+                                depthConversionEnabled: true,
+                                deepConversionType: dataInfo[key]?.deepConversionType,
+                                goal: dataInfo[key]?.deepConversionWorthSpec?.goal,
+                                deepBidAmount: dataInfo[key]?.deepConversionWorthSpec?.expectedroi,
+                            }
+                        } else if (dataInfo[key]?.deepConversionType === 'DEEP_CONVERSION_BEHAVIOR') {
+                            formData = {
+                                ...formData,
+                                optimizationMode: 'DEEP_CONVERSION_TARGET',
+                                depthConversionEnabled: true,
+                                deepConversionType: dataInfo[key]?.deepConversionType,
+                                goal: dataInfo[key]?.deepConversionBehaviorSpec?.goal,
+                                deepBidAmount: dataInfo[key]?.deepConversionBehaviorSpec?.bidAmount / 100,
+                            }
+                        }
+                        break
+                }
             })
+
+            form.setFieldsValue(formData)
             if (dataInfo?.firstDayBeginTime) {//存在首日开始时间,选中开关
                 setState({ ...state, isShowTime: ['1'] })
             }
@@ -162,15 +264,44 @@ function WeChatAdModal(props: Props) {
     // 出价和版位改变时查询
     useEffect(() => {
         if (bidMode && siteSet && siteSet?.length > 0) {
-            let obj:any = {siteSet,promotedObjectType: queryForm.promotedObjectType}
-            if(bidMode === 'BID_MODE_OCPC' || bidMode === 'BID_MODE_OCPM'){
-                obj = {...obj,bidMode}
+            let obj: any = { siteSet, promotedObjectType: queryForm.promotedObjectType }
+            if (bidMode === 'BID_MODE_OCPC' || bidMode === 'BID_MODE_OCPM') {
+                obj = { ...obj, bidMode }
             }
-            ajax.run(obj).then((res: any)=>{
+            ajax.run(obj).then((res: any) => {
                 console.log(res)
             })
         }
     }, [bidMode, siteSet])
+
+    // 处理深度转化优化
+    useEffect(() => {
+        if (optimizationGoal && ajax?.data) {
+            let { deepBehaviorOptimizationGoalPermissionList, deepWorthOptimizationGoalPermissionList } = ajax?.data
+            // deepBehaviorOptimizationGoalPermissionList 优化转化行为
+            // deepWorthOptimizationGoalPermissionList 优化ROI
+            let behavior = deepBehaviorOptimizationGoalPermissionList?.find((item: { optimizationGoal: string }) => item.optimizationGoal === optimizationGoal)
+            let worth = deepWorthOptimizationGoalPermissionList?.find((item: { optimizationGoal: string }) => item.optimizationGoal === optimizationGoal)
+            setBehaviorList(behavior?.deepBehaviorOptimizationGoalList || [])
+            setWorthList(worth?.deepWorthOptimizationGoalList || [])
+        }
+    }, [optimizationGoal, ajax?.data])
+
+    /**处理分版位数据 */
+    const setSiteSetHandle = (siteSet: string[]) => {
+        let data: SiteSetPackageDataProps[] = []
+        if (siteSet && siteSet?.length > 0) {
+            data = siteSet?.map((item: string) => ({ siteSet: [item], bidCoefficient: 1, deepBidCoefficient: 1 }))
+        } else {
+            data = []
+        }
+        form.setFieldsValue({
+            siteSetPackage: data
+        })
+    }
+
+    console.log('siteSetPackage--->', siteSetPackage, bidAdjustmentEnabled, siteSet);
+
     return <Modal
         visible={visible}
         title={type === 'add' ? '新建广告' : type === 'look' ? '广告详情' : '编辑广告'}
@@ -205,7 +336,9 @@ function WeChatAdModal(props: Props) {
                     wechatSceneType: '0',
                     wechatPositionType: '0',
                     // optimizationGoal: 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER',
-                    configuredStatus: 'AD_STATUS_SUSPEND'
+                    configuredStatus: 'AD_STATUS_SUSPEND',
+                    optimizationMode: 'DEEP_CONVERSION_TARGET',
+                    siteSetPackage: [{ siteSet: ["SITE_SET_MOMENTS"], bidCoefficient: 1, deepBidCoefficient: 1 }, { siteSet: ["SITE_SET_WECHAT"], bidCoefficient: 1, deepBidCoefficient: 1 }]
                 }
             }
         >
@@ -222,7 +355,7 @@ function WeChatAdModal(props: Props) {
                     </Radio.Group>
                 </Form.Item>
                 {!automaticSiteEnabled && <Form.Item name='siteSet' noStyle rules={[{ required: true, message: '请输入选择广告版位!' }]}>
-                    <Checkbox.Group style={{ width: '100%' }} onChange={() => { bidModeChange({ bidMode: 'BID_MODE_OCPM' }) }}>
+                    <Checkbox.Group style={{ width: '100%' }} onChange={(e) => { bidModeChange({ bidMode: 'BID_MODE_OCPM' }); setSiteSetHandle(e as string[]) }}>
                         <Row>
                             <Col span={5}>
                                 <Checkbox value="SITE_SET_MOMENTS">微信朋友圈</Checkbox>
@@ -338,15 +471,29 @@ function WeChatAdModal(props: Props) {
                 </>
             }
             {/* 出价类型为手动出价才展示 */}
-            {
-                smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC' && <>
-                    <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
-                        <Input placeholder={`输入价格 元/${bidMode === 'BID_MODE_CPM' ? '千次曝光' : bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[optimizationGoal]}`} style={{ width: 300 }} />
-                    </Form.Item>
+            {smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC' && <>
+                <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
+                    <Input placeholder={`输入价格 元/${bidMode === 'BID_MODE_CPM' ? '千次曝光' : bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[optimizationGoal]}`} style={{ width: 300 }} />
+                </Form.Item>
+
+                {bidMode === 'BID_MODE_OCPM' && <>
                     {/* 当版位选择大于1时才出现 */}
-                    {/* {siteSet?.length > 1 &&<Form.Item label={<strong>分版位出价</strong>} name='bidAdjustment'>
+                    {siteSet?.length > 1 && <Form.Item label={<strong>分版位出价</strong>} name='bidAmountAdjustmentEnabled' valuePropName="checked">
                         <Switch checkedChildren="开启" unCheckedChildren="关闭" />
-                    </Form.Item>} */}
+                    </Form.Item>}
+                    {bidAmountAdjustmentEnabled && <Form.Item
+                        label={<strong>分版位出价</strong>}
+                        name='siteSetPackage'
+                        rules={[{ required: bidAmountAdjustmentEnabled ? true : false, message: '请设置系数' }]}
+                    >
+                        <BidAdjustment bidAmount={bidAmount} deepConversionType='BID_MODE' goal={goal}>
+                            <AdPositionList value={siteSetPackage} onChange={(data) => {
+                                form.setFieldsValue({
+                                    siteSetPackage: data
+                                })
+                            }} />
+                        </BidAdjustment>
+                    </Form.Item>}
                     <Form.Item label={<strong>一键起量</strong>} name='autoAcquisitionEnabled' valuePropName="checked">
                         <Switch checkedChildren="开启" unCheckedChildren="关闭" />
                     </Form.Item>
@@ -354,8 +501,69 @@ function WeChatAdModal(props: Props) {
                     {autoAcquisitionEnabled && <Form.Item label={<strong>起量预算</strong>} name='autoAcquisitionBudget' rules={[{ required: true, message: '请输入起量预算' }]}>
                         <Input placeholder='起量预算' style={{ width: 300 }} />
                     </Form.Item>}
-                </>
-            }
+                    {/* 深度优化 */}
+                    {(behaviorList?.length > 0 || worthList?.length > 0) && <Form.Item label={<strong>深度转化优化</strong>} name='depthConversionEnabled' valuePropName="checked">
+                        <Switch checkedChildren="开启" unCheckedChildren="关闭" onChange={(e) => {
+                            if (e) {
+                                form.setFieldsValue({
+                                    optimizationMode: 'DEEP_CONVERSION_TARGET',
+                                    deepConversionType: behaviorList?.length > 0 ? 'DEEP_CONVERSION_BEHAVIOR' : worthList?.length > 0 ? 'DEEP_CONVERSION_WORTH' : ''
+                                })
+                            }
+                        }} />
+                    </Form.Item>}
+                    {depthConversionEnabled && <div style={{ backgroundColor: 'rgb(247, 248, 250)', padding: '18px 0 3px', marginBottom: 10, borderRadius: 8 }}>
+                        <Form.Item label={<strong>深度优化方式</strong>} name='optimizationMode' rules={[{ required: true, message: '请选择深度优化方式' }]}>
+                            <Radio.Group>
+                                <Radio.Button value="DEEP_CONVERSION_TARGET">深度目标优化</Radio.Button>
+                            </Radio.Group>
+                        </Form.Item>
+                        <Form.Item label={<strong>深度优化类型</strong>} name='deepConversionType' rules={[{ required: true, message: '请选择深度优化类型' }]}>
+                            <Radio.Group onChange={() => {
+                                form.setFieldsValue({
+                                    goal: undefined,
+                                    deepBidAmount: undefined
+                                })
+                            }}>
+                                {behaviorList?.length > 0 && <Radio.Button value="DEEP_CONVERSION_BEHAVIOR">优化转化行为</Radio.Button>}
+                                {worthList?.length > 0 && <Radio.Button value="DEEP_CONVERSION_WORTH">优化 ROI</Radio.Button>}
+                            </Radio.Group>
+                        </Form.Item>
+                        <Form.Item label={<strong>深度优化目标</strong>} name='goal' rules={[{ required: true, message: '请选择深度优化目标' }]}>
+                            <Select style={{ width: 380 }} placeholder='请选择'>
+                                {deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? Object.keys(OptimizationGoalEnum).filter(key => behaviorList?.includes(key)).map(key => <Select.Option value={key} key={key}>{OptimizationGoalEnum[key]}</Select.Option>) : deepConversionType === 'DEEP_CONVERSION_WORTH' ?
+                                    Object.keys(GoalRoasEnum).filter(key => worthList?.includes(key)).map(key => <Select.Option value={key} key={key}>{GoalRoasEnum[key]}</Select.Option>) : null}
+                            </Select>
+                        </Form.Item>
+                        {deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? <>
+                            <Form.Item label={<strong>深度目标出价</strong>} name='deepBidAmount' rules={[{ required: true, message: '请输入深度目标出价' }]}>
+                                <Input style={{ width: 380 }} suffix={`元/${OptimizationGoalEnum[goal] || '优化目标'}`} placeholder={`请输入深度目标出价,范围0.1~10000`} />
+                            </Form.Item>
+                        </> :
+                            deepConversionType === 'DEEP_CONVERSION_WORTH' ? <>
+                                <Form.Item label={<strong>期望ROI</strong>} name='deepBidAmount' rules={[{ required: true, message: '请输入期望ROI' }]}>
+                                    <Input style={{ width: 380 }} placeholder={`期望ROI目标范围0.001~1000,输入0.05,表示ROI目标为5%`} />
+                                </Form.Item>
+                            </> : null}
+                        {siteSet?.length > 1 && <Form.Item label={<strong>开启分版位深度目标出价</strong>} name='bidAdjustmentEnabled' valuePropName="checked">
+                            <Switch checkedChildren="开启" unCheckedChildren="关闭" />
+                        </Form.Item>}
+                        {bidAdjustmentEnabled && <Form.Item
+                            label={<strong>分版位深度目标出价</strong>}
+                            name='siteSetPackage'
+                            rules={[{ required: bidAdjustmentEnabled ? true : false, message: '请设置系数' }]}
+                        >
+                            <BidAdjustment bidAmount={deepBidAmount} deepConversionType={deepConversionType} goal={goal}>
+                                {!(bidAmountAdjustmentEnabled) ? <AdPositionList value={siteSetPackage} onChange={(data) => {
+                                    form.setFieldsValue({
+                                        siteSetPackage: data
+                                    })
+                                }} /> : <></>}
+                            </BidAdjustment>
+                        </Form.Item>}
+                    </div>}
+                </>}
+            </>}
             <Form.Item label={<strong>广告日预算</strong>} name='dailyBudget'>
                 <Input placeholder='不填默认为不限' style={{ width: 300 }} />
             </Form.Item>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
src/services/launchAdq/enum.ts


Vissa filer visades inte eftersom för många filer har ändrats