wjx 10 mesi fa
parent
commit
7ab100a120

+ 4 - 4
config/proxy.ts

@@ -10,23 +10,23 @@
  export default {
   dev: {
     '/api/': {
-      target: 'http://test.api.zanxiangwl.com',
+      target: 'https://testapi.zanxiangwl.com',
       // target: 'http://api.zanxiangwl.com',
       changeOrigin: true,
       pathRewrite: { '/api': '' },
     },
     '/wapi/': {
-      target: 'http://api.zanxiangnet.com',//服务器
+      target: 'https://api.zanxiangnet.com',//服务器
       changeOrigin: true,
       pathRewrite: { '^/wapi/': '' },
     },
     '/wxapi/':{//小程序
-      target: 'http://report.zanxiangwl.com',//服务器
+      target: 'https://report.zanxiangwl.com',//服务器
       changeOrigin: true,
       pathRewrite: { '^/wxapi/': '' },
     },
     '/dapi/': {
-      target: 'http://data.zanxiangnet.com',//数据平台
+      target: 'https://data.zanxiangnet.com',//数据平台
       changeOrigin: true,
       pathRewrite: { '^/dapi/': '' },
     },

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

@@ -23,7 +23,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
     const [selectedRows, setSelectedRows] = useState<any[]>([])
     const [update, setUpdate] = useState<{ visible: boolean }>({ visible: false })
     const [addDynamicVisible, setAddDynamicVisible] = useState<boolean>(false)
-    const [handleType, setHandleType] = useState<number>(1)
+    const [handleType, setHandleType] = useState<number>(2)
     const [czjlShow, setCzjlShow] = useState(false)
 
     const syncBatch = useAjax((params) => syncBatchApi(params))

+ 40 - 41
src/pages/launchSystemV3/components/TextAideInput/index.tsx

@@ -1,6 +1,6 @@
 import { useAjax } from "@/Hook/useAjax"
 import { txtLength } from "@/utils/utils";
-import { Input, List, Popover, Space } from "antd";
+import { Input, List, Popover } from "antd";
 import React, { useEffect } from "react"
 import { useState } from "react";
 import './index.less'
@@ -39,46 +39,45 @@ const TextAideInput: React.FC<Props> = (props) => {
         getTextLsit.run({ keyword, maxTextLength })
     }
 
-    return <div className="textAideInput">
-        <Space>
-            <Popover placement="topLeft" overlayClassName="textAideInputPopover" style={{ minWidth: 600 }} visible={descriptionShow} content={<>
-                {
-                    <List
-                        loading={getTextLsit?.loading}
-                        size="small"
-                        style={{ maxHeight: 300, overflowX: 'auto', minWidth: 600 }}
-                        dataSource={getTextLsit?.data?.returnTexts}
-                        renderItem={(item: any) => <List.Item onClick={(e: any) => {
-                            setText(item.text)
-                            onChange && onChange(item.text)
-                            setTimeout(() => { setDescriptionshow(false) }, 50)
-                        }}><span >{item.text}{item.tag && <span className="crt">{'CTR 高'}</span>}</span></List.Item>}
-                    />
-                }
-            </>}>
-                <Input
-                    placeholder={placeholder}
-                    style={style}
-                    value={text}
-                    onFocus={() => {
-                        setDescriptionshow(true)
-                        textList(value)
-                    }}
-                    onBlur={() => {
-                        setTimeout(() => { setDescriptionshow(false) }, 500)
-                    }}
-                    onChange={(e) => {
-                        let value = e.target.value
-                        setText(value)
-                        textList(value)
-                        onChange && onChange(value)
-                    }}
-                    allowClear
-                />
-            </Popover>
-
-            <span>{`${txtLength(text)}/${maxTextLength}`}</span>
-        </Space>
+    return <div style={{ display: 'inline-flex', alignItems: 'center', columnGap: 5 }}>
+        <Popover
+            placement="topLeft"
+            overlayClassName="textAideInputPopover"
+            style={{ minWidth: 600 }}
+            visible={descriptionShow}
+            content={<List
+                loading={getTextLsit?.loading}
+                size="small"
+                style={{ maxHeight: 300, overflowX: 'auto', minWidth: 600 }}
+                dataSource={getTextLsit?.data?.returnTexts}
+                renderItem={(item: any) => <List.Item onClick={() => {
+                    setText(item.text)
+                    onChange && onChange(item.text)
+                    setTimeout(() => { setDescriptionshow(false) }, 50)
+                }}><span >{item.text}{item.tag && <span className="crt">{'CTR 高'}</span>}</span></List.Item>}
+            />}
+        >
+            <Input
+                placeholder={placeholder}
+                style={style}
+                value={text}
+                onFocus={() => {
+                    setDescriptionshow(true)
+                    textList(value)
+                }}
+                onBlur={() => {
+                    setTimeout(() => { setDescriptionshow(false) }, 500)
+                }}
+                onChange={(e) => {
+                    let value = e.target.value
+                    setText(value)
+                    textList(value)
+                    onChange && onChange(value)
+                }}
+                allowClear
+            />
+        </Popover>
+        <span>{`${txtLength(text)}/${maxTextLength}`}</span>
     </div>
 }
 

+ 47 - 18
src/pages/launchSystemV3/components/WechatAccount/index.tsx

@@ -23,6 +23,7 @@ const WechatAccount: React.FC<Props> = (props) => {
     const [tableData, setTableData] = useState<any[]>([])//table数据
     const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
     const [data, setData] = useState<PULLIN.AccountCreateLogsProps[]>(data1)
+    const [loading, setLoading] = useState<boolean>(false)
 
     const getWechatOfficialAccount = useAjax((params) => getWechatOfficialAccountApi(params))
     const getWechatOfficialAccounts = useAjax((params) => getWechatOfficialAccountsApi(params))
@@ -70,35 +71,63 @@ const WechatAccount: React.FC<Props> = (props) => {
     }
 
     /** 一键设置 */
-    const setOnekey = () => {
-        let wechatChannelNames: string[] = data[selectAdz - 1]['wechatChannelList']?.map((item: { wechatOfficialAccountName: string }) => item.wechatOfficialAccountName) || []
+    const setOnekey = (isFirst?: boolean) => {
         let newData: PULLIN.AccountCreateLogsProps[] = JSON.parse(JSON.stringify(data))
         const hide = message.loading(`正在设置...`, 0, () => {
             message.success('设置成功');
         });
-        getWechatOfficialAccounts.run({ accountIdList: newData?.filter(item => item.accountId !== data[selectAdz - 1].accountId)?.map(item => item?.accountId) }).then(res => {
-            
-            if (res?.length > 0) {
-                res.forEach((i: { accountId: number, wechatOfficialAccountName: string }) => {
-                    if (wechatChannelNames.includes(i.wechatOfficialAccountName)) {
+        if (isFirst) {
+            setLoading(true)
+            let ajax = data.map(item => getWechatOfficialAccountApi({ accountId: item.accountId }))
+            Promise.all(ajax).then(res => {
+                if (res) {
+                    res.forEach(a => {
+                        let data = a?.data?.[0] || {}
                         newData = newData.map(item => {
-                            if (item.accountId.toString() === i.accountId.toString()) {
-                                return { ...item, wechatChannelList: [i] }
+                            if (item.accountId.toString() === data.accountId.toString()) {
+                                return { ...item, wechatChannelList: [data] }
                             }
                             return item
                         })
-                    }
-                })
-                setData(newData)
-            }
-            message.success('设置完成');
-            hide()
-        })
+                    })
+                    setData(newData)
+                }
+                message.success('设置完成');
+                setLoading(false)
+                hide()
+            }).catch(() => {
+                message.success('设置失败');
+                setLoading(false)
+                hide()
+            })
+        } else {
+            let wechatChannelNames: string[] = data[selectAdz - 1]['wechatChannelList']?.map((item: { wechatOfficialAccountName: string }) => item.wechatOfficialAccountName) || []
+            getWechatOfficialAccounts.run({ accountIdList: newData?.filter(item => item.accountId !== data[selectAdz - 1].accountId)?.map(item => item?.accountId) }).then(res => {
+                if (res?.length > 0) {
+                    res.forEach((i: { accountId: number, wechatOfficialAccountName: string }) => {
+                        if (wechatChannelNames.includes(i.wechatOfficialAccountName)) {
+                            newData = newData.map(item => {
+                                if (item.accountId.toString() === i.accountId.toString()) {
+                                    return { ...item, wechatChannelList: [i] }
+                                }
+                                return item
+                            })
+                        }
+                    })
+                    setData(newData)
+                }
+                message.success('设置完成');
+                hide()
+            })
+        }
     }
 
 
     return <Modal
-        title={<strong>选择公众号</strong>}
+        title={<Space>
+            <strong>选择公众号</strong>
+            <Button type="link" danger onClick={() => setOnekey(true)} loading={loading}>一键设置第一个公众号给账户</Button>
+        </Space>}
         visible={visible}
         onCancel={() => { onClose && onClose() }}
         onOk={handleOk}
@@ -122,7 +151,7 @@ const WechatAccount: React.FC<Props> = (props) => {
             <div className={style.right}>
                 <Space style={{ marginBottom: 10 }} align="end" size={0}>
                     <Button icon={<SyncOutlined />} type='link' loading={getWechatOfficialAccount?.loading} onClick={() => { getList(data[selectAdz - 1].accountId) }}>刷新</Button>
-                    {data?.length > 1 && <Button disabled={!data[selectAdz - 1]['wechatChannelList']?.length} onClick={setOnekey} type="link" loading={getWechatOfficialAccount.loading}>
+                    {data?.length > 1 && <Button disabled={!data[selectAdz - 1]['wechatChannelList']?.length} onClick={() => setOnekey()} type="link" loading={getWechatOfficialAccount.loading}>
                         <Space>
                             <span>一键设置</span>
                             <Tooltip color="#FFF" overlayInnerStyle={{ color: '#000' }} title="设置其它账号有相同名称的商品为那个账号的商品(注意需要用户商品称相同,否则不设置)">

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

@@ -97,7 +97,7 @@ const Ad: React.FC = () => {
                     setAddelivery({ ...addelivery, adgroups })
                 } else {
                     setAccountCreateLogs(accountCreateLogs.map(item => ({ accountId: item.accountId })))
-                    setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {} })
+                    setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
                 }
                 setNewVisible(false)
                 clearData()

+ 19 - 4
src/pages/launchSystemV3/tencentAdPutIn/create/Material/addMaterial.tsx

@@ -7,8 +7,10 @@ import { useModel } from "umi"
 import SelectCloud from "@/pages/launchSystemNew/components/selectCloud"
 import { getVideoImgUrl } from "@/utils/utils"
 import VideoFrameSelect from "@/pages/launchSystemV3/components/VideoFrameSelect"
+import New1Radio from "@/pages/launchSystemV3/components/New1Radio"
 
 interface Props {
+    adLength: number,
     creativeTemplateId: number
     materialData: any
     deliveryMode: string,
@@ -18,7 +20,7 @@ interface Props {
     onChange?: (value: any) => void
 }
 
-const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, deliveryMode, visible, value, onChange, onClose }) => {
+const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, deliveryMode, visible, value, onChange, onClose, adLength }) => {
 
     /*********************************/
     const { init } = useModel('useLaunchAdq.useBdMediaPup')
@@ -99,14 +101,22 @@ const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, delive
     }
 
     const handleOk = (values: any) => {
-        console.log(values)
-        onChange?.(values)
+        const { mediaType, dynamicGroup } = values
+        if (mediaType === 1 && dynamicGroup.length < adLength) {
+            message.error({
+                content: `创意组分配规则选择创意组平均分配到广告时,创意组总数必须大于等于广告总数。当前广告总数:${adLength},创意组总数:${dynamicGroup.length}`,
+                duration: 8
+            })
+            return
+        }
+        onChange?.({ mediaType, dynamicMaterialDTos: { dynamicGroup } })
     }
 
     useEffect(() => {
         console.log('value--->', value)
         if (value) {
-            form.setFieldsValue({ ...value })
+            const { dynamicMaterialDTos, mediaType } = value
+            form.setFieldsValue({ ...dynamicMaterialDTos, mediaType })
         }
     }, [value])
 
@@ -175,6 +185,11 @@ const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, delive
                 dynamicGroup: [undefined]
             }}
         >
+            <Card className="cardResetCss" style={{ marginBottom: 10 }}>
+                <Form.Item name="mediaType" label={<strong>创意组分配规则</strong>} style={{ marginBottom: 0 }} rules={[{ required: true, message: '请选择营销目的!' }]}>
+                    <New1Radio data={[{ label: '全账号复用', value: 0 }, { label: '创意组平均分配到广告', value: 1 }]} />
+                </Form.Item>
+            </Card>
             <Form.List name="dynamicGroup">
                 {(fields, { add, remove }) => (<>
                     <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10 }}>

+ 53 - 29
src/pages/launchSystemV3/tencentAdPutIn/create/Material/index.tsx

@@ -7,12 +7,13 @@ import AddMaterial from "./addMaterial";
 import VideoNews from "@/pages/launchSystemNew/components/newsModal/videoNews";
 const { Title } = Typography;
 
-const Material: React.FC = () => {
+const Material: React.FC<{ adData?: any[] }> = ({ adData }) => {
 
     /***************************************/
-    const { materialData, addelivery, setAddelivery, clearData } = useContext(DispatchAddelivery)!;
-    const { dynamic, dynamicMaterialDTos } = addelivery
+    const { materialData, addelivery, setAddelivery, clearData, accountCreateLogs } = useContext(DispatchAddelivery)!;
+    const { dynamic, dynamicMaterialDTos, mediaType, targeting, adgroups: { marketingAssetOuterSpec } } = addelivery
     const { creativeTemplateId, deliveryMode } = dynamic
+    const [adLength, setAdLength] = useState<number>(0)
 
     const [newVisible, setNewVisible] = useState<boolean>(false)
     const [mType, setMtype] = useState<string>()
@@ -25,6 +26,25 @@ const Material: React.FC = () => {
         }
     }, [materialData])
 
+    // 获取广告总数
+    useEffect(() => {
+        if (adData && adData?.length > 0) {
+            setAdLength(adData.length)
+        } else if (accountCreateLogs?.length > 0 && marketingAssetOuterSpec?.marketingTargetType) {
+            let adLength = 0
+            accountCreateLogs.forEach(item => {
+                let productList: any[] = []
+                if (['MARKETING_TARGET_TYPE_FICTION'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说
+                    productList = item?.productList || []
+                } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
+                    productList = item?.wechatChannelList || []
+                }
+                adLength += productList.length * targeting.length
+            })
+            setAdLength(adLength)
+        }
+    }, [accountCreateLogs, marketingAssetOuterSpec, adData])
+
     return <div className={`${style.settingsBody_content_row} ${style.row4}`}>
         <div className={style.title}>
             <span>创意素材 <span className={style.selected}>已选 {dynamicMaterialDTos?.dynamicGroup?.length || 0}</span></span>
@@ -33,29 +53,32 @@ const Material: React.FC = () => {
         <div className={style.detail}>
             <div className={style.detail_body}>
                 {(dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length > 0) ?
-                    <div className={style.detail_body_m}>
-                        {dynamicMaterialDTos.dynamicGroup.map((item: any, index: number) => {
-                            return <div key={index} style={{ width: deliveryMode === 'DELIVERY_MODE_CUSTOMIZE' ? [641, 642, 643].includes(creativeTemplateId) ? '100%' : [720, 721, 722, 1529, 618].includes(creativeTemplateId) ? '50%' : '25%' : '100%' }}>
-                                <Title level={5} style={{ fontSize: 12 }}>创意组{index + 1}</Title>
-                                {mType ? ['short_video', 'video'].includes(mType) ? <div className={style.video}>
-                                    <VideoNews src={item?.video_id?.url || item?.short_video1?.url} />
-                                    {item?.cover_id && <div className={style.cover_image}>
-                                        <img src={item?.cover_id?.url} />
-                                    </div>}
-                                </div> : ['image_list'].includes(mType) ? <div className={style.imageList}>
-                                    {item?.image_list?.map((item: { url: string | undefined; }, index: undefined) => <div className={style.cover_image} key={index}>
-                                        <img src={item?.url} />
-                                    </div>)}
-                                </div> : ['element_story'].includes(mType) ? <div className={style.imageList}>
-                                    {item?.element_story?.map((item: { url: string | undefined; }, index: undefined) => <div className={style.cover_image} key={index}>
-                                        <img src={item?.url} />
-                                    </div>)}
-                                </div> : ['image'].includes(mType) ? <div className={style.cover_image}>
-                                    <img src={item?.image_id?.url} />
-                                </div> : null : null}
-                            </div>
-                        })}
-                    </div> :
+                    <>
+                        <Title level={5} style={{ fontSize: 12 }}>{mediaType === 0 ? '全账号复用' : mediaType === 1 ? '创意组平均分配到广告' : null}</Title>
+                        <div className={style.detail_body_m}>
+                            {dynamicMaterialDTos.dynamicGroup.map((item: any, index: number) => {
+                                return <div key={index} style={{ width: deliveryMode === 'DELIVERY_MODE_CUSTOMIZE' ? [641, 642, 643].includes(creativeTemplateId) ? '100%' : [720, 721, 722, 1529, 618].includes(creativeTemplateId) ? '50%' : '25%' : '100%' }}>
+                                    <Title level={5} style={{ fontSize: 12 }}>创意组{index + 1}</Title>
+                                    {mType ? ['short_video', 'video'].includes(mType) ? <div className={style.video}>
+                                        <VideoNews src={item?.video_id?.url || item?.short_video1?.url} />
+                                        {item?.cover_id && <div className={style.cover_image}>
+                                            <img src={item?.cover_id?.url} />
+                                        </div>}
+                                    </div> : ['image_list'].includes(mType) ? <div className={style.imageList}>
+                                        {item?.image_list?.map((item: { url: string | undefined; }, index: undefined) => <div className={style.cover_image} key={index}>
+                                            <img src={item?.url} />
+                                        </div>)}
+                                    </div> : ['element_story'].includes(mType) ? <div className={style.imageList}>
+                                        {item?.element_story?.map((item: { url: string | undefined; }, index: undefined) => <div className={style.cover_image} key={index}>
+                                            <img src={item?.url} />
+                                        </div>)}
+                                    </div> : ['image'].includes(mType) ? <div className={style.cover_image}>
+                                        <img src={item?.image_id?.url} />
+                                    </div> : null : null}
+                                </div>
+                            })}
+                        </div>
+                    </> :
                     <div className={style.empty_block}>
                         <Empty description="暂无素材" imageStyle={{ height: 50 }} />
                     </div>}
@@ -70,16 +93,17 @@ const Material: React.FC = () => {
         </div>
 
         {newVisible && <AddMaterial
+            adLength={adLength}
             creativeTemplateId={creativeTemplateId}
-            value={dynamicMaterialDTos}
+            value={{ dynamicMaterialDTos, mediaType }}
             materialData={materialData}
             deliveryMode={deliveryMode}
             visible={newVisible}
             onClose={() => {
                 setNewVisible(false)
             }}
-            onChange={(values) => {
-                setAddelivery({ ...addelivery, dynamicMaterialDTos: values, dynamicCreativesTextDTOS: {} })
+            onChange={({ dynamicMaterialDTos, mediaType }) => {
+                setAddelivery({ ...addelivery, dynamicMaterialDTos, mediaType })
                 setNewVisible(false)
                 clearData()
             }}

+ 9 - 8
src/pages/launchSystemV3/tencentAdPutIn/create/MaterialText/newText.tsx

@@ -103,7 +103,7 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
             onFinish={handleOk}
         >
             <Card className="cardResetCss" style={{ marginTop: 10 }}>
-                <Form.Item name="type" label={<strong>文案分配规则</strong>} style={{ marginBottom: 0 }} rules={[{ required: true, message: '请选择营销目的!' }]}>
+                <Form.Item name="type" label={<strong>文案分配规则</strong>} style={{ marginBottom: 0 }} rules={[{ required: true, message: '请选择文案分配规则!' }]}>
                     <New1Radio
                         data={TextTypeList}
                         onChange={(value) => {
@@ -138,8 +138,8 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                             style={{ marginTop: 10 }}
                             key={key}
                         >
-                            <Space style={{ display: 'flex' }} align="baseline">
-                                <Space style={{ width: '100%' }} direction="vertical" size={10}>
+                            <div style={{ display: 'flex', width: '100%', alignItems: 'center', columnGap: 10 }}>
+                                <div style={{ display: 'inline-flex', flexWrap: 'wrap', gap: 10 }}>
                                     {textList.map(item => {
                                         return <Form.Item
                                             {...restField}
@@ -161,21 +161,22 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                                             }]}
                                         >
                                             <TextAideInput placeholder={'请输入' + item.label} style={{ width: 450 }} maxTextLength={item.restriction.textRestriction.maxLength} />
+                                            {/* <div style={{ width: 450, border: '1px solid red' }}>11111111</div> */}
                                         </Form.Item>
                                     })}
-                                </Space>
-                                
-                                {([3,2].includes(type) && textDto?.length > 1) && <Popconfirm
+                                </div>
+
+                                {([3, 2].includes(type) && textDto?.length > 1) && <Popconfirm
                                     title="你确定删除当前文案组?"
                                     onConfirm={() => remove(name)}
                                 >
                                     <MinusCircleOutlined style={{ marginLeft: 20, color: 'red' }} />
                                 </Popconfirm>}
-                            </Space>
+                            </div>
 
                         </Card>
                     ))}
-                    {[3,2].includes(type) && !(type === 2 && textDto.length >= dynamicMaterialDTos.dynamicGroup.length) && <Form.Item style={{ marginTop: 10, marginBottom: 0 }}>
+                    {[3, 2].includes(type) && !(type === 2 && textDto.length >= dynamicMaterialDTos.dynamicGroup.length) && <Form.Item style={{ marginTop: 10, marginBottom: 0 }}>
                         <Button type="dashed" onClick={() => add({})} block icon={<PlusOutlined />} disabled={type === 3 && textDto.length >= 30}>
                             新增
                         </Button>

+ 89 - 41
src/pages/launchSystemV3/tencentAdPutIn/create/addDynamic.tsx

@@ -9,7 +9,7 @@ import PageList from "./PageList";
 import { DispatchAddelivery } from ".";
 import { CheckOutlined, SearchOutlined } from "@ant-design/icons";
 import WechatAccount from "../../components/WechatAccount";
-import { cartesianProduct } from "@/utils/utils";
+import { cartesianProduct, distributeArray } from "@/utils/utils";
 import { columnsAddDynamic } from "./tableConfig";
 import { useAjax } from "@/Hook/useAjax";
 import { createDynamicTaskApi } from "@/services/adqV3";
@@ -22,7 +22,7 @@ const { Text, Title } = Typography;
 const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose, adData }) => {
 
     /****************************************/
-    const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {} })
+    const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
     const { adgroups } = addelivery
     const [wechatVisible, setWechatVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
     const [materialData, setMaterialData] = useState<any>({}) // 素材数据
@@ -55,7 +55,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
             message.error('请先选择媒体账户')
             return
         }
-        const { adgroups, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS } = addelivery
+        const { adgroups, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = addelivery
         if (!(adgroups && Object.keys(adgroups).length)) {
             message.error('请先配置广告信息')
             return
@@ -82,18 +82,60 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
         }
 
         let newTableData = {}, newDynamicCount = 0
+
+        let textType = dynamicCreativesTextDTOS.type
+        let textDto = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList || []
+        let textDtoLenth = textDto.length
+        let dynamicGroupLength = dynamicMaterialDTos?.dynamicGroup?.length || 0
+
+        let newDynamicGroup: any = []
+        if (![910].includes(dynamic.creativeTemplateId)) {
+            newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
+            if (newDynamicGroup.length > 0 && [0, 1, 2, 3].includes(textType)) {
+                if (textType === 0) {
+                    newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
+                } else if (textType === 1) {
+                    newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
+                } else if (textType === 2) {
+                    newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
+                } else if (textType === 3) {
+                    if (mediaType === 0) {
+                        newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
+                            let [dynamicGroup, textDtoData] = item
+                            return {
+                                ...dynamicGroup as any,
+                                textDto: textDtoData
+                            }
+                        })
+                    }
+                }
+            }
+        }
+
+        // 创意组平均分配到广告逻辑
+        let averageAdDynamicList: any[] = []
+        if (mediaType === 1 && newDynamicGroup.length) {
+            let adLength = 0
+            accountCreateLogs.forEach(item => {
+                adLength = adData.length
+                if (adLength > dynamicGroupLength) {
+                    message.error(`创意组分配规则选择创意组平均分配到广告时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
+                    return
+                }
+                averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
+            })
+        }
+
         adData.forEach((ad, index) => {
             let item = accountCreateLogs.find(a => a.accountId === ad.accountId) as PULLIN.AccountCreateLogsProps
-            let textType = dynamicCreativesTextDTOS.type
-            let textDto = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList || []
-            let textDtoLenth = textDto.length
-            let dynamicGroupLength = dynamicMaterialDTos?.dynamicGroup?.length || 0
+            let averageAdDynamic = averageAdDynamicList?.[index]
             let data = [{
                 id: ad.adgroupId + '_' + index,
                 pageListDto: item.pageList,                   // 落地页
                 adgroupsDto: ad,
                 dynamicDto: dynamic,                          // 创意信息
-                rowSpan: ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
+                averageAdDynamic,
+                rowSpan: mediaType === 1 ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
             }]
 
             let newData: any[] = []
@@ -111,37 +153,43 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                     }
                 })
             } else {
-                let newDynamicGroup: any = dynamicMaterialDTos?.dynamicGroup || []
-                if (newDynamicGroup.length > 0 && [0, 1, 2, 3].includes(textType)) {
-                    if (textType === 0) {
-                        newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
-                    } else if (textType === 1) {
-                        newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
-                    } else if (textType === 2) {
-                        newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
-                    } else if (textType === 3) {
-                        newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
-                            let [dynamicGroup, textDtoData] = item
-                            return {
-                                ...dynamicGroup as any,
-                                textDto: textDtoData
-                            }
-                        })
-                    }
-                }
-                newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
-                    let [d1, group, num] = item
-                    return {
-                        ...d1,
-                        id: d1.id + '_' + index,
-                        dynamicGroup: group,
-                        textDto: (group as any)?.textDto,
-                        dynamicDto: {
-                            ...d1.dynamicDto,
-                            dynamicCreativeName: d1.dynamicDto.dynamicCreativeName + '_' + num
+                if (mediaType === 1) {
+                    data.forEach(item => {
+                        const { averageAdDynamic, ...ad } = item
+                        if (textType === 3) {
+                            let rowSpan = textDtoLenth * averageAdDynamic.length
+                            cartesianProduct(textDto, averageAdDynamic).forEach((taad: any, index) => {
+                                let [textValue, aad] = taad
+                                newData.push({
+                                    ...ad,
+                                    id: ad.id + '_' + index,
+                                    dynamicGroup: aad,
+                                    textDto: textValue,
+                                    rowSpan
+                                })
+                            })
+                        } else {
+                            averageAdDynamic.forEach((aad: any, index: number) => {
+                                newData.push({
+                                    ...ad,
+                                    id: ad.id + '_' + index,
+                                    dynamicGroup: aad,
+                                    textDto: aad?.textDto
+                                })
+                            })
                         }
-                    }
-                })
+                    })
+                } else {
+                    newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
+                        let [d1, group] = item
+                        return {
+                            ...d1,
+                            id: d1.id + '_' + index,
+                            dynamicGroup: group,
+                            textDto: (group as any)?.textDto
+                        }
+                    })
+                }
             }
             newDynamicCount += newData.length
             newTableData[ad.adgroupId] = newData
@@ -153,7 +201,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
     }
 
     const onSubmit = () => {
-        const { dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS } = addelivery
+        const { dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = addelivery
 
         let dynamicMaterialDTOS = []
         if ((materialData && Object.keys(materialData).length && dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
@@ -237,7 +285,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
         })
         let params = {
             accountAdgroupMaps: adData.map(item => `${item.accountId},${item.adgroupId}`),
-            dynamicCreativesDTO: dynamic,
+            dynamicCreativesDTO: { ...dynamic, mediaType },
             dynamicCreativesTextDTOS,
             dynamicMaterialDTOS,
             accountIdParamDTOMap
@@ -301,7 +349,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                                     {/* 创意 */}
                                     <Dynamic />
                                     {/* 创意素材 */}
-                                    <Material />
+                                    <Material adData={adData} />
                                 </div>
                                 <div className={style.settingsBody_content_left}>
                                     {/* 创意文案 */}

+ 102 - 42
src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx

@@ -16,7 +16,7 @@ import Dynamic from "./Dynamic"
 import Material from "./Material"
 import MaterialText from "./MaterialText"
 import PageList from "./PageList"
-import { cartesianProduct, randomString } from "@/utils/utils"
+import { cartesianProduct, distributeArray, randomString } from "@/utils/utils"
 import columns from "./tableConfig"
 import SubmitModal from "./submitModal"
 import { createAdgroupTaskApi, getSelectTaskDetailApi } from "@/services/adqV3"
@@ -33,7 +33,7 @@ const Create: React.FC = () => {
     /*******************************************/
     const { getAllUserAccount } = useModel('useLaunchAdq.useAdAuthorize')
     const { initTargeting } = useModel('useLaunchV3.useTargeting')
-    const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {} })
+    const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
     const { marketingAssetOuterSpec, marketingCarrierType } = addelivery.adgroups
     const [accSearch, setAccSearch] = useState<string>()
     const [accountCreateLogs, setAccountCreateLogs] = useState<PULLIN.AccountCreateLogsProps[]>([])  // 账户
@@ -79,7 +79,7 @@ const Create: React.FC = () => {
                     })
                     setAccountCreateLogs(userArr?.map((item) => ({ accountId: item?.accountId })))
                     clearData()
-                    setAddelivery({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {} })
+                    setAddelivery({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
                 } else {
                     message.error('操作异常')
                 }
@@ -112,7 +112,8 @@ const Create: React.FC = () => {
             targeting: [],
             dynamic: {},
             dynamicMaterialDTos: {},
-            dynamicCreativesTextDTOS: {}
+            dynamicCreativesTextDTOS: {},
+            mediaType: 0
         })
         setTableData({})
     }
@@ -125,7 +126,7 @@ const Create: React.FC = () => {
             getSelectTaskDetail.run(taskId).then(res => {
                 console.log(res)
                 if (res) {
-                    const { adgroupDTO, accountIdParamVOMap, targetings, dynamicCreativesDTO, dynamicCreativesTextDTO, dynamicMaterialDTOS } = res
+                    const { adgroupDTO, accountIdParamVOMap, targetings, dynamicCreativesDTO: { mediaType, ...dynamic }, dynamicCreativesTextDTO, dynamicMaterialDTOS } = res
                     let beginDate = adgroupDTO.beginDate
                     let endDate = adgroupDTO.endDate
                     if (beginDate && moment(beginDate) < moment()) {
@@ -134,7 +135,7 @@ const Create: React.FC = () => {
                         message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
                     }
                     let dynamicGroup: any[] = []
-                    if (dynamicCreativesDTO.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
+                    if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
                         dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
                             let { type, valueJson } = item[0]
                             let value = JSON.parse(valueJson).value
@@ -162,9 +163,10 @@ const Create: React.FC = () => {
                             const { targetingName, ...targeting } = item
                             return { targetingName, targeting }
                         }),
-                        dynamic: dynamicCreativesDTO,
+                        dynamic,
                         dynamicMaterialDTos: dynamicGroup.length > 0 ? { dynamicGroup } : {},
-                        dynamicCreativesTextDTOS: dynamicCreativesTextDTO
+                        dynamicCreativesTextDTOS: dynamicCreativesTextDTO,
+                        mediaType: mediaType || 0
                     })
                     setAccountCreateLogs(Object.keys(accountIdParamVOMap || {}).map(accountId => {
                         const { productDTOS, wechatOfficialAccountsVO, pageList, userActionSetsList } = accountIdParamVOMap[accountId]
@@ -213,7 +215,7 @@ const Create: React.FC = () => {
             message.error('请先选择媒体账户')
             return
         }
-        const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS } = addelivery
+        const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = addelivery
         if (!(adgroups && Object.keys(adgroups).length)) {
             message.error('请先配置广告信息')
             return
@@ -249,6 +251,54 @@ const Create: React.FC = () => {
 
         let newTableData: any = {}
         let newAdCount = 0, newdynamicCount = 0
+        let textType = dynamicCreativesTextDTOS.type
+        let textDto = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList || []
+        let textDtoLenth = textDto.length
+        let dynamicGroupLength = dynamicMaterialDTos?.dynamicGroup?.length || 0
+        let newDynamicGroup: any = []
+        if (![910].includes(dynamic.creativeTemplateId)) {
+            newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
+            if (newDynamicGroup.length > 0 && [0, 1, 2, 3].includes(textType)) {
+                if (textType === 0) {
+                    newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
+                } else if (textType === 1) {
+                    newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
+                } else if (textType === 2) {
+                    newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
+                } else if (textType === 3) {
+                    if (mediaType === 0) {
+                        newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
+                            let [dynamicGroup, textDtoData] = item
+                            return {
+                                ...dynamicGroup as any,
+                                textDto: textDtoData
+                            }
+                        })
+                    }
+                }
+            }
+        }
+
+        // 创意组平均分配到广告逻辑
+        let averageAdDynamicList: any[] = []
+        if (mediaType === 1 && newDynamicGroup.length) {
+            let adLength = 0
+            accountCreateLogs.forEach(item => {
+                let productList: any[] = []
+                if (['MARKETING_TARGET_TYPE_FICTION'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说
+                    productList = item?.productList || []
+                } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
+                    productList = item?.wechatChannelList || []
+                }
+                adLength += productList.length * targeting.length
+                if (adLength > dynamicGroupLength) {
+                    message.error(`创意组分配规则选择创意组平均分配到广告时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
+                    return
+                }
+                averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
+            })
+        }
+        let accountIndex = 0
         accountCreateLogs.forEach(item => {
             let productList: any[] = []
             if (['MARKETING_TARGET_TYPE_FICTION'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说
@@ -256,13 +306,11 @@ const Create: React.FC = () => {
             } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
                 productList = item?.wechatChannelList || []
             }
-            let textType = dynamicCreativesTextDTOS.type
-            let textDto = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList || []
-            let textDtoLenth = textDto.length
-            let dynamicGroupLength = dynamicMaterialDTos?.dynamicGroup?.length || 0
+
             let data = cartesianProduct(productList, targeting).map(newD => {
                 let [productDto, targetDto, index] = newD
                 let suffix = '_' + item.accountId + '_' + index
+                let averageAdDynamic = averageAdDynamicList?.[accountIndex]
                 let dat: any = {
                     id: item.accountId + '_' + index,
                     accountId: item.accountId,                    // 账户
@@ -278,11 +326,13 @@ const Create: React.FC = () => {
                         adgroupName: adgroups.adgroupName + suffix
                     },
                     dynamicDto: dynamic,                          // 创意信息
-                    rowSpan: ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
+                    averageAdDynamic,
+                    rowSpan: mediaType === 1 ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
                 }
                 if (marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') { // 营销载体
                     dat.marketingCarrierDto = item?.wechatChannelList
                 }
+                accountIndex += 1
                 return dat
             })
 
@@ -302,33 +352,43 @@ const Create: React.FC = () => {
                     }
                 })
             } else {
-                let newDynamicGroup: any = dynamicMaterialDTos?.dynamicGroup || []
-                if (newDynamicGroup.length > 0 && [0, 1, 2, 3].includes(textType)) {
-                    if (textType === 0) {
-                        newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
-                    } else if (textType === 1) {
-                        newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
-                    } else if (textType === 2) {
-                        newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
-                    } else if (textType === 3) {
-                        newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
-                            let [dynamicGroup, textDtoData] = item
-                            return {
-                                ...dynamicGroup as any,
-                                textDto: textDtoData
-                            }
-                        })
-                    }
+                if (mediaType === 1) {
+                    data.forEach(item => {
+                        const { averageAdDynamic, ...ad } = item
+                        if (textType === 3) {
+                            let rowSpan = textDtoLenth * averageAdDynamic.length
+                            cartesianProduct(textDto, averageAdDynamic).forEach((taad: any, index) => {
+                                let [textValue, aad] = taad
+                                newData.push({
+                                    ...ad,
+                                    id: ad.id + '_' + index,
+                                    dynamicGroup: aad,
+                                    textDto: textValue,
+                                    rowSpan
+                                })
+                            })
+                        } else {
+                            averageAdDynamic.forEach((aad: any, index: number) => {
+                                newData.push({
+                                    ...ad,
+                                    id: ad.id + '_' + index,
+                                    dynamicGroup: aad,
+                                    textDto: aad?.textDto
+                                })
+                            })
+                        }
+                    })
+                } else {
+                    newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
+                        let [d1, group] = item
+                        return {
+                            ...d1,
+                            id: d1.id + '_' + index,
+                            dynamicGroup: group,
+                            textDto: (group as any)?.textDto
+                        }
+                    })
                 }
