wjx 9 kuukautta sitten
vanhempi
commit
9867d9c50f

+ 4 - 9
src/pages/launchSystemV3/adqv3/creative/reviewDetails.tsx

@@ -25,23 +25,18 @@ const ReviewDetails: React.FC<Props> = ({ dynamic, visible, onClose }) => {
         if (dynamic && Object.keys(dynamic).length) {
             const { accountId, dynamicCreativeId } = dynamic
             getCreativeReviewDetail.run({ accountId, dynamicCreativeIds: [dynamicCreativeId] }).then(res => {
-                console.log('------------->', res)
                 if (res) {
                     let { elementResultList } = res[0]
-                    let cover: any, count = 0
+                    let count = 0
                     elementResultList.forEach((item: any) => {
-                        if (item?.elementName === '图片' && item?.componentInfo?.componentType === "VIDEO") {
-                            cover = item
-                        }
                         if (item?.componentInfo) {
                             count += 1
                         }
                     })
-
-                    setTableData(elementResultList?.map((item: any, index: number) => {
+                    setTableData(elementResultList?.map((item: any, index: number, array: any[]) => {
                         let params = { ...item, id: index, rowSpan: 1 }
-                        if (item?.componentInfo?.componentType === 'VIDEO' && cover) {
-                            if (item?.elementType === "VIDEO") {
+                        if (item?.componentInfo?.componentType === 'VIDEO') {
+                            if (array?.[index + 1]?.componentInfo?.componentId === item?.componentInfo?.componentId) {
                                 params.rowSpan = 2
                             } else {
                                 params.rowSpan = 0

+ 8 - 8
src/pages/launchSystemV3/adqv3/creative/tableConfig.tsx

@@ -195,7 +195,7 @@ function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle: (
             align: 'center',
             width: 120,
             render: (a: string) => {
-                return DELIVERY_MODE[a]
+                return DELIVERY_MODE[a as keyof typeof DELIVERY_MODE]
             }
         },
         {
@@ -205,7 +205,7 @@ function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle: (
             align: 'center',
             width: 120,
             render: (a: string) => {
-                return DYNAMIC_CREATIVE_TYPE[a]
+                return DYNAMIC_CREATIVE_TYPE[a as keyof typeof DYNAMIC_CREATIVE_TYPE]
             }
         },
         {
@@ -216,7 +216,7 @@ function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle: (
             width: 120,
             ellipsis: true,
             render: (a: string) => {
-                return creativeTemplate[a] || '--'
+                return creativeTemplate[a as keyof typeof creativeTemplate] || '--'
             }
         },
         {
@@ -248,7 +248,7 @@ export const tableConfigDetail = (): TableProps<any>['columns'] => {
                 if (value) {
                     let { componentId, componentType } = value
                     return <Space direction="vertical" size={0}>
-                        <Text style={{ fontSize: 12 }}>{ELEMENT_ENUM[componentType]}</Text>
+                        <Text style={{ fontSize: 12 }}>{ELEMENT_ENUM[componentType as keyof typeof ELEMENT_ENUM]}</Text>
                         <Text type="secondary" style={{ fontSize: 12 }}>{componentId}</Text>
                     </Space>
                 }
@@ -267,7 +267,7 @@ export const tableConfigDetail = (): TableProps<any>['columns'] => {
                 if (!records?.componentInfo) {
                     return null
                 }
-                return <span style={{ fontSize: 12 }}>{AD_STATUS[records?.componentInfo?.reviewStatus]}</span>
+                return <span style={{ fontSize: 12 }}>{AD_STATUS[records?.componentInfo?.reviewStatus  as keyof typeof AD_STATUS]}</span>
             }
         },
         {
@@ -302,7 +302,7 @@ export const tableConfigDetail = (): TableProps<any>['columns'] => {
             key: 'reviewStatus',
             width: 120,
             render: (value) => {
-                return <span style={{ fontSize: 12 }}>{AD_STATUS[value]}</span>
+                return <span style={{ fontSize: 12 }}>{AD_STATUS[value as keyof typeof AD_STATUS]}</span>
             }
         },
         {
@@ -321,7 +321,7 @@ export const tableConfigDetail = (): TableProps<any>['columns'] => {
             width: 125,
             render: (_, records) => {
                 let siteSetList = records?.elementRejectDetailInfo?.siteSetList
-                return siteSetList ? <Text style={{ fontSize: 12 }}>{siteSetList?.map((item: { siteSet: any }) => SITE_SET_ENUM[item?.siteSet])?.join(',')}</Text> : null
+                return siteSetList ? <Text style={{ fontSize: 12 }}>{siteSetList?.map((item: { siteSet: any }) => SITE_SET_ENUM[item?.siteSet as keyof typeof SITE_SET_ENUM])?.join(',')}</Text> : null
             }
         },
     ]
@@ -445,7 +445,7 @@ export const columnsLog = (): TableProps<any>['columns'] => [
                 '失败': 'error',
                 '执行中': 'warning'
             }
-            return <Badge status={obj[a]} text={a} />
+            return <Badge status={obj[a as keyof typeof obj] as any} text={a} />
         }
     },
     {

+ 10 - 6
src/pages/launchSystemV3/components/PageModal/index.tsx

@@ -179,7 +179,7 @@ const PageModal: React.FC<Props> = (props) => {
                     </Tooltip>
                 </Space>
             </Button>}
-            {![910].includes(creativeTemplateId) && <Text type="warning">{OPTION_ENUM[overrideCanvasHeadOption]}</Text>}
+            {![910].includes(creativeTemplateId) && <Text type="warning">{OPTION_ENUM[overrideCanvasHeadOption as keyof typeof OPTION_ENUM]}</Text>}
         </Space>}
         visible={visible}
         onCancel={() => { onClose && onClose() }}
@@ -347,12 +347,16 @@ const PageModal: React.FC<Props> = (props) => {
                         hideSelectAll: deliveryMode === "DELIVERY_MODE_COMPONENT",
                         onSelect: (record: any, selected: boolean) => {
                             let newData = JSON.parse(JSON.stringify(data))
-                            let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
-                            if (selected) {
-                                selectedRows.push({ ...record })
-                                newData[selectAdz - 1]['pageList'] = selectedRows
+                            if (([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT")) {
+                                let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
+                                if (selected) {
+                                    selectedRows.push({ ...record })
+                                    newData[selectAdz - 1]['pageList'] = selectedRows
+                                } else {
+                                    newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { pageId: number }) => item.pageId !== record.pageId)
+                                }
                             } else {
-                                newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { pageId: number }) => item.pageId !== record.pageId)
+                                newData[selectAdz - 1]['pageList'] = [record]
                             }
                             setData([...newData])
                         },

+ 344 - 0
src/pages/launchSystemV3/components/PageModal/indexOfficial.tsx

@@ -0,0 +1,344 @@
+
+import { useAjax } from "@/Hook/useAjax"
+import { CheckOutlined, CloseOutlined, QuestionCircleOutlined, SyncOutlined } from "@ant-design/icons"
+import { Button, Checkbox, Input, message, Modal, Select, Space, Table, Tooltip, Typography } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../GoodsModal/index.less'
+import columns from "./tableConfig"
+import { getAdqLandingPageOfficialListApi, getWXDownPageAuthInfoListApi } from "@/services/adqV3/global"
+import New1Radio from "../New1Radio"
+const { Title, Text } = Typography;
+
+/**
+ * 获取adq落地页
+ * @returns 
+ */
+interface Props {
+    visible?: boolean,
+    onClose?: () => void,
+    onChange?: (value: { data: PULLIN.AccountCreateLogsProps[], landingPageType: 0 | 1 }) => void,
+    adgroups: any
+    dynamic: any
+    data: PULLIN.AccountCreateLogsProps[]
+}
+const PageOfficialModal: React.FC<Props> = (props) => {
+
+    /*************************/
+    const { visible, onClose, data: data1, adgroups, onChange, dynamic } = props
+    const { marketingGoal, marketingAssetOuterSpec: { marketingTargetType }, marketingCarrierType, siteSet } = adgroups
+    const { creativeTemplateId, deliveryMode, creativeComponents: { mainJumpInfo }, landingPageType } = dynamic
+    const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
+    const [data, setData] = useState<PULLIN.AccountCreateLogsProps[]>(data1 || [])
+    const [queryForm, setQueryForm] = useState<{ accountId?: number, pageName?: string, ownerUid?: number, pageSize: number, pageNum: number, isSqDownPage: boolean }>({ pageNum: 1, pageSize: 20, isSqDownPage: false })
+    const [loading, setLoading] = useState<boolean>(false)
+    const [wXDownPageAuthInfo, setWXDownPageAuthInfo] = useState<any>({})
+    const [tableData, setTableData] = useState<any>({})
+    const [pageAllocationType, setPageAllocationType] = useState<0 | 1>(landingPageType || 0)
+
+    const listAjax = useAjax((params) => getAdqLandingPageOfficialListApi(params))
+    /*************************/
+
+    useEffect(() => {
+        if (data?.length > 0) {
+            setQueryForm({ ...queryForm, pageNum: 1, accountId: data[selectAdz - 1].accountId })
+        }
+    }, [])
+
+    useEffect(() => {
+        if (data?.length > 0) {
+            setLoading(true)
+            let accountDataList = data.map(item => item.accountId)
+            let infoAjax = accountDataList.map(accountId => getWXDownPageAuthInfoListApi(accountId))
+            let newData: any = {}
+            Promise.all(infoAjax).then(res => {
+                res?.forEach((item, index) => {
+                    newData[accountDataList[index]] = item?.data || []
+                })
+                setWXDownPageAuthInfo(newData)
+                setLoading(false)
+            }).catch(() => setLoading(false))
+        }
+    }, [])
+
+    useEffect(() => {
+        if (queryForm?.accountId) {
+            getList()
+        }
+    }, [queryForm, marketingGoal, marketingTargetType, marketingCarrierType, siteSet, mainJumpInfo, creativeTemplateId])
+
+    // 获取落地页列表
+    const getList = () => {
+        let params: any = { ...queryForm, pageStatus: 'NORMAL', marketingGoal, marketingTargetType, marketingCarrierType, siteSet, pageType: 'PAGE_TYPE_OFFICIAL', creativeTemplateId: 311  }
+        if (params.isSqDownPage) {
+            if (!params?.ownerUid) {
+                setTableData({ total: 0, records: [] })
+                return
+            }
+        } else {
+            delete params?.ownerUid
+        }
+        delete params.isSqDownPage
+        listAjax.run(params).then(res => {
+            setTableData(res || {})
+        })
+    }
+
+    const handleOk = () => {
+        if (deliveryMode === "DELIVERY_MODE_COMPONENT" && !data?.every(item => item.pageList?.length === mainJumpInfo?.length)) {
+            message.error(`当前落地页数量不足,跳转类型选择了${mainJumpInfo.length}组,落地页应当选择${mainJumpInfo.length}组`)
+            return
+        }
+        if (data?.every(item => item.pageList)) {
+            onChange && onChange({ data, landingPageType: pageAllocationType })
+        } else {
+            message.error('还有账号未选择落地页!')
+        }
+    }
+
+    /** 设置选中广告主 */
+    const handleSelectAdz = (value: number, item: any) => {
+        if (value === selectAdz) {
+            return
+        }
+        let accountId = data[value - 1].accountId
+        setQueryForm({ ...queryForm, pageNum: 1, accountId, ownerUid: wXDownPageAuthInfo[accountId]?.[0]?.accountId })
+        setSelectAdz(value)
+    }
+
+    /** 表格选折 */
+    // const onChangeTable = (_: React.Key[], selectedRows: any) => {
+    //     let newData = JSON.parse(JSON.stringify(data))
+    //     newData[selectAdz - 1]['pageList'] = selectedRows
+    //     setData([...newData])
+    // }
+
+    /** 一键设置 */
+    const setOnekey = () => {
+        let pageName: string = data[selectAdz - 1]['pageList']?.[0]?.pageName
+        let newData: PULLIN.AccountCreateLogsProps[] = JSON.parse(JSON.stringify(data))
+        const hide = message.loading(`正在设置...`, 0, () => {
+            message.success('设置成功');
+        });
+        let filterData = newData?.filter(item => item.accountId !== data[selectAdz - 1].accountId)
+        let ajax = filterData?.map(item => {
+            let params: any = { ...queryForm, pageNum: 1, accountId: item.accountId, ownerUid: wXDownPageAuthInfo[item.accountId]?.[0]?.accountId, pageName, pageStatus: 'NORMAL', marketingGoal, marketingTargetType, marketingCarrierType, siteSet, pageType: 'PAGE_TYPE_OFFICIAL', creativeTemplateId }
+            if (!params.isSqDownPage) {
+                delete params?.ownerUid
+            }
+            delete params.isSqDownPage
+            return getAdqLandingPageOfficialListApi(params)
+        })
+        Promise.all(ajax).then(res => {
+            if (res && Array.isArray(res)) {
+                res.forEach((item: any, index) => {
+                    let records = item?.data?.records
+                    if (Array.isArray(records) && records?.length > 0) {
+                        let record = records[0]
+                        let filterD = filterData[index]
+                        newData = newData.map(item => {
+                            if (item.accountId.toString() === filterD.accountId.toString() && record) {
+                                return { ...item, pageList: [{ ...record, id: record.pageId }] }
+                            }
+                            return item
+                        })
+                    }
+                })
+                setData(newData)
+            }
+            message.success('设置完成');
+            hide()
+        })
+    }
+
+    return <Modal
+        title={<Space>
+            <strong>选择落地页</strong>
+            {data?.length > 1 && <Button style={{ padding: 0, margin: 0 }} disabled={!data[selectAdz - 1]['pageList']?.length} onClick={setOnekey} type="link" loading={listAjax.loading}>
+                <Space>
+                    <span style={{ fontSize: 12 }}>一键设置</span>
+                    <Tooltip color="#FFF" overlayInnerStyle={{ color: '#000' }} title="设置其它账号有相同名称的落地页为那个账号的落地页(需要落地页名称相同,否则不设置,注意只会根据当前条件去搜索,比如选择了被授权落地页,只会去被授权落地页里找对应落地页)">
+                        <QuestionCircleOutlined />
+                    </Tooltip>
+                </Space>
+            </Button>}
+            <Text type="warning">请确保账号开通了权限</Text>
+        </Space>}
+        visible={visible}
+        onCancel={() => { onClose && onClose() }}
+        onOk={handleOk}
+        width={1100}
+        className={`${style.SelectPackage} modalResetCss`}
+        bodyStyle={{ padding: '0 10px 0 10px' }}
+    >
+        {[910].includes(creativeTemplateId) && <div className={style.pageType}>
+            <Space size={12}>
+                <strong>落地页分配规则</strong>
+                <New1Radio
+                    data={[{ label: '全部相同', value: 0 }, { label: '平均分配', value: 1 }]}
+                    value={pageAllocationType}
+                    onChange={(e) => {
+                        setPageAllocationType(e)
+                    }}
+                />
+            </Space>
+        </div>}
+        <div className={style.content}>
+            <div className={style.left}>
+                <h4 className={style.title}>媒体账户</h4>
+                {data?.map((item, index) => (
+                    <div key={index} onClick={() => { if (!loading) handleSelectAdz(index + 1, item) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
+                        {item?.accountId}
+                        {data[index].pageList?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
+                    </div>
+                ))}
+            </div>
+            <div className={style.right}>
+                <Space style={{ marginBottom: 10 }}>
+                    <Checkbox
+                        onChange={(e) => {
+                            if (e.target.checked && wXDownPageAuthInfo && Object.keys(wXDownPageAuthInfo).length) {
+                                setQueryForm({ ...queryForm, pageNum: 1, ownerUid: wXDownPageAuthInfo[data[selectAdz - 1].accountId]?.[0]?.accountId, isSqDownPage: true })
+                            } else {
+                                setQueryForm({ ...queryForm, pageNum: 1, ownerUid: undefined, isSqDownPage: false })
+                            }
+                        }}
+                        checked={queryForm.isSqDownPage}
+                    ><span style={{ fontSize: 12 }}>被授权落地页</span></Checkbox>
+                    {queryForm.isSqDownPage && <Select
+                        placeholder='选择原生页授权方信息'
+                        style={{ width: 210 }}
+                        showSearch
+                        filterOption={(input: any, option: any) =>
+                            (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                        }
+                        allowClear
+                        onChange={(value) => {
+                            setQueryForm({ ...queryForm, pageNum: 1, ownerUid: value })
+                        }}
+                        value={queryForm?.ownerUid}
+                        dropdownMatchSelectWidth={false}
+                    >
+                        {wXDownPageAuthInfo?.[data[selectAdz - 1].accountId]?.map((item: { accountId: number; accountName: string }) => {
+                            return <Select.Option value={item.accountId} key={item.accountId}>{item.accountName}({item.accountId})</Select.Option>
+                        })}
+                    </Select>}
+                    <Input value={queryForm?.pageName} style={{ width: 150 }} allowClear placeholder='请输入落地页名称' onChange={(e) => setQueryForm({ ...queryForm, pageNum: 1, pageName: e.target.value })} />
+                    <Button style={{ padding: 0, margin: 0 }} icon={<SyncOutlined />} type='link' loading={listAjax?.loading} onClick={() => { listAjax?.refresh() }}><span style={{ fontSize: 12 }}>刷新</span></Button>
+                </Space>
+                <Table
+                    columns={columns(2)}
+                    dataSource={tableData?.records}
+                    size="small"
+                    loading={listAjax?.loading || loading}
+                    scroll={{ y: 300 }}
+                    bordered
+                    rowKey={'pageId'}
+                    pagination={{
+                        total: tableData?.total,
+                        defaultPageSize: 20,
+                        current: tableData?.current,
+                        pageSize: tableData?.size
+                    }}
+                    onChange={(pagination) => {
+                        const { current, pageSize } = pagination
+                        setQueryForm({ ...queryForm, pageNum: current as number, pageSize: pageSize as number || 20 })
+                    }}
+                    /**
+                     * canvasType
+                     * VIDEO
+                     * VIDEO_1280_960  横板1280*960
+                     * VIDEO_1280_720  横板1280*720
+                     * VERTICAL_VIDEO_NEW_916 9:16 视频
+                     * COMMON_PAGE 引用外层素材原生页
+                     * IMAGE
+                     */
+                    rowSelection={{
+                        type: ([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT") ? 'checkbox' : 'radio',
+                        getCheckboxProps: (record) => {
+                            let { disableCode } = record
+                            if (disableCode !== 0) {
+                                return {
+                                    disabled: true
+                                }
+                            }
+                            // 组件化创意
+                            if (deliveryMode === "DELIVERY_MODE_COMPONENT") {
+                                if (data[selectAdz - 1]?.pageList?.length >= mainJumpInfo?.length) {
+                                    return {
+                                        disabled: data[selectAdz - 1]?.pageList?.some((item: any) => item?.pageId === record?.pageId) ? false : true
+                                    }
+                                } else {
+                                    return {
+                                        disabled: false
+                                    }
+                                }
+                            }
+                            return {
+                                disabled: false
+                            }
+                        },
+                        selectedRowKeys: data[selectAdz - 1]?.pageList?.map((item: any) => item?.pageId),
+                        // onChange: onChangeTable,
+                        hideSelectAll: deliveryMode === "DELIVERY_MODE_COMPONENT",
+                        onSelect: (record: any, selected: boolean) => {
+                            let newData = JSON.parse(JSON.stringify(data))
+                            if (([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT")) {
+                                let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
+                                if (selected) {
+                                    selectedRows.push({ ...record })
+                                    newData[selectAdz - 1]['pageList'] = selectedRows
+                                } else {
+                                    newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { pageId: number }) => item.pageId !== record.pageId)
+                                }
+                            } else {
+                                newData[selectAdz - 1]['pageList'] = [record]
+                            }
+                            setData([...newData])
+                        },
+                        onSelectAll: (selected: boolean, _: any[], changeRows: { pageId: number }[]) => {
+                            let newData = JSON.parse(JSON.stringify(data))
+                            let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
+                            if (selected) {
+                                let newSelectAccData = [...selectedRows]
+                                changeRows.forEach((item: { pageId: number }) => {
+                                    let index = newSelectAccData.findIndex((ite: { pageId: number }) => ite.pageId === item.pageId)
+                                    if (index === -1) {
+                                        let data: any = { ...item }
+                                        newSelectAccData.push(data)
+                                    }
+                                })
+                                newData[selectAdz - 1]['pageList'] = newSelectAccData
+                            } else {
+                                newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { pageId: number }) => {
+                                    let index = changeRows.findIndex((ite: { pageId: number }) => ite.pageId === item.pageId)
+                                    if (index !== -1) {
+                                        return false
+                                    } else {
+                                        return true
+                                    }
+                                })
+                            }
+                            setData([...newData])
+                        }
+                    }}
+                />
+            </div>
+            <div className={style.center}>
+                <Title level={5}>已选:{data[selectAdz - 1]?.pageList?.length || 0}</Title>
+                <div className={style.select_content} style={{ height: 416 }}>
+                    {data[selectAdz - 1]?.pageList?.map((item: any) => <div key={item.pageId}>
+                        <Text ellipsis={{ tooltip: true }} className={style.marketingAssetName}>{item.pageName}</Text>
+                        <CloseOutlined className={style.close} onClick={() => {
+                            let newData: PULLIN.AccountCreateLogsProps[] = JSON.parse(JSON.stringify(data))
+                            newData[selectAdz - 1].pageList = newData[selectAdz - 1]?.pageList?.filter((i: any) => i?.pageId !== item.pageId)
+                            setData(newData)
+                        }} />
+                    </div>)}
+                </div>
+            </div>
+        </div>
+
+    </Modal>
+}
+
+export default React.memo(PageOfficialModal)

+ 74 - 29
src/pages/launchSystemV3/components/PageModal/tableConfig.tsx

@@ -1,34 +1,79 @@
 import { PageStatusEnum } from "@/services/launchAdq/enum"
-import { Badge, TableProps } from "antd"
+import { Badge, TableProps, Tag } from "antd"
 import React from "react"
 
-let columns = (): TableProps<any>['columns'] => [
-    {
-        title: '落地页ID',
-        dataIndex: 'pageId',
-        key: 'pageId',
-        align: 'center',
-        width: 100,
-        render: (a: string) => {
-            return <a>{a}</a>
-        }
-    },
-    {
-        title: '落地页名称',
-        dataIndex: 'pageName',
-        key: 'pageName',
-        ellipsis: true
-    },
-    {
-        title: '落地页状态',
-        dataIndex: 'pageStatus',
-        key: 'pageStatus',
-        align: 'center',
-        width: 90,
-        render: (a: string | number) => {
-            return <Badge status={a === 'NORMAL' ? "processing" : "error"} text={PageStatusEnum[a]} />
-        }
-    }
-]
+let columns = (type?: 1 | 2): TableProps<any>['columns'] => {
 
+    if (type === 2) {  // 灵鹊落地页
+        return [
+            {
+                title: '是否可用?',
+                dataIndex: 'disableCode',
+                key: 'disableCode',
+                width: 80,
+                fixed: 'left',
+                align: 'center',
+                render(value) {
+                    return <Tag style={{ fontSize: 12 }} color={value === 0 ? 'success' : 'error'}>{value === 0 ? '可用' : '不可用'}</Tag>
+                },
+            },
+            {
+                title: '落地页ID',
+                dataIndex: 'pageId',
+                key: 'pageId',
+                align: 'center',
+                width: 85
+            },
+            {
+                title: '落地页名称',
+                dataIndex: 'pageName',
+                key: 'pageName',
+                ellipsis: true,
+                width: 300
+            },
+            {
+                title: '落地页状态',
+                dataIndex: 'pageStatus',
+                key: 'pageStatus',
+                align: 'center',
+                width: 90,
+                render: (a: string | number) => {
+                    return <Badge status={a === 'NORMAL' ? "success" : a === 'DELETED' ? "error" : 'processing'} text={PageStatusEnum[a as keyof typeof PageStatusEnum]} />
+                }
+            },
+            {
+                title: '不可用原因',
+                dataIndex: 'disableMessage',
+                key: 'disableMessage',
+                ellipsis: true,
+                width: 200
+            },
+        ]
+    }
+    return [
+        {
+            title: '落地页ID',
+            dataIndex: 'pageId',
+            key: 'pageId',
+            align: 'center',
+            width: 90
+        },
+        {
+            title: '落地页名称',
+            dataIndex: 'pageName',
+            key: 'pageName',
+            ellipsis: true
+        },
+        {
+            title: '落地页状态',
+            dataIndex: 'pageStatus',
+            key: 'pageStatus',
+            align: 'center',
+            width: 90,
+            render: (a: string | number) => {
+                return <Badge status={a === 'NORMAL' ? "success" : a === 'DELETED' ? "error" : 'processing'} text={PageStatusEnum[a as keyof typeof PageStatusEnum]} />
+            }
+        }
+    ]
+}
 export default columns

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

@@ -395,7 +395,8 @@ export const pageSpecFieldConVert = {
 	PAGE_TYPE_WECHAT_OFFICIAL_ACCOUNT_DETAIL: 'wechatOfficialAccountDetailSpec',
 	PAGE_TYPE_H5_PROFILE: 'h5ProfileSpec',
 	PAGE_TYPE_H5: 'h5Spec',
-	PAGE_TYPE_WECHAT_CHANNELS_PROFILE: 'wechatChannelsProfileSpec'
+	PAGE_TYPE_WECHAT_CHANNELS_PROFILE: 'wechatChannelsProfileSpec',
+	PAGE_TYPE_OFFICIAL: 'officialSpec'
 }
 
 export const pageSpecFieldConVertUn = {
@@ -408,6 +409,7 @@ export const pageSpecFieldConVertUn = {
 	h5ProfileSpec: 'PAGE_TYPE_H5_PROFILE', // 品牌简介页
 	h5Spec: 'PAGE_TYPE_H5',  // 自定义落地页
 	wechatChannelsProfileSpec: 'PAGE_TYPE_WECHAT_CHANNELS_PROFILE',  // 视频号
+	officialSpec: 'PAGE_TYPE_OFFICIAL',  // 视频号
 }
 
 /** 落地页类型 */
@@ -422,7 +424,8 @@ export enum PAGE_TYPE_ENUM {
 	PAGE_TYPE_WECHAT_MINI_PROGRAM = "微信小程序",
 	PAGE_TYPE_XJ_QUICK = "蹊径性能版落地页",
 	PAGE_TYPE_WECHAT_CANVAS = "原生推广页",
-	PAGE_TYPE_APP_DEEP_LINK = "应用直达"
+	PAGE_TYPE_APP_DEEP_LINK = "应用直达",
+	PAGE_TYPE_OFFICIAL = "灵鹊落地页(官方落地页)"
 }
 
 /** 外显数据类型 */

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

@@ -33,25 +33,25 @@ const Ad: React.FC = () => {
             <div className={style.detail}>
                 <div className={style.detail_body}>
                     {(adgroups && Object.keys(adgroups).length > 0) ? <>
-                        <p style={{ fontWeight: 'bold', color: configuredStatus === 'AD_STATUS_NORMAL' ? '#52c41a' : '#FF4D4F' }}>广告状态:{AD_STATUS_ENUM[configuredStatus]}</p>
-                        <p>营销目的:{MARKETING_GOAL_ENUM[marketingGoal]}</p>
-                        <p style={{ fontWeight: 'bold', color: '#000' }}>推广产品类型:{MARKETING_TARGET_TYPE_ENUM[marketingAssetOuterSpec?.marketingTargetType]}</p>
-                        <p>营销载体类型:{MARKETING_CARRIER_TYPE_ENUM[marketingCarrierType]}</p>
+                        <p style={{ fontWeight: 'bold', color: configuredStatus === 'AD_STATUS_NORMAL' ? '#52c41a' : '#FF4D4F' }}>广告状态:{AD_STATUS_ENUM[configuredStatus as keyof typeof AD_STATUS_ENUM]}</p>
+                        <p>营销目的:{MARKETING_GOAL_ENUM[marketingGoal as keyof typeof MARKETING_GOAL_ENUM]}</p>
+                        <p style={{ fontWeight: 'bold', color: '#000' }}>推广产品类型:{MARKETING_TARGET_TYPE_ENUM[marketingAssetOuterSpec?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]}</p>
+                        <p>营销载体类型:{MARKETING_CARRIER_TYPE_ENUM[marketingCarrierType as keyof typeof MARKETING_CARRIER_TYPE_ENUM]}</p>
                         <p>版位选择:{automaticSiteEnabled ? '自动版位' : '选择特定版位'}</p>
-                        {!automaticSiteEnabled && <Typography.Paragraph className={style.tpP} style={{ marginBottom: 0 }} ellipsis={{ tooltip: true, rows: 2 }}>广告版位:{siteSet.map((item: string | number) => SITE_SET_ENUM[item]).toString()}</Typography.Paragraph>}
+                        {!automaticSiteEnabled && <Typography.Paragraph className={style.tpP} style={{ marginBottom: 0 }} ellipsis={{ tooltip: true, rows: 2 }}>广告版位:{siteSet.map((item: string | number) => SITE_SET_ENUM[item as keyof typeof SITE_SET_ENUM]).toString()}</Typography.Paragraph>}
                         <p>搜索场景扩量:{searchExpandTargetingSwitch === 'SEARCH_EXPAND_TARGETING_SWITCH_OPEN' ? '开启' : '关闭'}</p>
-                        <p>计费方式:{BID_MODE_ENUM[bidMode]}</p>
-                        <p>出价类型:{SMART_BID_TYPE_ENUM[smartBidType]}</p>
-                        <p>出价:{bidAmount}元/{optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}</p>
-                        {optimizationGoal && <p style={{ fontWeight: 'bold', color: '#000' }}>优化目标:{OPTIMIZATIONGOAL_ENUM[optimizationGoal]}</p>}
+                        <p>计费方式:{BID_MODE_ENUM[bidMode as keyof typeof BID_MODE_ENUM]}</p>
+                        <p>出价类型:{SMART_BID_TYPE_ENUM[smartBidType as keyof typeof SMART_BID_TYPE_ENUM]}</p>
+                        <p>出价:{bidAmount}元/{optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}</p>
+                        {optimizationGoal && <p style={{ fontWeight: 'bold', color: '#000' }}>优化目标:{OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM]}</p>}
                         {deepConversionSpec && <>
                             <p style={{ fontWeight: 'bold', color: '#000' }}>深度转化优化:开启</p>
-                            <p style={{ fontWeight: 'bold', color: '#000' }}>深度优化类型:{DEEP_CONVERSION_ENUM[deepConversionSpec?.deepConversionType]}</p>
+                            <p style={{ fontWeight: 'bold', color: '#000' }}>深度优化类型:{DEEP_CONVERSION_ENUM[deepConversionSpec?.deepConversionType as keyof typeof DEEP_CONVERSION_ENUM]}</p>
                             {deepConversionSpec.deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? <>
-                                <p style={{ fontWeight: 'bold', color: '#000' }}>深度优化目标:{OPTIMIZATIONGOAL_ENUM[deepConversionSpec.deepConversionBehaviorSpec.goal]}</p>
-                                <p style={{ fontWeight: 'bold', color: '#000' }}>深度目标出价:{deepConversionSpec.deepConversionBehaviorSpec.bidAmount}元/{OPTIMIZATIONGOAL_ENUM[deepConversionSpec.deepConversionBehaviorSpec.goal] || '优化目标'}</p>
+                                <p style={{ fontWeight: 'bold', color: '#000' }}>深度优化目标:{OPTIMIZATIONGOAL_ENUM[deepConversionSpec.deepConversionBehaviorSpec.goal as keyof typeof OPTIMIZATIONGOAL_ENUM]}</p>
+                                <p style={{ fontWeight: 'bold', color: '#000' }}>深度目标出价:{deepConversionSpec.deepConversionBehaviorSpec.bidAmount}元/{OPTIMIZATIONGOAL_ENUM[deepConversionSpec.deepConversionBehaviorSpec.goal as keyof typeof OPTIMIZATIONGOAL_ENUM] || '优化目标'}</p>
                             </> : <>
-                                <p style={{ fontWeight: 'bold', color: '#000' }}>深度优化目标:{GOAL_ROAS_ENUM[deepConversionSpec.deepConversionWorthSpec.goal]}</p>
+                                <p style={{ fontWeight: 'bold', color: '#000' }}>深度优化目标:{GOAL_ROAS_ENUM[deepConversionSpec.deepConversionWorthSpec.goal as keyof typeof GOAL_ROAS_ENUM]}</p>
                                 <p style={{ fontWeight: 'bold', color: '#000' }}>期望ROI:{deepConversionSpec.deepConversionWorthSpec.expectedRoi}</p>
                             </>}
                         </>}

+ 3 - 0
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeConversionAssistant.tsx

@@ -166,6 +166,9 @@ const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean }>
             actionButtonPageType = actionButton?.children?.page_type
             if (actionButtonPageType) {
                 actionButtonPageTypeEnumeration = (actionButtonPageType?.enumProperty?.enumeration as { value: string, description: string }[]).filter(item => item.value === pageSpecPageType).map(item => ({ label: item.description, value: item.value }))
+                if (pageSpecPageType === 'PAGE_TYPE_OFFICIAL' && actionButtonPageTypeEnumeration.length === 0) {
+                    actionButtonPageTypeEnumeration = (actionButtonPageType?.enumProperty?.enumeration as { value: string, description: string }[]).filter(item => item.value === 'PAGE_TYPE_WECHAT_FOCUS_DAILOG').map(item => ({ label: item.description, value: item.value }))
+                }
             }
 
             return <Form.Item style={{ marginBottom: 0 }}>

+ 56 - 16
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeTemplateContent.tsx

@@ -18,7 +18,8 @@ const CreativeTemplateContent: React.FC<{ automaticSiteEnabled: boolean }> = ({
     const pageType = Form.useWatch(['jumpInfo', 'pageType'], form)
     const pageSpec = Form.useWatch('pageSpec', form)
     const deliveryMode = Form.useWatch('deliveryMode', form)
-    const linkName = Form.useWatch(['textLink', 'value', 'linkName'], form)
+    const linkNameTypeOld = Form.useWatch(['textLink', 'value', 'linkNameType'], form)
+    const buttonTextOld = Form.useWatch(['actionButton', 'value', 'buttonText'], form)
     /******************************************/
 
     const content = useMemo(() => {
@@ -38,7 +39,7 @@ const CreativeTemplateContent: React.FC<{ automaticSiteEnabled: boolean }> = ({
                     let jump_info = creativeComponents[key]
                     jumpInfoNumber = jump_info?.arrayProperty?.maxNumber || 1
                     let jumpInfoPageType = jump_info.children.page_type
-                    pageTypeList = (jumpInfoPageType.enumProperty.enumeration as { value: string, description: string }[]).filter(item => !["PAGE_TYPE_WECHAT_CANVAS_MINI_PROGRAM", "PAGE_TYPE_H5", "PAGE_TYPE_WECHAT_SIMPLE_CANVAS", "PAGE_TYPE_APP_DEEP_LINK"].includes(item.value)).map(item => ({ label: item.description, value: item.value, disabled: "PAGE_TYPE_WECHAT_CANVAS" !== item.value }))
+                    pageTypeList = (jumpInfoPageType.enumProperty.enumeration as { value: string, description: string }[]).filter(item => !["PAGE_TYPE_WECHAT_CANVAS_MINI_PROGRAM", "PAGE_TYPE_H5", "PAGE_TYPE_WECHAT_SIMPLE_CANVAS", "PAGE_TYPE_APP_DEEP_LINK"].includes(item.value)).map(item => ({ label: item.description, value: item.value, disabled: !["PAGE_TYPE_WECHAT_CANVAS", "PAGE_TYPE_OFFICIAL"].includes(item.value) }))
                     break
             }
         })
@@ -70,26 +71,65 @@ const CreativeTemplateContent: React.FC<{ automaticSiteEnabled: boolean }> = ({
                                             name={[name, 'pageType']}
                                             style={{ marginBottom: 0 }}
                                             rules={[{ required: true, message: '请选择跳转类型!' }]}
+                                            help={'注意"灵鹊落地页(官方落地页)"该类型需要申请权限'}
+                                            validateStatus="warning"
                                         >
                                             <New1Radio
                                                 data={pageTypeList}
                                                 onChange={(pageType) => {
-                                                    form.setFieldsValue({ pageSpec: pageSpec.map((item: any) => ({ ...item, pageType })) })
-                                                    if (Object.keys(creativeComponents).includes('text_link')) {
-                                                        let textLink = creativeComponents['text_link']
-                                                        let linkNameType = textLink?.children?.link_name_type
-                                                        let linkNameEnumeration = (linkNameType?.enumProperty?.enumeration as { value: string, description: string }[]).map(item => ({ label: item.description, value: item.value + '_' + item.description }))
-                                                        form.setFieldsValue({
-                                                            textLink: {
-                                                                value: {
-                                                                    linkName: linkName || linkNameEnumeration?.[0]?.value,
-                                                                    jump_info: {
-                                                                        pageType: pageType
+                                                    let pageSpecNew: any = []
+                                                    if (pageType === 'PAGE_TYPE_WECHAT_CANVAS') {
+                                                        pageSpecNew = pageSpec.map(() => ({ pageType, overrideCanvasHeadOption: 'OPTION_CREATIVE_OVERRIDE_CANVAS' }))
+                                                    } else {
+                                                        pageSpecNew = pageSpec.map(() => ({ pageType }))
+                                                    }
+                                                    form.setFieldsValue({ pageSpec: pageSpecNew })
+                                                    let values: any = {}
+                                                    Object.keys(creativeComponents).forEach(key => {
+                                                        switch (key) {
+                                                            case 'text_link':// 朋友圈文字链
+                                                                let textLink = creativeComponents[key]
+                                                                let linkNameType = textLink?.children?.link_name_type
+                                                                let linkNameEnumeration = (linkNameType?.enumProperty?.enumeration as { value: string, description: string }[]).map(item => ({ label: item.description, value: item.value }))
+                                                                values.textLink = {
+                                                                    value: {
+                                                                        linkNameType: linkNameTypeOld || linkNameEnumeration?.[0]?.value,
+                                                                        jumpInfo: {
+                                                                            pageType: pageType
+                                                                        }
                                                                     }
                                                                 }
-                                                            }
-                                                        })
-                                                    }
+                                                                break
+                                                            case 'action_button':
+                                                                let actionButton = creativeComponents[key]
+                                                                let buttonText = actionButton.children.button_text
+                                                                let butttonTextEnumeration = (buttonText.enumProperty.enumeration as { value: string }[]).map(item => ({ label: item.value, value: item.value }))
+                                                                let enumeration = actionButton.children?.page_type?.enumProperty?.enumeration
+                                                                let jumpInfo: any = {}
+                                                                if (enumeration) {
+                                                                    if (enumeration.some((item: { value: any; }) => item.value === pageType)) {
+                                                                        jumpInfo = {
+                                                                            pageType: pageType
+                                                                        }
+                                                                    } else {
+                                                                        jumpInfo = {
+                                                                            pageType: 'PAGE_TYPE_WECHAT_FOCUS_DAILOG'
+                                                                        }
+                                                                    }
+                                                                }
+                                                                values.actionButton = {
+                                                                    value: {
+                                                                        buttonText: buttonTextOld || butttonTextEnumeration?.[0]?.value,
+                                                                        jumpInfo
+                                                                    }
+                                                                }
+                                                                if (actionButton.required) {
+                                                                    values.actionButtonShow = true
+                                                                }
+                                                                break
+                                                        }
+                                                    })
+                                                    form.setFieldsValue(values)
                                                 }}
                                             />
                                         </Form.Item>

+ 11 - 11
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/index.tsx

@@ -55,8 +55,8 @@ const Dynamic: React.FC = () => {
                     let creativeComponents = adcreativeTemplateStructAdpermits?.creativeComponents || []
                     let result = processData(creativeComponents);
 
-                    let newMaterialData = {};
-                    let newTextData = {};
+                    let newMaterialData: any = {};
+                    let newTextData: any = {};
 
                     Object.keys(result).forEach(key => {
                         let data = result[key]
@@ -91,12 +91,12 @@ const Dynamic: React.FC = () => {
         <div className={style.detail}>
             <div className={style.detail_body}>
                 {dynamicData && Object.keys(dynamicData).length > 0 && <>
-                    <p style={{ fontWeight: 'bold', color: configuredStatus === 'AD_STATUS_NORMAL' ? '#52c41a' : '#FF4D4F' }}>创意状态:{AD_STATUS_ENUM[configuredStatus]}</p>
+                    <p style={{ fontWeight: 'bold', color: configuredStatus === 'AD_STATUS_NORMAL' ? '#52c41a' : '#FF4D4F' }}>创意状态:{AD_STATUS_ENUM[configuredStatus as keyof typeof AD_STATUS_ENUM]}</p>
                     <p>创意名称:{dynamicCreativeName}</p>
-                    <p style={{ fontWeight: 'bold', color: '#000' }}>投放模式:{DELIVERY_MODE_ENUM[deliveryMode]}</p>
+                    <p style={{ fontWeight: 'bold', color: '#000' }}>投放模式:{DELIVERY_MODE_ENUM[deliveryMode as keyof typeof DELIVERY_MODE_ENUM]}</p>
                     <p>{(creativeTemplateAppellation || creativeTemplateId) && `创意形式:${creativeTemplateAppellation || creativeTemplateId}`}</p>
                     {brand?.length > 0 && <>
-                        <p style={{ fontWeight: 'bold', color: '#000' }}>品牌形象跳转:{PAGE_TYPE_ENUM[brand?.[0]?.value?.jumpInfo?.pageType]}</p>
+                        <p style={{ fontWeight: 'bold', color: '#000' }}>品牌形象跳转:{PAGE_TYPE_ENUM[brand?.[0]?.value?.jumpInfo?.pageType as keyof typeof PAGE_TYPE_ENUM]}</p>
                         {['PAGE_TYPE_H5_PROFILE'].includes(brand?.[0]?.value?.jumpInfo?.pageType) ? <>
                             {profileData ? <>
                                 <Space>
@@ -112,22 +112,22 @@ const Dynamic: React.FC = () => {
                     </>}
                     <p style={{ fontWeight: 'bold', color: '#000' }}>跳转类型:{mainJumpInfo?.map((item: any) => {
                         let pageSpec = item.value.pageSpec
-                        return PAGE_TYPE_ENUM[pageSpecFieldConVertUn[Object.keys(pageSpec)?.[0]]]
+                        return PAGE_TYPE_ENUM[pageSpecFieldConVertUn[Object.keys(pageSpec)?.[0] as keyof typeof pageSpecFieldConVertUn] as keyof typeof PAGE_TYPE_ENUM]
                     }).toString()}</p>
                     {textLink?.length > 0 && <>
                         <p style={{ fontWeight: 'bold', color: '#000' }}>朋友圈文字链:开启</p>
-                        <p>文字链文案:{TEXT_LINK_TYPE_ENUM[textLink?.[0]?.value?.linkNameType]}</p>
-                        {textLink?.[0]?.value?.jumpInfo?.pageType && <p>跳转落地页:{PAGE_TYPE_ENUM[textLink?.[0]?.value?.jumpInfo?.pageType]}</p>}
+                        <p>文字链文案:{TEXT_LINK_TYPE_ENUM[textLink?.[0]?.value?.linkNameType as keyof typeof TEXT_LINK_TYPE_ENUM]}</p>
+                        {textLink?.[0]?.value?.jumpInfo?.pageType && <p>跳转落地页:{PAGE_TYPE_ENUM[textLink?.[0]?.value?.jumpInfo?.pageType as keyof typeof PAGE_TYPE_ENUM]}</p>}
                     </>}
                     {actionButton?.length > 0 && <>
                         <p style={{ fontWeight: 'bold', color: '#000' }}>行动按钮:开启</p>
                         <p>按钮文案:{actionButton?.[0]?.value?.buttonText}</p>
-                        {actionButton?.[0]?.value?.jumpInfo?.pageType && <p>跳转落地页:{PAGE_TYPE_ENUM[actionButton?.[0]?.value?.jumpInfo?.pageType]}</p>}
+                        {actionButton?.[0]?.value?.jumpInfo?.pageType && <p>跳转落地页:{PAGE_TYPE_ENUM[actionButton?.[0]?.value?.jumpInfo?.pageType as keyof typeof PAGE_TYPE_ENUM]}</p>}
                     </>}
                     {showData?.length > 0 && <>
                         <p style={{ fontWeight: 'bold', color: '#000' }}>数据外显:开启</p>
-                        <p>数据类型:{CONVERSION_DATA_ENUM[showData?.[0]?.value?.conversionDataType]}</p>
-                        <p>转化行为:{CONVERSION_TARGET_ENUM[showData?.[0]?.value?.conversionTargetType]}</p>
+                        <p>数据类型:{CONVERSION_DATA_ENUM[showData?.[0]?.value?.conversionDataType as keyof typeof CONVERSION_DATA_ENUM]}</p>
+                        <p>转化行为:{CONVERSION_TARGET_ENUM[showData?.[0]?.value?.conversionTargetType as keyof typeof CONVERSION_TARGET_ENUM]}</p>
                     </>}
                 </>}
             </div>

+ 10 - 11
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/newDynamic.tsx

@@ -188,12 +188,11 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
         let creativeTemplateId = adcreativeTemplateStructAdpermits?.creativeTemplateId
         let creativeTemplateAppellation = adcreativeTemplateStructAdpermits?.creativeTemplateAppellation
         let result = processData(creativeComponents);
-        console.log(result);
-        // console.log(JSON.stringify(result));
+        console.log('result-->', result);
         setCreativeComponents(result)
 
-        let newMaterialData = {};
-        let newTextData = {};
+        let newMaterialData: { [x: string]: any } = {};
+        let newTextData: { [x: string]: any } = {};
 
         Object.keys(result).forEach(key => {
             let data = result[key]
@@ -300,7 +299,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
                     value: {
                         pageType: item.pageType,
                         pageSpec: {
-                            [pageSpecFieldConVert[item.pageType]]: {
+                            [pageSpecFieldConVert[item.pageType as keyof typeof pageSpecFieldConVert]]: {
                                 pageId: null,
                                 overrideCanvasHeadOption: item.overrideCanvasHeadOption
                             }
@@ -313,7 +312,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
         // 品牌形象
         if (jumpInfo) {
             let pageType = jumpInfo.pageType
-            let value = {
+            let value: { [x: string]: any } = {
                 jumpInfo
             }
             if (['PAGE_TYPE_H5_PROFILE'].includes(pageType)) {
@@ -330,16 +329,16 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
             let pageSpecData: any = {}
 
             if (pageType === 'PAGE_TYPE_H5_PROFILE') {
-                pageSpecData[pageSpecFieldConVert[pageType]] = {
+                pageSpecData[pageSpecFieldConVert[pageType as keyof typeof pageSpecFieldConVert]] = {
                     pageId: null
                 }
             } else if (pageType === 'PAGE_TYPE_H5') {
-                pageSpecData[pageSpecFieldConVert[pageType]] = {
+                pageSpecData[pageSpecFieldConVert[pageType as keyof typeof pageSpecFieldConVert]] = {
                     pageUrl: null,
                     mpaH5WildcardUrl: null
                 }
             } else if (pageType === 'PAGE_TYPE_WECHAT_OFFICIAL_ACCOUNT_DETAIL') {
-                pageSpecData[pageSpecFieldConVert[pageType]] = {
+                pageSpecData[pageSpecFieldConVert[pageType as keyof typeof pageSpecFieldConVert]] = {
                     appId: null
                 }
             }
@@ -423,7 +422,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
                     let { pageSpec } = item.value
                     let key = Object.keys(pageSpec)[0]
                     let pageSpecValue = pageSpec[key]
-                    return { ...pageSpecValue, pageType: pageSpecFieldConVertUn[key] }
+                    return { ...pageSpecValue, pageType: pageSpecFieldConVertUn[key as keyof typeof pageSpecFieldConVertUn] }
                 })
             }
 
@@ -508,7 +507,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
                                     setTimeout(() => { setIsUpdate(true) }, 50)
                                     setValue(undefined)
                                 }}>
-                                    {Object.keys(DELIVERY_MODE_ENUM).map(key => <Radio value={key} key={key}>{DELIVERY_MODE_ENUM[key]}</Radio>)}
+                                    {Object.keys(DELIVERY_MODE_ENUM).map(key => <Radio value={key} key={key}>{DELIVERY_MODE_ENUM[key as keyof typeof DELIVERY_MODE_ENUM]}</Radio>)}
                                 </Radio.Group>
                             </Form.Item>
                             {deliveryMode === 'DELIVERY_MODE_CUSTOMIZE' && <>

+ 38 - 4
src/pages/launchSystemV3/tencentAdPutIn/create/Material/index.tsx

@@ -2,17 +2,18 @@ import React, { useContext, useEffect, useState } from "react"
 import style from '../index.less'
 import styles from './index.less'
 import { DispatchAddelivery } from "..";
-import { Button, Empty, Typography } from "antd";
-import { FolderOpenOutlined, RedoOutlined } from "@ant-design/icons";
+import { Button, Empty, Tooltip, Typography } from "antd";
+import { FolderOpenOutlined, RedoOutlined, SyncOutlined } from "@ant-design/icons";
 import AddMaterial from "./addMaterial";
 import VideoNews from "@/pages/launchSystemNew/components/newsModal/videoNews";
+import { shuffleArray } from "@/utils/utils";
 const { Title } = Typography;
 
 const Material: React.FC<{ adData?: any[] }> = ({ adData }) => {
 
     /***************************************/
     const { materialData, addelivery, setAddelivery, clearData, accountCreateLogs } = useContext(DispatchAddelivery)!;
-    const { dynamic, dynamicMaterialDTos, mediaType, targeting, adgroups: { marketingAssetOuterSpec } } = addelivery
+    const { dynamic, dynamicMaterialDTos, mediaType, targeting, adgroups: { marketingAssetOuterSpec }, dynamicCreativesTextDTOS } = addelivery
     const { creativeTemplateId, deliveryMode } = dynamic
     const [adLength, setAdLength] = useState<number>(0)
 
@@ -46,9 +47,42 @@ const Material: React.FC<{ adData?: any[] }> = ({ adData }) => {
         }
     }, [accountCreateLogs, marketingAssetOuterSpec, adData])
 
+    const handleDisruption = () => {
+        let count = dynamicMaterialDTos?.dynamicGroup?.length
+        if (count) {
+            let shuffleArrayData = shuffleArray(Array(count).fill(undefined).map((_, index) => index))
+            let newDynamicGroup: any[] = [], newDynamicCreativesTextDetailDTOList: any[] = []
+            shuffleArrayData.forEach(index => {
+                newDynamicGroup.push(dynamicMaterialDTos.dynamicGroup[index])
+                if (dynamicCreativesTextDTOS?.type === 1 && dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList?.length) {
+                    newDynamicCreativesTextDetailDTOList.push(dynamicCreativesTextDTOS.dynamicCreativesTextDetailDTOList[index])
+                } else {
+                    newDynamicCreativesTextDetailDTOList = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList
+                }
+            })
+            setAddelivery({
+                ...addelivery,
+                dynamicMaterialDTos: {
+                    ...dynamicMaterialDTos,
+                    dynamicGroup: newDynamicGroup
+                },
+                dynamicCreativesTextDTOS: {
+                    ...dynamicCreativesTextDTOS,
+                    dynamicCreativesTextDetailDTOList: newDynamicCreativesTextDetailDTOList
+                }
+            })
+        }
+    }
+
     return <div className={`${style.settingsBody_content_row} ${style.row4}`}>
         <div className={style.title}>
-            <span>创意素材 <span className={style.selected}>已选 {dynamicMaterialDTos?.dynamicGroup?.length || 0}</span></span>
+            <span>
+                创意素材
+                <span className={style.selected}>已选 {dynamicMaterialDTos?.dynamicGroup?.length || 0}</span>
+                {dynamicMaterialDTos?.dynamicGroup?.length > 1 && <Tooltip title="随机打乱素材">
+                    <a style={{ marginLeft: 16, fontSize: 12 }} onClick={handleDisruption}><SyncOutlined /></a>
+                </Tooltip>}
+            </span>
             {(dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length > 0) ? <Button type="link" size="small" style={{ fontSize: 11, padding: 0 }} onClick={() => setAddelivery({ ...addelivery, dynamicMaterialDTos: [], dynamicCreativesTextDTOS: {} })}><RedoOutlined />清空</Button> : null}
         </div>
         <div className={style.detail}>

+ 20 - 25
src/pages/launchSystemV3/tencentAdPutIn/create/MaterialText/newText.tsx

@@ -38,7 +38,6 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
     /*************************************/
 
     const handleOk = (values: any) => {
-        console.log(values)
         const { type, textDto } = values
         onChange?.({
             type,
@@ -84,27 +83,24 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
             let dynamic = dynamicGroup[index]
             const keys = Object.keys(dynamic)
             if (deliveryMode === "DELIVERY_MODE_CUSTOMIZE") {
-                console.log('--->', keys)
-                return <>
-                    <div className={style.detail_body_m}>
-                        {(keys.includes('video_id') || keys.includes('short_video1')) ? <>
-                            <div className={style.video}>
-                                <VideoNews src={dynamic?.video_id?.url || dynamic?.short_video1?.url} style={{ width: 40, height: 30 }} />
-                                {dynamic?.cover_id && <div className={style.cover_image} style={{ marginLeft: 4, width: 40, height: 30, minWidth: 42 }}>
-                                    <img src={dynamic?.cover_id?.url} style={{ maxWidth: '96%', maxHeight: '96%' }}/>
-                                </div>}
-                            </div>
-                        </> : keys.includes('image_id') ? <>
-                            <div className={style.cover_image} style={{ width: 40, height: 30, minWidth: 42 }}>
-                                <img src={dynamic?.image_id?.url} />
-                            </div>
-                        </> : (keys.includes('image_list') || keys.includes('element_story')) ? <>
-                            {dynamic?.[keys.includes('image_list') ? 'image_list' : 'element_story']?.map((item: { url: string | undefined; }, index: undefined) => <div className={style.cover_image} key={index} style={{ width: 30, height: 24, minWidth: 32 }}>
-                                <img src={item?.url} />
-                            </div>)}
-                        </> : null}
-                    </div>
-                </>
+                return <div className={style.detail_body_m}>
+                    {(keys.includes('video_id') || keys.includes('short_video1')) ? <>
+                        <div className={style.video}>
+                            <VideoNews src={dynamic?.video_id?.url || dynamic?.short_video1?.url} style={{ width: 40, height: 30 }} />
+                            {dynamic?.cover_id && <div className={style.cover_image} style={{ marginLeft: 4, width: 40, height: 30, minWidth: 42 }}>
+                                <img src={dynamic?.cover_id?.url} style={{ maxWidth: '96%', maxHeight: '96%' }} />
+                            </div>}
+                        </div>
+                    </> : keys.includes('image_id') ? <>
+                        <div className={style.cover_image} style={{ width: 40, height: 30, minWidth: 42 }}>
+                            <img src={dynamic?.image_id?.url} />
+                        </div>
+                    </> : (keys.includes('image_list') || keys.includes('element_story')) ? <>
+                        {dynamic?.[keys.includes('image_list') ? 'image_list' : 'element_story']?.map((item: { url: string | undefined; }, index: undefined) => <div className={style.cover_image} key={index} style={{ width: 30, height: 24, minWidth: 32 }}>
+                            <img src={item?.url} />
+                        </div>)}
+                    </> : null}
+                </div>
             } else {
                 return <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap' }}>
                     {dynamic?.list?.map((item: any, index: number) => {
@@ -200,7 +196,7 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                 {(fields, { add, remove }) => (<>
                     {fields.map(({ key, name, ...restField }, num) => (
                         <Card
-                            title={<div style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
+                            title={type === 0 ? null : <div style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
                                 {type === 1 ? <strong style={{ fontSize: 14 }}>创意组{num + 1}</strong> : type === 0 ? null : <strong style={{ fontSize: 14 }}>文案组{num + 1}</strong>}
                                 {type === 1 && showMaterial(num)}
                             </div>}
@@ -214,7 +210,7 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                                 <DeleteOutlined style={{ color: 'red' }} />
                             </Popconfirm>}
                         >
-                            <div style={{ display: 'inline-flex', flexWrap: 'wrap', gap: 10, width: '100%' }}>
+                            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, width: '100%', flexDirection: 'column' }}>
                                 {textList.map(item => {
                                     return <Form.Item
                                         {...restField}
@@ -273,7 +269,6 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                         .map(line => line.trim()) // 去除每行的首尾空白
                         .filter(line => line !== ''); // 过滤掉空行
                     let fieldData = textList.find(item => item.label === addSTitle)
-                    console.log(valList, textDto, fieldData)
                     let count = dynamicMaterialDTos.dynamicGroup.length
                     if (type === 1) {
                         let len = 0

+ 74 - 3
src/pages/launchSystemV3/tencentAdPutIn/create/PageList/index.tsx

@@ -1,9 +1,11 @@
-import React, { useContext, useState } from 'react'
+import React, { useContext, useEffect, useState } from 'react'
 import style from '../index.less'
 import { Button, Empty, Typography, message } from 'antd'
 import { DispatchAddelivery } from '..';
 import { PlusCircleOutlined } from '@ant-design/icons';
 import PageModal from '@/pages/launchSystemV3/components/PageModal';
+import PageOfficialModal from '@/pages/launchSystemV3/components/PageModal/indexOfficial';
+import { PAGE_TYPE_ENUM } from '../../const';
 const { Title, Text } = Typography;
 
 /**
@@ -17,11 +19,20 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
     const { dynamic, adgroups, targeting } = addelivery
 
     const [addVisible, setAddVisible] = useState<boolean>(false)
+    const [addOfficialVisible, setAddOfficialVisible] = useState<boolean>(false)
+    const [pageType, setPageType] = useState<keyof typeof PAGE_TYPE_ENUM>()
     /********************************/
 
+    // 获取落地页类型
+    useEffect(() => {
+        if (dynamic?.creativeComponents?.mainJumpInfo?.length) {
+            setPageType(dynamic?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType)
+        }
+    }, [dynamic])
+
     return <div className={`${style.settingsBody_content_row} ${style.row6}`}>
         <div className={style.title}>
-            <span>落地页</span>
+            <span>{pageType === 'PAGE_TYPE_OFFICIAL' ? '灵鹊落地页(官方落地页)' : '原生推广页'}</span>
         </div>
         <div className={style.detail}>
             <div className={style.detail_body}>
@@ -36,7 +47,21 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
                 })}
             </div>
             <div className={style.detail_footer}>
-                <Button disabled={!(dynamic && Object.keys(dynamic)?.length > 0)} type="link" icon={<PlusCircleOutlined />} style={{ padding: 0, fontSize: 12 }} onClick={() => setAddVisible(true)}>添加</Button>
+                <Button
+                    disabled={!(dynamic && Object.keys(dynamic)?.length > 0)}
+                    type="link"
+                    icon={<PlusCircleOutlined />}
+                    style={{ padding: 0, fontSize: 12 }}
+                    onClick={() => {
+                        if (pageType === 'PAGE_TYPE_OFFICIAL') {
+                            setAddOfficialVisible(true)
+                        } else {
+                            setAddVisible(true)
+                        }
+                    }}
+                >
+                    添加
+                </Button>
             </div>
         </div>
 
@@ -84,6 +109,52 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
                 clearData()
             }}
         />}
+
+        {/* 灵鹊落地页 */}
+        {addOfficialVisible && <PageOfficialModal
+            data={accountCreateLogs}
+            adgroups={adgroups}
+            dynamic={dynamic}
+            visible={addOfficialVisible}
+            onClose={() => {
+                setAddOfficialVisible(false)
+            }}
+            onChange={({ data, landingPageType }) => {
+                if (landingPageType === 1) {
+                    if (adDataGroup && Object.keys(adDataGroup).length) {
+                        if (data.some(item => {
+                            let total = adDataGroup[item.accountId].length
+                            let pageLength = item.pageList.length
+                            if (total > pageLength) {
+                                message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
+                                return true
+                            }
+                            return false
+                        })) {
+                            return
+                        }
+                    } else {
+                        let targetingLength = targeting.length
+                        if (data.some(item => {
+                            let productListLength = item?.productList?.length || 1
+                            let total = targetingLength * productListLength
+                            let pageLength = item.pageList.length
+                            if (total > pageLength) {
+                                message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
+                                return true
+                            }
+                            return false
+                        })) {
+                            return
+                        }
+                    }
+                }
+                setAccountCreateLogs(data);
+                setAddelivery({ ...addelivery, dynamic: { ...dynamic, landingPageType } })
+                setAddOfficialVisible(false);
+                clearData()
+            }}
+        />}
     </div>
 }
 

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

@@ -55,7 +55,6 @@ const Create: React.FC = () => {
     /*******************************************/
 
     useEffect(() => {
-        console.log('addelivery---->', addelivery)
         // 获取账户组
         getGroupList.run()
         // 获取账户列表
@@ -187,7 +186,7 @@ const Create: React.FC = () => {
                         mediaType: mediaType || 0
                     })
                     setAccountCreateLogs(Object.keys(accountIdParamVOMap || {}).map(accountId => {
-                        const { productDTOS, wechatOfficialAccountsVO, pageList, userActionSetsList } = accountIdParamVOMap[accountId]
+                        const { productDTOS, wechatOfficialAccountsVO, pageList, landingPageVOS, userActionSetsList } = accountIdParamVOMap[accountId]
                         let data: PULLIN.AccountCreateLogsProps = {
                             accountId: Number(accountId),
                             productList: productDTOS
@@ -195,8 +194,8 @@ const Create: React.FC = () => {
                         if (wechatOfficialAccountsVO) {
                             data.wechatChannelList = [wechatOfficialAccountsVO]
                         }
-                        if (pageList) {
-                            data.pageList = pageList
+                        if (pageList || landingPageVOS) {
+                            data.pageList = pageList || landingPageVOS
                         }
                         if (userActionSetsList) {
                             data.userActionSetsList = userActionSetsList

+ 43 - 2
src/services/adqV3/global.ts

@@ -193,10 +193,17 @@ export async function getAdqLandingPageListApi(params: {
     pageTemplateId?: string;
     pageStatus?: string;
     ownerUid?: number;
+    creativeTemplateId?: number,
+    marketingCarrierId?: string,
+    marketingCarrierType?: string
+    marketingGoal?: string,
+    marketingSubGoal?: string,
+    marketingTargetType?: string,
+    siteSet?: string[],
 }) {
     Object.keys(params).forEach(key => {
-        if (!params[key]) {
-            delete params[key]
+        if (!params[key as keyof typeof params]) {
+            delete params[key as keyof typeof params]
         }
     })
     return request(api + '/adq/v3/marketingAssets/listWXDownPageToRemote', {
@@ -205,6 +212,40 @@ export async function getAdqLandingPageListApi(params: {
     });
 }
 
+/**
+ * 灵鹊落地页
+ * @param params 
+ * @returns 
+ */
+export async function getAdqLandingPageOfficialListApi(params: {
+    pageNum: number;
+    pageSize: number;
+    accountId?: number;
+    creativeTemplateId?: number,
+    marketingCarrierId?: string,
+    marketingCarrierType?: string
+    marketingGoal?: string,
+    marketingSubGoal?: string,
+    marketingTargetType?: string,
+    siteSet?: string[],
+    ownerUid?: number,
+    pageIds?: number[],
+    pageName?: string,
+    pageStatus?: string,
+    pageType?: string,
+    userId?: number
+}) {
+    Object.keys(params).forEach(key => {
+        if (!params[key as keyof typeof params]) {
+            delete params[key as keyof typeof params]
+        }
+    })
+    return request(api + '/adq/v3/marketingAssets/listDownPageToRemote', {
+        method: 'POST',
+        data: params,
+    });
+}
+
 /**
  * 按账号同步落地页
  * @param adAccountId 本地ID

+ 3 - 2
src/services/launchAdq/enum.ts

@@ -399,8 +399,9 @@ export enum SourceTypeEnum {
 }
 /**落地页状态*/
 export enum PageStatusEnum {
-  NORMAL = '有效',
-  DELETED = '删除',
+  NORMAL = '正常',
+  DELETED = '已删除',
+  PENDING = '审核中'
 }
 /**原生页类型*/
 export enum CanvasTypeEnum {