wjx 9 months ago
parent
commit
d9c8ffb453

+ 14 - 4
src/pages/launchSystemV3/adqv3/ad/index.tsx

@@ -324,7 +324,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
             </Space>}
             </Space>}
             rowSelection={{
             rowSelection={{
                 selectedRowKeys: selectedRows.map(item => item.adgroupId.toString()),
                 selectedRowKeys: selectedRows.map(item => item.adgroupId.toString()),
-                hideSelectAll: handleType === 3,
+                // hideSelectAll: handleType === 3,
                 getCheckboxProps: (record: any) => {
                 getCheckboxProps: (record: any) => {
                     if (handleType === 2 && selectedRows?.length > 0) {
                     if (handleType === 2 && selectedRows?.length > 0) {
                         const { siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled } = selectedRows[0]
                         const { siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled } = selectedRows[0]
@@ -395,10 +395,20 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                 }
                 }
             }}
             }}
             onChange={(props: any) => {
             onChange={(props: any) => {
-                let { pagination } = props
+                let { pagination, sortData } = props
                 let { current, pageSize } = pagination
                 let { current, pageSize } = pagination
-                set_queryFrom({ ...queryFrom, pageNum: current, pageSize })
-                getList({ ...queryFrom, pageNum: current, pageSize })
+                let newQueryForm = JSON.parse(JSON.stringify(queryFrom))
+                if (sortData && sortData?.order) {
+                    newQueryForm['isAsc'] = sortData?.order === 'ascend'
+                    newQueryForm['orderByColumn'] = [sortData?.field]
+                } else {
+                    delete newQueryForm['isAsc']
+                    delete newQueryForm['orderByColumn']
+                }
+                newQueryForm.pageNum = current
+                newQueryForm.pageSize = pageSize
+                set_queryFrom(newQueryForm)
+                getList(newQueryForm)
             }}
             }}
         />
         />
 
 

+ 1 - 0
src/pages/launchSystemV3/adqv3/ad/tableConfig.tsx

@@ -95,6 +95,7 @@ function tableConfig(onChange: () => void, creativeHandle?: (id: number) => void
             align: 'center',
             align: 'center',
             width: 150,
             width: 150,
             ellipsis: true,
             ellipsis: true,
+            sorter: true,
             render: (a: string, b: { endDate: string }) => {
             render: (a: string, b: { endDate: string }) => {
                 return b?.endDate ? a + '~' + b.endDate : a + '~' + '长期投放'
                 return b?.endDate ? a + '~' + b.endDate : a + '~' + '长期投放'
             }
             }

+ 5 - 4
src/pages/launchSystemV3/adqv3/config.ts

@@ -46,10 +46,11 @@ const txDynamicConfig = [
             { title: '广告ID', dataIndex: 'adgroupId', label: '广告详情', default: 10, width: 90 },
             { title: '广告ID', dataIndex: 'adgroupId', label: '广告详情', default: 10, width: 90 },
             { title: '创意名称', dataIndex: 'dynamicCreativeName', label: '广告详情', default: 11, width: 120 },
             { title: '创意名称', dataIndex: 'dynamicCreativeName', label: '广告详情', default: 11, width: 120 },
             { title: '创意ID', dataIndex: 'dynamicCreativeId', label: '广告详情', default: 12, width: 80 },
             { title: '创意ID', dataIndex: 'dynamicCreativeId', label: '广告详情', default: 12, width: 80 },
-            { title: '投放模式', dataIndex: 'deliveryMode', label: '广告详情', default: 13, width: 90 },
-            { title: '创意形式匹配方式', dataIndex: 'dynamicCreativeType', label: '广告详情', default: 14, width: 85 },
-            { title: '创意形式', dataIndex: 'creativeTemplateId', label: '广告详情', default: 15, width: 100 },
-            { title: '审核状态', dataIndex: 'reviewStatusCn', label: '广告详情', default: 16, width: 100 },
+            { title: '是否已删除', dataIndex: 'isDeleted', label: '广告详情', default: 13, width: 60 },
+            { title: '投放模式', dataIndex: 'deliveryMode', label: '广告详情', default: 14, width: 75 },
+            { title: '创意形式匹配方式', dataIndex: 'dynamicCreativeType', label: '广告详情', default: 15, width: 75 },
+            { title: '创意形式', dataIndex: 'creativeTemplateId', label: '广告详情', default: 16, width: 90 },
+            { title: '审核状态', dataIndex: 'reviewStatusCn', label: '广告详情', default: 17, width: 90 },
         ]
         ]
     }
     }
 ]
 ]

+ 96 - 0
src/pages/launchSystemV3/adqv3/creative/handleLog.tsx

@@ -0,0 +1,96 @@
+import { useAjax } from "@/Hook/useAjax"
+import { dynamicCreativeLogApi } from "@/services/launchAdq/adqv3"
+import { Button, Col, Form, Input, Modal, Row, Space, Table } from "antd"
+import React, { useEffect, useState } from "react"
+import { columnsLog } from "./tableConfig"
+
+interface Props {
+    userId: number
+    visible?: boolean,
+    onClose?: () => void,
+}
+
+/**
+ * 创意操作记录
+ * @param param0 
+ * @returns 
+ */
+const HandleLog: React.FC<Props> = ({ visible, onClose, userId }) => {
+
+    /*********************************/
+    const [form] = Form.useForm();
+    const [queryParams, setQueryParams] = useState<ADQV3.DynamicCreativeLogProps>({ pageNum: 1, pageSize: 20 })
+    const dynamicCreativeLog = useAjax((params) => dynamicCreativeLogApi(params))
+    /*********************************/
+
+    useEffect(() => {
+        dynamicCreativeLog.run({ ...queryParams, userId })
+    }, [queryParams])
+
+    const onFinish = (values: any) => {
+        console.log(values)
+        setQueryParams({ ...queryParams, ...values })
+    }
+
+    return <Modal
+        title={<strong>创意操作记录</strong>}
+        className="modalResetCss"
+        visible={visible}
+        onCancel={onClose}
+        footer={null}
+        width={1100}
+    >
+        <Space style={{ width: '100%' }} direction="vertical">
+            <Form
+                layout="inline"
+                form={form}
+                name="handleLog"
+                onFinish={onFinish}
+                style={{ marginBottom: 6 }}
+            >
+                <Row gutter={[10, 10]}>
+                    <Col><Form.Item name='accountId' style={{ marginRight: 0 }}>
+                        <Input placeholder="广告账号" style={{ width: 120 }} allowClear />
+                    </Form.Item></Col>
+                    <Col><Form.Item name='adgroupId' style={{ marginRight: 0 }}>
+                        <Input placeholder="广告ID" style={{ width: 120 }} allowClear />
+                    </Form.Item></Col>
+                    <Col><Form.Item name='creativeName' style={{ marginRight: 0 }}>
+                        <Input placeholder="创意名称" style={{ width: 150 }} allowClear />
+                    </Form.Item></Col>
+                    <Col><Form.Item name='creativeId' style={{ marginRight: 0 }}>
+                        <Input placeholder="创意ID" style={{ width: 120 }} allowClear />
+                    </Form.Item></Col>
+                    <Col><Form.Item style={{ marginRight: 0 }}>
+                        <Space>
+                            <Button type="primary" htmlType="submit">搜索</Button>
+                            <Button onClick={() => {
+                                form.resetFields()
+                            }}>重置</Button>
+                        </Space>
+                    </Form.Item></Col>
+                </Row>
+            </Form>
+            <Table
+                columns={columnsLog()}
+                dataSource={dynamicCreativeLog?.data?.records}
+                size="small"
+                loading={dynamicCreativeLog?.loading}
+                scroll={{ y: 500 }}
+                rowKey={'id'}
+                pagination={{
+                    defaultPageSize: 100,
+                    current: dynamicCreativeLog.data?.current || 1,
+                    pageSize: dynamicCreativeLog.data?.size || 10,
+                    total: dynamicCreativeLog.data?.total || 0
+                }}
+                onChange={(pagination) => {
+                    const { current, pageSize } = pagination
+                    setQueryParams({ ...queryParams, pageNum: current || 1, pageSize: pageSize || 10 })
+                }}
+            />
+        </Space>
+    </Modal>
+}
+
+export default React.memo(HandleLog)