-                newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
-                    let [d1, group] = item
-                    return {
-                        ...d1,
-                        id: d1.id + '_' + index,
-                        dynamicGroup: group,
-                        textDto: (group as any)?.textDto
-                    }
-                })
             }
 
             newdynamicCount = newdynamicCount + newData.length
@@ -342,7 +402,7 @@ const Create: React.FC = () => {
     }
 
     const onSubmit = (values: any) => {
-        const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS } = addelivery
+        const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = addelivery
 
         let dynamicMaterialDTOS = []
         if ((materialData && Object.keys(materialData).length && dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
@@ -428,7 +488,7 @@ const Create: React.FC = () => {
             ...values,
             adgroupDTO: adgroups,
             targetings: targeting.map(item => ({ targetingName: item.targetingName, ...item?.targeting || {} })),
-            dynamicCreativesDTO: dynamic,
+            dynamicCreativesDTO: { ...dynamic, mediaType },
             dynamicCreativesTextDTOS,
             dynamicMaterialDTOS,
             accountIdParamDTOMap

+ 2 - 1
src/pages/launchSystemV3/tencentAdPutIn/typings.d.ts

@@ -17,7 +17,8 @@ declare namespace PULLIN {
         targeting: any[],
         dynamic: any,
         dynamicMaterialDTos: any
-        dynamicCreativesTextDTOS: any
+        dynamicCreativesTextDTOS: any,
+        mediaType: 0 | 1
     }
     interface DispatchAddelivery {
         addelivery: AddeliveryProps,

+ 2 - 2
src/services/api.ts

@@ -1,5 +1,5 @@
-export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'http://test.api.zanxiangwl.com'
+export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'https://testapi.zanxiangwl.com'
 // export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'http://api.zanxiangwl.com'
-export let dataApi: any = process.env.NODE_ENV === 'development' ? 'dapi' : `http://data.zanxiangnet.com`
+export let dataApi: any = process.env.NODE_ENV === 'development' ? 'dapi' : `https://data.zanxiangnet.com`
 export let wxApi: any = process.env.NODE_ENV === 'development' ? 'wxapi' : `https://report.zanxiangwl.com`
 export let launchApi: any = `http://192.168.7.175:8018`

+ 20 - 1
src/utils/utils.ts

@@ -372,4 +372,23 @@ export const arraysHaveSameValues = (arr1: any[], arr2: any[]): boolean => {
   }
 
   return true;
-}
+}
+
+
+/**
+ * 定义一个函数,用于将数组平均分配给多个数组
+ * @param originalArray 
+ * @param numGroups 
+ * @returns 
+ */
+export const distributeArray = (originalArray: any[], numGroups: number): any[][] => {
+  // 初始化结果数组,每个子数组为空
+  const result: any[][] = Array.from({ length: numGroups }, () => []);
+
+  // 分配原始数组中的元素到结果数组
+  originalArray.forEach((element, index) => {
+    result[index % numGroups].push(element);
+  });
+
+  return result;
+};