+ 34 - 4
src/pages/launchSystemV3/adqv3/creative/index.tsx

@@ -8,7 +8,7 @@ import TableData from "@/pages/launchSystemNew/components/TableData"
 import ReviewDetails from "./reviewDetails"
 import ReviewDetails from "./reviewDetails"
 import { DeleteOutlined, PauseCircleOutlined, PlayCircleOutlined } from "@ant-design/icons"
 import { DeleteOutlined, PauseCircleOutlined, PlayCircleOutlined } from "@ant-design/icons"
 import '../../tencentAdPutIn/index.less'
 import '../../tencentAdPutIn/index.less'
-import { MessageApi } from "antd/lib/message"
+import HandleLog from "./handleLog"
 
 
 /** 审核结果 */
 /** 审核结果 */
 export const AD_STATUS = {
 export const AD_STATUS = {
@@ -27,6 +27,7 @@ const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, user
     const [selectedRows, setSelectedRows] = useState<any[]>([])
     const [selectedRows, setSelectedRows] = useState<any[]>([])
     const [failIdList, setFailIdList] = useState<{ adgroupId: number, code: number, message: string, messageCn: string }[]>([])
     const [failIdList, setFailIdList] = useState<{ adgroupId: number, code: number, message: string, messageCn: string }[]>([])
     const [failVisible, setFailVisible] = useState<boolean>(false)
     const [failVisible, setFailVisible] = useState<boolean>(false)
+    const [logVisible, setLogVisible] = useState<boolean>(false)
 
 
     const getDynamicCreativeV3List = useAjax((params) => getDynamicCreativeV3ListApi(params), { formatResult: true })
     const getDynamicCreativeV3List = useAjax((params) => getDynamicCreativeV3ListApi(params), { formatResult: true })
     const delBatchCreative = useAjax((params) => delBatchCreativeApi(params))
     const delBatchCreative = useAjax((params) => delBatchCreativeApi(params))
@@ -57,7 +58,7 @@ const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, user
     }
     }
 
 
     const dynamicHandle = (type: '删除' | '启动' | '暂停', data?: any) => {
     const dynamicHandle = (type: '删除' | '启动' | '暂停', data?: any) => {
-        let accountAdgroupMaps = data ? [data.accountId + ',' + data.dynamicCreativeId] :[...new Set(selectedRows?.map(item => item.accountId + ',' + item.dynamicCreativeId))]
+        let accountAdgroupMaps = data ? [data.accountId + ',' + data.dynamicCreativeId] : [...new Set(selectedRows?.map(item => item.accountId + ',' + item.dynamicCreativeId))]
         let hide: any
         let hide: any
         if (data) {
         if (data) {
             hide = message.loading(`正在设置...`, 0, () => {
             hide = message.loading(`正在设置...`, 0, () => {
@@ -134,14 +135,31 @@ const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, user
                 <Col><Form.Item name='creativeId' style={{ marginRight: 0 }}>
                 <Col><Form.Item name='creativeId' style={{ marginRight: 0 }}>
                     <Input placeholder="创意ID" style={{ width: 120 }} allowClear />
                     <Input placeholder="创意ID" style={{ width: 120 }} allowClear />
                 </Form.Item></Col>
                 </Form.Item></Col>
+                <Col><Form.Item name='configuredStatus'>
+                    <Select
+                        placeholder='启用禁用状态'
+                        style={{ minWidth: 120 }}
+                        showSearch
+                        allowClear
+                        filterOption={(input: any, option: any) =>
+                            (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                        }
+                        mode="multiple"
+                    >
+                        <Select.Option value={'AD_STATUS_NORMAL'}>有效</Select.Option>
+                        <Select.Option value={'AD_STATUS_SUSPEND'}>暂停</Select.Option>
+                    </Select>
+                </Form.Item></Col>
                 <Col><Form.Item name='isDeleted'>
                 <Col><Form.Item name='isDeleted'>
                     <Select
                     <Select
                         placeholder='是否删除?'
                         placeholder='是否删除?'
-                        style={{ width: 100 }}
+                        style={{ minWidth: 100 }}
                         showSearch
                         showSearch
+                        allowClear
                         filterOption={(input: any, option: any) =>
                         filterOption={(input: any, option: any) =>
                             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                         }
                         }
+                        mode="multiple"
                     >
                     >
                         <Select.Option value={true}>是</Select.Option>
                         <Select.Option value={true}>是</Select.Option>
                         <Select.Option value={false}>否</Select.Option>
                         <Select.Option value={false}>否</Select.Option>
@@ -150,7 +168,10 @@ const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, user
                 <Col><Form.Item style={{ marginRight: 0 }}>
                 <Col><Form.Item style={{ marginRight: 0 }}>
                     <Space>
                     <Space>
                         <Button type="primary" htmlType="submit">搜索</Button>
                         <Button type="primary" htmlType="submit">搜索</Button>
-                        <Button onClick={() => form.resetFields()}>重置</Button>
+                        <Button onClick={() => {
+                            setQueryForm({ pageNum: 1, pageSize: queryForm.pageSize, userId })
+                            form.resetFields()
+                        }}>重置</Button>
                         <Button disabled={selectedRows.length === 0} style={{ padding: 0 }} danger type="link" onClick={() => setSelectedRows([])}>清空已选({selectedRows.length})</Button>
                         <Button disabled={selectedRows.length === 0} style={{ padding: 0 }} danger type="link" onClick={() => setSelectedRows([])}>清空已选({selectedRows.length})</Button>
                     </Space>
                     </Space>
                 </Form.Item></Col>
                 </Form.Item></Col>
@@ -182,6 +203,7 @@ const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, user
                 >
                 >
                     <Button type='primary' danger icon={<DeleteOutlined />} loading={delBatchCreative.loading} disabled={selectedRows.length === 0}>删除</Button>
                     <Button type='primary' danger icon={<DeleteOutlined />} loading={delBatchCreative.loading} disabled={selectedRows.length === 0}>删除</Button>
                 </Popconfirm>
                 </Popconfirm>
+                <Button type='dashed' onClick={() => { setLogVisible(true) }}>操作记录</Button>
             </Space>}
             </Space>}
             rowSelection={{
             rowSelection={{
                 selectedRowKeys: selectedRows.map(item => item.dynamicCreativeId.toString()),
                 selectedRowKeys: selectedRows.map(item => item.dynamicCreativeId.toString()),
@@ -270,6 +292,14 @@ const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, user
                 dataSource={failIdList}
                 dataSource={failIdList}
             />
             />
         </Modal>}
         </Modal>}
+
+        {logVisible && <HandleLog
+            visible={logVisible}
+            onClose={() => {
+                setLogVisible(false)
+            }}
+            userId={userId}
+        />}
     </>
     </>
 }
 }
 
 

+ 165 - 1
src/pages/launchSystemV3/adqv3/creative/tableConfig.tsx

@@ -1,5 +1,5 @@
 import React from 'react'
 import React from 'react'
-import { Space, Switch, Image, Popover, TableProps, Typography } from 'antd'
+import { Space, Switch, Image, Popover, TableProps, Typography, Badge } from 'antd'
 import '../index.less'
 import '../index.less'
 import { copy } from '@/utils/utils'
 import { copy } from '@/utils/utils'
 import { DELIVERY_MODE, DYNAMIC_CREATIVE_TYPE } from '../const'
 import { DELIVERY_MODE, DYNAMIC_CREATIVE_TYPE } from '../const'
@@ -178,6 +178,16 @@ function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle: (
                 return <a onClick={() => copy(a)} >{a}</a>
                 return <a onClick={() => copy(a)} >{a}</a>
             }
             }
         },
         },
+        {
+            title: '是否已删除',
+            dataIndex: 'isDeleted',
+            key: 'isDeleted',
+            align: 'center',
+            width: 60,
+            render: (a: any) => {
+                return <Badge status={!a ? "processing" : "error"} text={a ? '是' : '否'} />
+            }
+        },
         {
         {
             title: '投放模式',
             title: '投放模式',
             dataIndex: 'deliveryMode',
             dataIndex: 'deliveryMode',
@@ -316,4 +326,158 @@ export const tableConfigDetail = (): TableProps<any>['columns'] => {
         },
         },
     ]
     ]
 }
 }
+
+export const columnsLog = (): TableProps<any>['columns'] => [
+    {
+        title: '创意名称',
+        dataIndex: 'creativeName',
+        key: 'creativeName',
+        width: 280,
+        ellipsis: true,
+        fixed: 'left',
+        render: (value) => {
+            return <span style={{ fontSize: "12px" }}>{value}</span>
+        }
+    },
+    {
+        title: '创意ID',
+        dataIndex: 'creativeId',
+        key: 'creativeId',
+        align: 'center',
+        width: 100,
+        ellipsis: true,
+        fixed: 'left',
+        render: (value) => {
+            return <span style={{ fontSize: "12px", cursor: 'pointer' }}>{value}</span>
+        }
+    },
+    {
+        title: '所属账号',
+        dataIndex: 'accountId',
+        key: 'accountId',
+        align: 'center',
+        width: 80,
+        ellipsis: true,
+        render: (value) => {
+            return <span style={{ fontSize: "12px" }}>{value}</span>
+        }
+    },
+    {
+        title: '广告ID',
+        dataIndex: 'adgroupId',
+        key: 'adgroupId',
+        align: 'center',
+        width: 100,
+        ellipsis: true,
+        render: (value) => {
+            return <span style={{ fontSize: "12px", cursor: 'pointer' }}>{value}</span>
+        }
+    },
+    {
+        title: '执行时间',
+        dataIndex: 'createTime',
+        key: 'createTime',
+        width: 140,
+        align: 'center',
+        render: (value) => {
+            return <span style={{ fontSize: 12 }}>{value}</span>
+        }
+    },
+    {
+        title: '操作者名字',
+        dataIndex: 'operationByName',
+        key: 'operationByName',
+        width: 65,
+        align: 'center',
+        render: (value) => {
+            return <span style={{ fontSize: 12 }}>{value}</span>
+        }
+    },
+    {
+        title: '操作类型名称',
+        dataIndex: 'operationName',
+        key: 'operationName',
+        width: 70,
+        align: 'center',
+        render: (value) => {
+            return <span style={{ fontSize: 12 }}>{value}</span>
+        }
+    },
+    {
+        title: '操作数',
+        dataIndex: 'operationCount',
+        key: 'operationCount',
+        width: 60,
+        align: 'center',
+        render: (value) => {
+            return <span style={{ fontSize: 12 }}>{value}</span>
+        }
+    },
+    {
+        title: '成功数',
+        dataIndex: 'successCount',
+        key: 'successCount',
+        width: 60,
+        align: 'center',
+        render: (value) => {
+            return <span style={{ fontSize: 12 }}>{value}</span>
+        }
+    },
+    {
+        title: '失败数',
+        dataIndex: 'failCount',
+        key: 'failCount',
+        width: 60,
+        align: 'center',
+        render: (value) => {
+            return <span style={value > 0 ? { color: 'red', fontSize: 12 } : { fontSize: 12 }}>{value}</span>
+        }
+    },
+    {
+        title: '状态',
+        dataIndex: 'status',
+        key: 'status',
+        width: 70,
+        align: 'center',
+        render: (a: any) => {
+            let obj = {
+                '成功': 'success',
+                '失败': 'error',
+                '执行中': 'warning'
+            }
+            return <Badge status={obj[a]} text={a} />
+        }
+    },
+    {
+        title: '完成时间',
+        dataIndex: 'updateTime',
+        key: 'updateTime',
+        width: 140,
+        align: 'center',
+        render: (a: string) => {
+            return <span style={{ fontSize: 12 }}>{a}</span>
+        }
+    },
+    {
+        title: '错误消息',
+        dataIndex: 'errorMsg',
+        key: 'errorMsg',
+        width: 400,
+        ellipsis: true,
+        render: (value) => {
+            return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+        }
+    },
+    {
+        title: '操作参数',
+        dataIndex: 'requestJson',
+        key: 'requestJson',
+        width: 400,
+        ellipsis: true,
+        render: (value) => {
+            return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+        }
+    },
+]
+
 export default tableConfig
 export default tableConfig

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

@@ -18,7 +18,7 @@ const AdqV3: React.FC = () => {
     const [activeKey, setActiveKey] = useState('1')
     const [activeKey, setActiveKey] = useState('1')
     const [userId, setUserId] = useState<any>(userInfo?.userId?.toString())
     const [userId, setUserId] = useState<any>(userInfo?.userId?.toString())
     const [hide, setHide] = useState<boolean>(false)
     const [hide, setHide] = useState<boolean>(false)
-    const [queryForm, setQueryForm] = useState<ADQV3.GetDynamicCreativeProps>({ pageNum: 1, pageSize: 20, userId, isDeleted: false })
+    const [queryForm, setQueryForm] = useState<ADQV3.GetDynamicCreativeProps>({ pageNum: 1, pageSize: 20, userId })
     /****************************/
     /****************************/
 
 
     // 获取组员
     // 获取组员

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

@@ -50,4 +50,13 @@ declare namespace ADQV3 {
     interface ModifyDailyBudgetBatchProps extends AccountAdgroupMapsProps {
     interface ModifyDailyBudgetBatchProps extends AccountAdgroupMapsProps {
         bidAmount: number
         bidAmount: number
     }
     }
+    interface DynamicCreativeLogProps {
+        pageNum: number,
+        pageSize: number,
+        accountId?: number,
+        adgroupId?: number,
+        creativeId?: number,
+        creativeName?: string,
+        userId?: number
+    }
 }
 }

+ 3 - 2
src/pages/launchSystemV3/tencentAdPutIn/const.ts

@@ -521,8 +521,9 @@ export enum TEXT_LINK_TYPE_ENUM {
 export const TextTypeList = [
 export const TextTypeList = [
 	{ label: '全部相同', value: 0 },
 	{ label: '全部相同', value: 0 },
 	{ label: '按创意组一一对应', value: 1 },
 	{ label: '按创意组一一对应', value: 1 },
-	{ label: '按文案顺序分配', value: 2 },
-	{ label: '根据文案叉乘', value: 3 },
+	{ label: '文案顺序分配', value: 2 },
+	{ label: '先分配,文案后叉乘', value: 3 },
+	{ label: '文案叉乘打乱后分配', value: 4 }
 ]
 ]
 
 
 /** 创意素材替换原生页顶部素材 */
 /** 创意素材替换原生页顶部素材 */

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

@@ -2,12 +2,12 @@ import React, { useContext, useState } from "react"
 import style from '../index.less'
 import style from '../index.less'
 import NewCreateAd from "./newCreateAd"
 import NewCreateAd from "./newCreateAd"
 import { DispatchAddelivery } from "..";
 import { DispatchAddelivery } from "..";
-import { Button, Typography } from "antd";
+import { Button, Modal, Typography } from "antd";
 import { EditOutlined } from "@ant-design/icons";
 import { EditOutlined } from "@ant-design/icons";
 import { AD_STATUS_ENUM, BID_MODE_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
 import { AD_STATUS_ENUM, BID_MODE_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
 import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook";
 import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook";
 import { arraysHaveSameValues } from "@/utils/utils";
 import { arraysHaveSameValues } from "@/utils/utils";
-
+import '../../index.less'
 
 
 /**
 /**
  * 广告信息
  * 广告信息
@@ -95,12 +95,42 @@ const Ad: React.FC = () => {
                     arraysHaveSameValues(adgroups?.sceneSpec?.wechatPosition || [], sceneSpec?.wechatPosition || []) // 微信公众号与小程序定投
                     arraysHaveSameValues(adgroups?.sceneSpec?.wechatPosition || [], sceneSpec?.wechatPosition || []) // 微信公众号与小程序定投
                 ) {
                 ) {
                     setAddelivery({ ...addelivery, adgroups })
                     setAddelivery({ ...addelivery, adgroups })
+                    setNewVisible(false)
+                    clearData()
                 } else {
                 } else {
                     setAccountCreateLogs(accountCreateLogs.map(item => ({ accountId: item.accountId })))
                     setAccountCreateLogs(accountCreateLogs.map(item => ({ accountId: item.accountId })))
-                    setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
+                    if (
+                        addelivery?.adgroups && Object.keys(addelivery?.adgroups).length &&
+                        ((addelivery?.dynamic && Object.keys(addelivery?.dynamic).length)
+                            || addelivery?.targeting?.length
+                            || (addelivery?.dynamicMaterialDTos && Object.keys(addelivery?.dynamicMaterialDTos).length)
+                            || (addelivery?.dynamicCreativesTextDTOS && Object.keys(addelivery?.dynamicCreativesTextDTOS).length)
+                        )
+                    ) {
+                        Modal.confirm({
+                            title: <strong style={{ color: '#FAAD14' }}>注意</strong>,
+                            content: <span>当前改变了“营销内容”、“营销载体”、“推广产品”、“版位选择”、“版位”、“微信公众号与小程序定投”中的某些内容,不清空后面内容可能会导致当前“定向”、“创意”、“素材”等一些内容不匹配,<span style={{ color: 'red' }}>是否清空内容</span></span>,
+                            okText: '清空',
+                            cancelText: '不清空',
+                            keyboard: false,
+                            onOk() { // 清空
+                                setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
+                                setNewVisible(false)
+                                clearData()
+                            },
+                            onCancel() { // 不清空
+                                setAddelivery({ ...addelivery, adgroups })
+                                setNewVisible(false)
+                                clearData()
+                            },
+                            className: 'modalResetCss'
+                        })
+                    } else {
+                        setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
+                        setNewVisible(false)
+                        clearData()
+                    }
                 }
                 }
-                setNewVisible(false)
-                clearData()
             }}
             }}
         />}
         />}
     </>
     </>

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

@@ -102,13 +102,13 @@ const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, delive
 
 
     const handleOk = (values: any) => {
     const handleOk = (values: any) => {
         const { mediaType, dynamicGroup } = values
         const { mediaType, dynamicGroup } = values
-        if (mediaType === 1 && dynamicGroup.length < adLength) {
-            message.error({
-                content: `创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前广告总数:${adLength},创意组总数:${dynamicGroup.length}`,
-                duration: 8
-            })
-            return
-        }
+        // if (mediaType === 1 && dynamicGroup.length < adLength) {
+        //     message.error({
+        //         content: `创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前广告总数:${adLength},创意组总数:${dynamicGroup.length}`,
+        //         duration: 8
+        //     })
+        //     return
+        // }
         if (mediaType === 2 && dynamicGroup.length > adLength) {
         if (mediaType === 2 && dynamicGroup.length > adLength) {
             message.error({
             message.error({
                 content: `创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前广告总数:${adLength},创意组总数:${dynamicGroup.length}`,
                 content: `创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前广告总数:${adLength},创意组总数:${dynamicGroup.length}`,

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

@@ -134,7 +134,11 @@ const Material: React.FC<{ adData?: any[] }> = ({ adData }) => {
                 setNewVisible(false)
                 setNewVisible(false)
             }}
             }}
             onChange={({ dynamicMaterialDTos, mediaType }) => {
             onChange={({ dynamicMaterialDTos, mediaType }) => {
-                setAddelivery({ ...addelivery, dynamicMaterialDTos, mediaType })
+                let newAddelivery = { ...addelivery, dynamicMaterialDTos, mediaType }
+                if (addelivery.mediaType !== mediaType && mediaType !== 1 && addelivery.dynamicCreativesTextDTOS?.type === 4) {
+                    newAddelivery.dynamicCreativesTextDTOS = {}
+                }
+                setAddelivery(newAddelivery)
                 setNewVisible(false)
                 setNewVisible(false)
                 clearData()
                 clearData()
             }}
             }}

+ 5 - 3
src/pages/launchSystemV3/tencentAdPutIn/create/MaterialText/index.tsx

@@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from "react"
 import style from '../index.less'
 import style from '../index.less'
 import { Button, Typography } from "antd"
 import { Button, Typography } from "antd"
 import { DispatchAddelivery } from "..";
 import { DispatchAddelivery } from "..";
-import { PlusCircleOutlined } from "@ant-design/icons";
+import { PlusCircleOutlined, RedoOutlined } from "@ant-design/icons";
 import NewText from "./newText";
 import NewText from "./newText";
 const { Title, Text } = Typography;
 const { Title, Text } = Typography;
 
 
@@ -11,7 +11,7 @@ const MaterialText: React.FC = () => {
 
 
     /*************************************/
     /*************************************/
     const { textData, addelivery, setAddelivery, clearData } = useContext(DispatchAddelivery)!;
     const { textData, addelivery, setAddelivery, clearData } = useContext(DispatchAddelivery)!;
-    const { dynamic, dynamicCreativesTextDTOS, dynamicMaterialDTos } = addelivery
+    const { dynamic, dynamicCreativesTextDTOS, dynamicMaterialDTos, mediaType } = addelivery
 
 
     const [addVisible, setAddVisible] = useState<boolean>(false)
     const [addVisible, setAddVisible] = useState<boolean>(false)
     const [descName, setDescName] = useState<string>('')
     const [descName, setDescName] = useState<string>('')
@@ -35,10 +35,11 @@ const MaterialText: React.FC = () => {
     return <div className={`${style.settingsBody_content_row} ${style.row5}`}>
     return <div className={`${style.settingsBody_content_row} ${style.row5}`}>
         <div className={style.title}>
         <div className={style.title}>
             <span>创意文案</span>
             <span>创意文案</span>
+            {(dynamicCreativesTextDTOS && Object.keys(dynamicCreativesTextDTOS).length > 0) ? <Button type="link" size="small" style={{ fontSize: 11, padding: 0 }} onClick={() => setAddelivery({ ...addelivery, dynamicCreativesTextDTOS: {} })}><RedoOutlined />清空</Button> : null}
         </div>
         </div>
         <div className={style.detail}>
         <div className={style.detail}>
             <div className={style.detail_body}>
             <div className={style.detail_body}>
-                <Title level={5} style={{ fontSize: 12 }}>{dynamicCreativesTextDTOS?.type === 0 ? '全部相同' : dynamicCreativesTextDTOS?.type === 1 ? '按创意组一一对应' : dynamicCreativesTextDTOS?.type === 2 ? '按文案顺序分配' : dynamicCreativesTextDTOS?.type === 3 ? '根据文案叉乘' : null}</Title>
+                <Title level={5} style={{ fontSize: 12 }} ellipsis>{dynamicCreativesTextDTOS?.type === 0 ? '全部相同' : dynamicCreativesTextDTOS?.type === 1 ? '按创意组一一对应' : dynamicCreativesTextDTOS?.type === 2 ? '按文案顺序分配' : dynamicCreativesTextDTOS?.type === 3 ? '先分配创意组,文案后叉乘': dynamicCreativesTextDTOS?.type === 4 ? '创意组和文案叉乘打乱后分配' : null}</Title>
                 {dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList?.map((item: { [x: string]: boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined; }, index: number) => {
                 {dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList?.map((item: { [x: string]: boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined; }, index: number) => {
                     if (item) {
                     if (item) {
                         let keys = Object.keys(item)
                         let keys = Object.keys(item)
@@ -66,6 +67,7 @@ const MaterialText: React.FC = () => {
         {addVisible && <NewText
         {addVisible && <NewText
             value={dynamicCreativesTextDTOS}
             value={dynamicCreativesTextDTOS}
             dynamicMaterialDTos={dynamicMaterialDTos}
             dynamicMaterialDTos={dynamicMaterialDTos}
+            mediaType={mediaType}
             textData={textData}
             textData={textData}
             visible={addVisible}
             visible={addVisible}
             onClose={() => {
             onClose={() => {

+ 28 - 29
src/pages/launchSystemV3/tencentAdPutIn/create/MaterialText/newText.tsx

@@ -1,7 +1,6 @@
-import New1Radio from "@/pages/launchSystemV3/components/New1Radio"
 import TextAideInput from "@/pages/launchSystemV3/components/TextAideInput"
 import TextAideInput from "@/pages/launchSystemV3/components/TextAideInput"
 import { txtLength } from "@/utils/utils"
 import { txtLength } from "@/utils/utils"
-import { Button, Card, Form, Modal, Popconfirm, Space, message } from "antd"
+import { Button, Card, Form, Modal, Popconfirm, Radio, Space, message } from "antd"
 import React, { useEffect, useState } from "react"
 import React, { useEffect, useState } from "react"
 import { TextTypeList } from "../../const"
 import { TextTypeList } from "../../const"
 import { DeleteOutlined, PlusOutlined } from "@ant-design/icons"
 import { DeleteOutlined, PlusOutlined } from "@ant-design/icons"
@@ -10,6 +9,7 @@ import { DeleteOutlined, PlusOutlined } from "@ant-design/icons"
 interface Props {
 interface Props {
     textData: any,
     textData: any,
     dynamicMaterialDTos: any,
     dynamicMaterialDTos: any,
+    mediaType: 0 | 1 | 2,
     value?: any,
     value?: any,
     visible?: boolean
     visible?: boolean
     onClose?: () => void
     onClose?: () => void
@@ -21,7 +21,7 @@ interface Props {
  * @param param0 
  * @param param0 
  * @returns 
  * @returns 
  */
  */
-const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData, dynamicMaterialDTos }) => {
+const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData, dynamicMaterialDTos, mediaType }) => {
 
 
     /*************************************/
     /*************************************/
     const [form] = Form.useForm();
     const [form] = Form.useForm();
@@ -47,7 +47,6 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
     }
     }
 
 
     useEffect(() => {
     useEffect(() => {
-        console.log('创意文案--->', value)
         if (value && Object.keys(value).length) {
         if (value && Object.keys(value).length) {
             const { type, dynamicCreativesTextDetailDTOList } = value
             const { type, dynamicCreativesTextDetailDTOList } = value
             form.setFieldsValue({
             form.setFieldsValue({
@@ -86,7 +85,7 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
             form={form}
             form={form}
             name="newText"
             name="newText"
             labelAlign='left'
             labelAlign='left'
-            labelCol={{ span: 5 }}
+            layout="vertical"
             colon={false}
             colon={false}
             style={{ backgroundColor: '#f1f4fc', maxHeight: 650, overflow: 'hidden', overflowY: 'auto', padding: '0 10px 10px', borderRadius: '0 0 8px 8px' }}
             style={{ backgroundColor: '#f1f4fc', maxHeight: 650, overflow: 'hidden', overflowY: 'auto', padding: '0 10px 10px', borderRadius: '0 0 8px 8px' }}
             scrollToFirstError={{
             scrollToFirstError={{
@@ -104,28 +103,28 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
         >
         >
             <Card className="cardResetCss" style={{ marginTop: 10 }}>
             <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) => {
-                            let count = dynamicMaterialDTos.dynamicGroup.length
-                            let oldtextDto: PULLIN.TextDtoProps[] = JSON.parse(JSON.stringify(textDto))
-                            let length = oldtextDto.length
-                            if (value === 0) {
-                                oldtextDto = [textDto[0] || {}]
-                            } else if (value === 1) {
-                                if (count > length) {
-                                    oldtextDto = oldtextDto.concat(Array(count - length).fill({}))
-                                } else {
-                                    oldtextDto = oldtextDto.slice(0, count)
-                                }
-                            } else if (value === 2) {
-                                if (count < length) {
-                                    oldtextDto = oldtextDto.slice(0, count)
-                                }
+                    <Radio.Group onChange={(e) => {
+                        let value = e.target.value
+                        let count = dynamicMaterialDTos.dynamicGroup.length
+                        let oldtextDto: PULLIN.TextDtoProps[] = JSON.parse(JSON.stringify(textDto))
+                        let length = oldtextDto.length
+                        if (value === 0) {
+                            oldtextDto = [textDto[0] || {}]
+                        } else if (value === 1) {
+                            if (count > length) {
+                                oldtextDto = oldtextDto.concat(Array(count - length).fill({}))
+                            } else {
+                                oldtextDto = oldtextDto.slice(0, count)
                             }
                             }
-                            form.setFieldsValue({ textDto: oldtextDto })
-                        }}
-                    />
+                        } else if (value === 2) {
+                            if (count < length) {
+                                oldtextDto = oldtextDto.slice(0, count)
+                            }
+                        }
+                        form.setFieldsValue({ textDto: oldtextDto })
+                    }}>
+                        {TextTypeList.filter(item => mediaType !== 1 ? item.value !== 4 : true).map(item => <Radio value={item.value} key={item.value}>{item.label}</Radio>)}
+                    </Radio.Group>
                 </Form.Item>
                 </Form.Item>
             </Card>
             </Card>
 
 
@@ -137,7 +136,7 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                             className="cardResetCss"
                             className="cardResetCss"
                             style={{ marginTop: 10, width: '100%' }}
                             style={{ marginTop: 10, width: '100%' }}
                             key={key}
                             key={key}
-                            extra={([3, 2].includes(type) && textDto?.length > 1) && <Popconfirm
+                            extra={([3, 2, 4].includes(type) && textDto?.length > 1) && <Popconfirm
                                 title="你确定删除当前文案组?"
                                 title="你确定删除当前文案组?"
                                 onConfirm={() => remove(name)}
                                 onConfirm={() => remove(name)}
                             >
                             >
@@ -165,13 +164,13 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                                             }
                                             }
                                         }]}
                                         }]}
                                     >
                                     >
-                                        <TextAideInput placeholder={'请输入' + item.label} style={{ width: 450 }} maxTextLength={item.restriction.textRestriction.maxLength} />
+                                        <TextAideInput placeholder={'请输入' + item.label} style={{ width: 580 }} maxTextLength={item.restriction.textRestriction.maxLength} />
                                     </Form.Item>
                                     </Form.Item>
                                 })}
                                 })}
                             </div>
                             </div>
                         </Card>
                         </Card>
                     ))}
                     ))}
-                    {[3, 2].includes(type) && !(type === 2 && textDto.length >= dynamicMaterialDTos.dynamicGroup.length) && <Form.Item style={{ marginTop: 10, marginBottom: 0 }}>
+                    {[3, 2, 4].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 type="dashed" onClick={() => add({})} block icon={<PlusOutlined />} disabled={type === 3 && textDto.length >= 30}>
                             新增
                             新增
                         </Button>
                         </Button>

+ 35 - 17
src/pages/launchSystemV3/tencentAdPutIn/create/addDynamic.tsx

@@ -9,7 +9,7 @@ import PageList from "./PageList";
 import { DispatchAddelivery } from ".";
 import { DispatchAddelivery } from ".";
 import { CheckOutlined, SearchOutlined } from "@ant-design/icons";
 import { CheckOutlined, SearchOutlined } from "@ant-design/icons";
 import WechatAccount from "../../components/WechatAccount";
 import WechatAccount from "../../components/WechatAccount";
-import { cartesianProduct, distributeArray } from "@/utils/utils";
+import { cartesianProduct, distributeArray, splitArrayIntoRandomChunks } from "@/utils/utils";
 import { columnsAddDynamic } from "./tableConfig";
 import { columnsAddDynamic } from "./tableConfig";
 import { useAjax } from "@/Hook/useAjax";
 import { useAjax } from "@/Hook/useAjax";
 import { createDynamicTaskApi } from "@/services/adqV3";
 import { createDynamicTaskApi } from "@/services/adqV3";
@@ -105,23 +105,21 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
         let newDynamicGroup: any = []
         let newDynamicGroup: any = []
         if (![910].includes(dynamic.creativeTemplateId)) {
         if (![910].includes(dynamic.creativeTemplateId)) {
             newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
             newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
-            if (newDynamicGroup.length > 0 && [0, 1, 2, 3].includes(textType)) {
+            if (newDynamicGroup.length > 0 && [0, 1, 2, 3, 4].includes(textType)) {
                 if (textType === 0) {
                 if (textType === 0) {
                     newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
                     newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
                 } else if (textType === 1) {
                 } else if (textType === 1) {
                     newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
                     newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
                 } else if (textType === 2) {
                 } else if (textType === 2) {
                     newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
                     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
-                            }
-                        })
-                    }
+                } else if ((textType === 3 && mediaType === 0) || (textType === 4 && mediaType === 1)) {
+                    newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
+                        let [dynamicGroup, textDtoData] = item
+                        return {
+                            ...dynamicGroup as any,
+                            textDto: textDtoData
+                        }
+                    })
                 }
                 }
             }
             }
         }
         }
@@ -131,11 +129,19 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
         if ((mediaType === 1 || mediaType === 2) && newDynamicGroup.length) {
         if ((mediaType === 1 || mediaType === 2) && newDynamicGroup.length) {
             let adLength = adData.length
             let adLength = adData.length
             if (mediaType === 1) {
             if (mediaType === 1) {
-                if (adLength > dynamicGroupLength) {
-                    message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
-                    return
+                if (textType === 4) {
+                    if (adLength > newDynamicGroup.length) {
+                        message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
+                        return
+                    }
+                    averageAdDynamicList = splitArrayIntoRandomChunks(newDynamicGroup, adLength)
+                } else {
+                    if (adLength > dynamicGroupLength) {
+                        message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
+                        return
+                    }
+                    averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
                 }
                 }
-                averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
             } else if (mediaType === 2) {
             } else if (mediaType === 2) {
                 if (adLength < dynamicGroupLength) {
                 if (adLength < dynamicGroupLength) {
                     message.error(`创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
                     message.error(`创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
@@ -154,7 +160,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                 adgroupsDto: ad,
                 adgroupsDto: ad,
                 dynamicDto: dynamic,                          // 创意信息
                 dynamicDto: dynamic,                          // 创意信息
                 averageAdDynamic,
                 averageAdDynamic,
-                rowSpan: mediaType === 1 ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
+                rowSpan: (mediaType === 1 && textType !== 4) ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
             }]
             }]
 
 
             let newData: any[] = []
             let newData: any[] = []
@@ -187,6 +193,17 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                                     rowSpan
                                     rowSpan
                                 })
                                 })
                             })
                             })
+                        } else if (textType === 4) {
+                            averageAdDynamic.forEach((aad: any, index: number) => {
+                                newData.push({
+                                    ...ad,
+                                    id: ad.id + '_' + index,
+                                    dynamicGroup: aad,
+                                    textDto: aad?.textDto,
+                                    rowSpan: index === 0 ? averageAdDynamic.length : 0,
+                                    isRowSpan: true
+                                })
+                            })
                         } else {
                         } else {
                             averageAdDynamic.forEach((aad: any, index: number) => {
                             averageAdDynamic.forEach((aad: any, index: number) => {
                                 newData.push({
                                 newData.push({
@@ -510,6 +527,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                     >
                     >
                         {adData.map(item => <Tabs.TabPane tab={item.adgroupId} key={item.adgroupId} />)}
                         {adData.map(item => <Tabs.TabPane tab={item.adgroupId} key={item.adgroupId} />)}
                     </Tabs>
                     </Tabs>
+                    {addelivery?.dynamicCreativesTextDTOS?.type === 4 && <Title level={5} style={{ color: 'red', fontSize: 12 }}>因为选择的是“创意组和文案叉乘打乱后分配”模式,会随机打乱,当前预览广告下创意内容会与实际建出来的创意有偏差,请以建出来的为准</Title>}
                     <div className={style.content} style={{ marginTop: 20 }}>
                     <div className={style.content} style={{ marginTop: 20 }}>
                         <Table
                         <Table
                             columns={columnsAddDynamic()}
                             columns={columnsAddDynamic()}

+ 37 - 18
src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx

@@ -16,11 +16,12 @@ import Dynamic from "./Dynamic"
 import Material from "./Material"
 import Material from "./Material"
 import MaterialText from "./MaterialText"
 import MaterialText from "./MaterialText"
 import PageList from "./PageList"
 import PageList from "./PageList"
-import { cartesianProduct, distributeArray, randomString } from "@/utils/utils"
+import { cartesianProduct, distributeArray, randomString, shuffleArray, splitArrayIntoRandomChunks } from "@/utils/utils"
 import columns from "./tableConfig"
 import columns from "./tableConfig"
 import SubmitModal from "./submitModal"
 import SubmitModal from "./submitModal"
 import { createAdgroupTaskApi, getSelectTaskDetailApi } from "@/services/adqV3"
 import { createAdgroupTaskApi, getSelectTaskDetailApi } from "@/services/adqV3"
 import WechatAccount from "../../components/WechatAccount"
 import WechatAccount from "../../components/WechatAccount"
+import Title from "antd/lib/typography/Title"
 
 
 export const DispatchAddelivery = React.createContext<PULLIN.DispatchAddelivery | null>(null);
 export const DispatchAddelivery = React.createContext<PULLIN.DispatchAddelivery | null>(null);
 
 
@@ -273,27 +274,24 @@ const Create: React.FC = () => {
         let newDynamicGroup: any = []
         let newDynamicGroup: any = []
         if (![910].includes(dynamic.creativeTemplateId)) {
         if (![910].includes(dynamic.creativeTemplateId)) {
             newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
             newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
-            if (newDynamicGroup.length > 0 && [0, 1, 2, 3].includes(textType)) {
+            if (newDynamicGroup.length > 0 && [0, 1, 2, 3, 4].includes(textType)) {
                 if (textType === 0) {
                 if (textType === 0) {
                     newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
                     newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
                 } else if (textType === 1) {
                 } else if (textType === 1) {
                     newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
                     newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
                 } else if (textType === 2) {
                 } else if (textType === 2) {
                     newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
                     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
-                            }
-                        })
-                    }
+                } else if ((textType === 3 && mediaType === 0) || (textType === 4 && mediaType === 1)) {
+                    newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
+                        let [dynamicGroup, textDtoData] = item
+                        return {
+                            ...dynamicGroup as any,
+                            textDto: textDtoData
+                        }
+                    })
                 }
                 }
             }
             }
         }
         }
-
         // 创意组平均分配到广告逻辑
         // 创意组平均分配到广告逻辑
         let averageAdDynamicList: any[] = []
         let averageAdDynamicList: any[] = []
         if ((mediaType === 1 || mediaType === 2) && newDynamicGroup.length) {
         if ((mediaType === 1 || mediaType === 2) && newDynamicGroup.length) {
@@ -308,11 +306,19 @@ const Create: React.FC = () => {
                 adLength += productList.length * targeting.length
                 adLength += productList.length * targeting.length
             })
             })
             if (mediaType === 1) {
             if (mediaType === 1) {
-                if (adLength > dynamicGroupLength) {
-                    message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
-                    return
+                if (textType === 4) {
+                    if (adLength > newDynamicGroup.length) {
+                        message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
+                        return
+                    }
+                    averageAdDynamicList = splitArrayIntoRandomChunks(newDynamicGroup, adLength)
+                } else {
+                    if (adLength > dynamicGroupLength) {
+                        message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
+                        return
+                    }
+                    averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
                 }
                 }
-                averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
             } else if (mediaType === 2) {
             } else if (mediaType === 2) {
                 if (adLength < dynamicGroupLength) {
                 if (adLength < dynamicGroupLength) {
                     message.error(`创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
                     message.error(`创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
@@ -349,7 +355,7 @@ const Create: React.FC = () => {
                     },
                     },
                     dynamicDto: dynamic,                          // 创意信息
                     dynamicDto: dynamic,                          // 创意信息
                     averageAdDynamic,
                     averageAdDynamic,
-                    rowSpan: mediaType === 1 ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
+                    rowSpan: (mediaType === 1 && textType !== 4) ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
                 }
                 }
                 if (marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') { // 营销载体
                 if (marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') { // 营销载体
                     dat.marketingCarrierDto = item?.wechatChannelList
                     dat.marketingCarrierDto = item?.wechatChannelList
@@ -391,6 +397,18 @@ const Create: React.FC = () => {
                                     isRowSpan: true
                                     isRowSpan: true
                                 })
                                 })
                             })
                             })
+                        } else if (textType === 4) {
+                            averageAdDynamic.forEach((aad: any, index: number) => {
+                                newData.push({
+                                    ...ad,
+                                    id: ad.id + '_' + index,
+                                    dynamicGroup: aad,
+                                    textDto: aad?.textDto,
+                                    adLength: data.length,
+                                    rowSpan: index === 0 ? averageAdDynamic.length : 0,
+                                    isRowSpan: true
+                                })
+                            })
                         } else {
                         } else {
                             averageAdDynamic.forEach((aad: any, index: number) => {
                             averageAdDynamic.forEach((aad: any, index: number) => {
                                 newData.push({
                                 newData.push({
@@ -824,6 +842,7 @@ const Create: React.FC = () => {
                 >
                 >
                     {accountCreateLogs.map(item => <Tabs.TabPane tab={item.accountId} key={item.accountId} />)}
                     {accountCreateLogs.map(item => <Tabs.TabPane tab={item.accountId} key={item.accountId} />)}
                 </Tabs>
                 </Tabs>
+                {addelivery?.dynamicCreativesTextDTOS?.type === 4 && <Title level={5} style={{ color: 'red', fontSize: 12 }}>因为选择的是“创意组和文案叉乘打乱后分配”模式,会随机打乱,当前预览广告下创意内容会与实际建出来的创意有偏差,请以建出来的为准</Title>}
                 <div className={style.content} style={{ marginTop: 20 }}>
                 <div className={style.content} style={{ marginTop: 20 }}>
                     <Table
                     <Table
                         columns={columns()}
                         columns={columns()}

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

@@ -143,4 +143,17 @@ export async function syncBatchCreativeApi(data: ADQV3.AccountAdgroupMapsProps)
         method: 'PUT',
         method: 'PUT',
         data
         data
     });
     });
+}
+
+
+/**
+ * 创意操作日志列表
+ * @param data 
+ * @returns 
+ */
+export async function dynamicCreativeLogApi(data: ADQV3.DynamicCreativeLogProps) {
+    return request(api + '/adq/dynamicCreative/operate/log', {
+        method: 'POST',
+        data
+    });
 }
 }

+ 34 - 0
src/utils/utils.ts

@@ -416,4 +416,38 @@ export function getRandomElements<T>(arr: T[], n: number): T[] {
   }
   }
 
 
   return result;
   return result;
+}
+
+
+/**
+ * 打乱数组
+ * @param array 
+ * @returns 
+ */
+export function shuffleArray<T>(array: T[]): T[] {
+  const shuffled = array.slice();
+  for (let i = shuffled.length - 1; i > 0; i--) {
+      const j = Math.floor(Math.random() * (i + 1));
+      [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
+  }
+  return shuffled;
+}
+
+function chunkArray<T>(array: T[], numChunks: number): T[][] {
+  const chunks: T[][] = Array.from({ length: numChunks }, () => []);
+  array.forEach((item, index) => {
+      chunks[index % numChunks].push(item);
+  });
+  return chunks;
+}
+
+/**
+ * 打乱随机分配数组
+ * @param array 
+ * @param numChunks 
+ * @returns 
+ */
+export function splitArrayIntoRandomChunks<T>(array: T[], numChunks: number): T[][] {
+  const shuffledArray = shuffleArray(array);
+  return chunkArray(shuffledArray, numChunks);
 }
 }