浏览代码

Merge branch 'develop' of http://git.zanxiangnet.com/wjx/ad-manage

wjx 4 月之前
父节点
当前提交
ea84911968

+ 6 - 0
config/routerConfig.ts

@@ -185,6 +185,12 @@ const launchSystemV3 = {
                     path: '/launchSystemV3/tencenTasset/copyWriting',
                     access: 'copyWriting',
                     component: './launchSystemV3/tencenTasset/copyWriting',
+                },
+                {
+                    name: '推广应用',
+                    path: '/launchSystemV3/tencenTasset/application',
+                    access: 'application',
+                    component: './launchSystemV3/tencenTasset/application',
                 }
             ],
         },

+ 14 - 3
src/pages/launchSystemV3/components/AdgroupTooltip/index.tsx

@@ -1,8 +1,9 @@
 import React from "react"
 import style from '../../tencentAdPutIn/create/index.less'
-import { AD_STATUS_ENUM, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../tencentAdPutIn/const"
+import { AD_STATUS_ENUM, BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../tencentAdPutIn/const"
 import { Typography } from "antd"
 import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook"
+import { ShowApplication } from "../../tencenTasset/application"
 
 
 interface Props {
@@ -20,7 +21,8 @@ const AdgroupTooltip: React.FC<Props> = ({ data: adgroups, taskType }) => {
     /************************************/
     const {
         marketingGoal, marketingSubGoal, marketingAssetOuterSpec, marketingCarrierType, automaticSiteEnabled, explorationStrategy, siteSet, prioritySiteSet, searchExpandTargetingSwitch, bidMode, smartBidType, bidScene, bidAmount, optimizationGoal,
-        deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName
+        deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName, promoteApplicationId, rtaId, rtaTargetId, bidAmountMin, bidAllocationMode, bidAmountMax,
+
     } = adgroups
     /************************************/
 
@@ -34,6 +36,7 @@ const AdgroupTooltip: React.FC<Props> = ({ data: adgroups, taskType }) => {
                 <p style={{ fontWeight: 'bold', color: '#000' }}>推广产品类型:{MARKETING_TARGET_TYPE_GAME_ENUM[marketingAssetOuterSpec?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM]}</p>
             </>}
             <p>营销载体类型:{MARKETING_CARRIER_TYPE_ENUM[marketingCarrierType as keyof typeof MARKETING_CARRIER_TYPE_ENUM]}</p>
+            {['MARKETING_CARRIER_TYPE_APP_ANDROID', 'MARKETING_CARRIER_TYPE_APP_IOS'].includes(marketingCarrierType) && <ShowApplication id={promoteApplicationId} />}
             <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 as keyof typeof SITE_SET_ENUM]).toString()}</Typography.Paragraph>}
             {automaticSiteEnabled && <>
@@ -41,11 +44,19 @@ const AdgroupTooltip: React.FC<Props> = ({ data: adgroups, taskType }) => {
                 {prioritySiteSet?.length > 0 && <Typography.Paragraph className={style.tpP} style={{ marginBottom: 0 }} ellipsis={{ tooltip: true, rows: 2 }}>优先探索版位:{prioritySiteSet.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>
+            {rtaId && <p>RTA ID:{rtaId}</p>}
+            {rtaTargetId && <p>策略 ID:{rtaTargetId}</p>}
             <p>计费方式:{BID_MODE_ENUM[bidMode as keyof typeof BID_MODE_ENUM]}</p>
             {taskType === 'GAME' ? <>
                 <p>出价场景:{BID_SCENE_NORMAL_ENUM[bidScene as keyof typeof BID_SCENE_NORMAL_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>
+            <p style={{ fontWeight: 'bold', color: '#000' }}>出价分配方式:{BID_ALL_OCATION_MODE.find(item => item.value === bidAllocationMode)?.label}</p>
+            {[2, 3].includes(bidAllocationMode)
+                ?
+                <p style={{ fontWeight: 'bold', color: '#000' }}>出价:{bidAmountMin}-{bidAmountMax} 元/{optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}</p>
+                :
+                <p style={{ fontWeight: 'bold', color: '#000' }}>出价:{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>

+ 1 - 1
src/pages/launchSystemV3/material/cloudNew/selectCloudNew.tsx

@@ -181,7 +181,7 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
             >
                 <div className={style.cloudNew}>
                     {/* 选择文件夹 */}
-                    <SelectFolder selectedKeys={queryParams?.folderId} onChange={(value) => { setQueryParams({ ...queryParams, folderId: value }) }} />
+                    <SelectFolder selectedKeys={queryParams?.folderId} onChange={(value) => { setQueryParams({ ...queryParams, folderId: value, pageNum: 1 }) }} />
                     <div className={style.material} style={{ height: '100%' }}>
                         <div className={style.operates}>
                             <div className={style.left_bts}>

+ 6 - 5
src/pages/launchSystemV3/tencenTasset/accountAssetSharing/addSubAccount.tsx

@@ -39,13 +39,13 @@ const AddSubAccount: React.FC<Props> = ({ authType, authMainAccountId, accountAs
 
     useEffect(() => {
         if (authType === 'conversion') {
-            getConversionInfo.run({ accountId: authMainAccountId, pageNum: 1, pageSize: 1000, keywords }).then(res => {
+            getConversionInfo.run({ accountId: authMainAccountId, pageNum: 1, pageSize: 20, keywords }).then(res => {
                 setTableData(res?.records?.filter((item: { ownerId: string }) => item.ownerId === authMainAccountId)?.map((item: { conversionName: string; optimizationGoal: string; deepBehaviorOptimizationGoal: string; deepWorthOptimizationGoal: string, conversionId: number, createSourceType: string }) => {
                     const deepGoal = item?.deepBehaviorOptimizationGoal ? OPTIMIZATIONGOAL_ENUM[item?.deepBehaviorOptimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : item?.deepWorthOptimizationGoal ? GOAL_ROAS_ENUM[item?.deepWorthOptimizationGoal as keyof typeof GOAL_ROAS_ENUM] : null
                     const cType = item?.createSourceType === 'SELF_CREATED' ? '自建转化' : item?.createSourceType === 'PLATFORM' ? '平台类转化' : ''
                     return {
                         label: item.conversionName + `(优化目标:${OPTIMIZATIONGOAL_ENUM[item?.optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM]}${deepGoal ? '/深度优化目标:' + deepGoal : ''}/上报类型:${cType})`, //+ '_' + (item?.deepBehaviorOptimizationGoal ? OPTIMIZATIONGOAL_ENUM[item?.deepBehaviorOptimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : item?.deepWorthOptimizationGoal ? GOAL_ROAS_ENUM[item?.deepWorthOptimizationGoal as keyof typeof GOAL_ROAS_ENUM] : null) + '_' + item?.createSourceType === 'SELF_CREATED' ? '自建转化' : item?.createSourceType === 'PLATFORM' ? '平台类转化' : '',
-                        value: item.conversionId,
+                        value: item.conversionId + "$" + item.conversionName,
                         name: item.conversionName
                     }
                 }) || [])
@@ -68,11 +68,12 @@ const AddSubAccount: React.FC<Props> = ({ authType, authMainAccountId, accountAs
             let params = { ...valid, accountAssetsGroupId: accountAssetsGroupId }
             if (authType === 'conversion') {
                 if (valid.assetId?.length) {
-                    params.assetsDTOS = valid.assetId.map((id: any) => {
-                        const assetName = tableData.find(item => item.value === id).name
-                        return { assetId: id, assetName }
+                    params.assetsDTOS = valid.assetId.map((conversion: string) => {
+                        const [assetId, assetName] = conversion.split('$')
+                        return { assetId, assetName }
                     })
                     delete valid.assetId
+                    delete params.assetId
                 }
                 batchAuthAccountAssetsGroupAccount.run(params).then(res => {
                     console.log(res)

+ 216 - 0
src/pages/launchSystemV3/tencenTasset/application/index.tsx

@@ -0,0 +1,216 @@
+import React, { useEffect, useState } from "react"
+import '../../tencentAdPutIn/index.less'
+import { Button, Card, Divider, Input, message, Select, Table, Typography } from "antd"
+import { PlusOutlined, SearchOutlined } from "@ant-design/icons"
+import { useAjax } from "@/Hook/useAjax"
+import { ApplicationTypeProps, delApplicationApi, getApplicationAllApi, getApplicationDetailApi, getApplicationListApi, GetApplicationListProps } from "@/services/adqV3/global"
+import { getErpUserAll } from "@/services/launchAdq/adq"
+import Modify from "./modify"
+import columns from "./tableConfig"
+const { Text, Paragraph } = Typography;
+
+/**
+ * 推广应用
+ * @returns 
+ */
+const Application: React.FC = () => {
+
+    /**********************************/
+    const [queryForm, setQueryForm] = useState<GetApplicationListProps>({ pageNum: 1, pageSize: 20 })
+    const [queryFormNew, setQueryFormNew] = useState<GetApplicationListProps>({ pageNum: 1, pageSize: 20 })
+    const [visible, setVisible] = useState<boolean>(false)
+    const [initialValues, setInitialValues] = useState<any>()
+
+    const getApplicationList = useAjax((params) => getApplicationListApi(params))
+    const allOfMember = useAjax(() => getErpUserAll())
+    const delApplication = useAjax((params) => delApplicationApi(params))
+    /**********************************/
+
+
+    useEffect(() => {
+        allOfMember.run()
+    }, [])
+
+    useEffect(() => {
+        getApplicationList.run(queryFormNew)
+    }, [queryFormNew])
+
+    const del = (id: number) => {
+        delApplication.run(id).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getApplicationList.refresh()
+            }
+        })
+    }
+
+    const update = (data: any) => {
+        setInitialValues(data)
+        setVisible(true)
+    }
+
+    return <Card
+        className="cardResetCss"
+        title={<div className="flexStart" style={{ gap: 8 }}>
+            <Select
+                showSearch
+                placeholder="请选择创建人"
+                onChange={(e) => setQueryForm({ ...queryForm, createBy: e, pageNum: 1 })}
+                filterOption={(input, option) =>
+                    (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }
+                allowClear
+                style={{ width: 140 }}
+                options={allOfMember?.data?.map((item: { userId: any; nickname: string }) => ({
+                    value: item.userId,
+                    label: item.nickname,
+                }))}
+            />
+            <Select
+                showSearch
+                placeholder="推广应用类型"
+                onChange={(e) => setQueryForm({ ...queryForm, type: e, pageNum: 1 })}
+                filterOption={(input, option) =>
+                    (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }
+                allowClear
+                style={{ width: 140 }}
+                options={[{ label: 'Ios', value: 'IOS' }, { label: 'Android', value: 'ANDROID' }]}
+            />
+            <Input style={{ width: 150 }} placeholder="推广应用名称" value={queryForm?.name} allowClear onChange={(e) => setQueryForm({ ...queryForm, name: e.target.value, pageNum: 1 })} />
+            <Input style={{ width: 150 }} placeholder="推广应用ID" value={queryForm?.applicationId} allowClear onChange={(e) => setQueryForm({ ...queryForm, applicationId: e.target.value, pageNum: 1 })} />
+
+            <Button type="primary" icon={<SearchOutlined />} onClick={() => setQueryFormNew({ ...queryForm })}>搜索</Button>
+            <Button type="primary" icon={<PlusOutlined />} onClick={() => { setVisible(true) }}>新增推广应用</Button>
+        </div>}
+    >
+        <Table
+            columns={columns(del, update)}
+            dataSource={getApplicationList.data?.records}
+            size="small"
+            loading={getApplicationList?.loading}
+            scroll={{ y: 600, x: 1100 }}
+            bordered
+            rowKey={'id'}
+            pagination={{
+                defaultPageSize: 20,
+                current: getApplicationList.data?.current || 1,
+                pageSize: getApplicationList.data?.size || 10,
+                total: getApplicationList.data?.total || 0
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryForm({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
+                setQueryFormNew({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
+            }}
+        />
+
+        {/* 新增 修改 */}
+        {visible && <Modify
+            visible={visible}
+            initialValues={initialValues}
+            onChange={() => {
+                setVisible(false)
+                setInitialValues(undefined)
+                getApplicationList.refresh()
+            }}
+            onClose={() => {
+                setVisible(false)
+                setInitialValues(undefined)
+            }}
+        />}
+    </Card>
+}
+
+export default Application
+
+
+
+/**
+ * 选择应用
+ * @param param0 
+ * @returns 
+ */
+export const SelectApplication: React.FC<{ value?: number, onChange?: (value?: number) => void, type?: ApplicationTypeProps }> = ({ value, onChange, type }) => {
+
+    /*******************************/
+    const [visible, setVisible] = useState<boolean>(false)
+    const getApplicationAll = useAjax((params) => getApplicationAllApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        getApplicationAll.run({ type })
+    }, [type])
+
+    return <>
+        <Select
+            showSearch
+            allowClear
+            placeholder="请选择应用"
+            filterOption={(input: any, option: any) => {
+                return option!.name?.toString().toLowerCase().includes(input.toLowerCase())
+            }}
+            style={{ width: 480 }}
+            dropdownRender={menu => <>
+                {menu}
+                <Divider style={{ margin: '8px 0' }} />
+                <div>
+                    <Button type="link" onClick={() => {
+                        window.location.href = '/#/launchSystemV3/tencenTasset/application'
+                    }}>前往管理</Button>
+                    <Button type="link" style={{ paddingLeft: 0, paddingRight: 0 }} onClick={() => {
+                        setVisible(true)
+                    }}>新增</Button>
+                    {/* <Link href="/#/launchSystemV3/tencenTasset/application" target="_blank">
+                    前往管理
+                </Link> */}
+                </div>
+            </>}
+            value={value}
+            onChange={(e) => onChange?.(e)}
+        >
+            {getApplicationAll?.data?.map((item: { id: number, applicationId: string, name: string }) => {
+                return <Select.Option value={item.id} key={item.id} name={item.name + `(${item.applicationId})`}><Text strong>{item.name}</Text><Text type="secondary">{`(${item.applicationId})`}</Text></Select.Option>
+            })}
+        </Select>
+
+        {/* 新增 */}
+        {visible && <Modify
+            visible={visible}
+            onClose={() => {
+                setVisible(false)
+            }}
+            onChange={() => {
+                setVisible(false)
+                getApplicationAll.refresh()
+            }}
+        />}
+    </>
+}
+
+
+/**
+ * 展示应用详情
+ * @param param0 
+ * @returns 
+ */
+export const ShowApplication: React.FC<{ id: number }> = ({ id }) => {
+
+    /*******************************/
+    const getApplicationDetail = useAjax((params) => getApplicationDetailApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        if (id) {
+            getApplicationDetail.run(id)
+        }
+    }, [id])
+
+    return <Paragraph style={{ fontSize: 12, wordBreak: 'break-all', marginBottom: 0 }} ellipsis={{ rows: 2 }}>
+        {getApplicationDetail.data ? <>
+            <Text strong>应用:{getApplicationDetail.data?.name}</Text><Text type="secondary">{`(${getApplicationDetail.data?.applicationId})`}</Text>
+        </> : id}
+    </Paragraph>
+}

+ 83 - 0
src/pages/launchSystemV3/tencenTasset/application/modify.tsx

@@ -0,0 +1,83 @@
+import { Form, Input, message, Modal, Radio } from "antd"
+import React from "react"
+import '../../tencentAdPutIn/index.less'
+import { useAjax } from "@/Hook/useAjax"
+import { addApplicationApi, updateApplicationApi } from "@/services/adqV3/global"
+
+interface Props {
+    initialValues?: any
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+
+const Modify: React.FC<Props> = ({ initialValues, visible, onClose, onChange }) => {
+
+    /****************************************/
+    const [form] = Form.useForm()
+
+    const addApplication = useAjax((params) => addApplicationApi(params))
+    const updateApplication = useAjax((params) => updateApplicationApi(params))
+    /****************************************/
+
+    const handleOk = () => {
+        form.validateFields().then(valid => {
+            console.log(valid)
+            let params = JSON.parse(JSON.stringify(valid))
+            if (params?.contentList) {
+                params.contentList = params?.contentList?.split(/[\n\s]+/ig).filter((item: any) => item)
+            }
+            if (initialValues?.id) {
+                params.id = initialValues.id
+                updateApplication.run(params).then(res => {
+                    if (res) {
+                        message.success('修改成功')
+                        onChange?.()
+                    }
+                })
+            } else {
+                addApplication.run(params).then(res => {
+                    if (res) {
+                        message.success('新增成功')
+                        onChange?.()
+                    }
+                })
+            }
+
+        })
+    }
+
+    return <Modal
+        title={<strong>{initialValues?.id ? '修改文案' : '新增文案'}</strong>}
+        open={visible}
+        onCancel={onClose}
+        className="modalResetCss"
+        onOk={handleOk}
+        confirmLoading={addApplication.loading || updateApplication.loading}
+    >
+        <Form
+            name="basicApplication"
+            form={form}
+            layout='vertical'
+            autoComplete="off"
+            initialValues={initialValues || {
+                type: 'ANDROID'
+            }}
+        >
+            <Form.Item label={<strong>应用名称</strong>} name="name" rules={[{ required: true, message: '请输入应用名称!' }]}>
+                <Input placeholder="请输入应用名称" allowClear />
+            </Form.Item>
+            <Form.Item label={<strong>应用ID</strong>} name="applicationId" rules={[{ required: true, message: '请输入应用ID!' }]}>
+                <Input placeholder="请输入应用ID" allowClear />
+            </Form.Item>
+            <Form.Item label={<strong>应用类型</strong>} name="type" rules={[{ required: true, message: '请选择应用类型!' }]}>
+                <Radio.Group buttonStyle="solid">
+                    <Radio.Button value="ANDROID">Android</Radio.Button>
+                    <Radio.Button value="IOS">Ios</Radio.Button>
+                </Radio.Group>
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(Modify)

+ 109 - 0
src/pages/launchSystemV3/tencenTasset/application/tableConfig.tsx

@@ -0,0 +1,109 @@
+import { Space, Popconfirm, TableProps } from "antd"
+import React from "react"
+
+
+const columns = (del: (id: number) => void, update: (data: any) => void): TableProps<any>['columns'] => {
+
+
+    const data: TableProps<any>['columns'] = [
+        {
+            title: '应用名称',
+            dataIndex: 'name',
+            key: 'name',
+            ellipsis: true,
+            width: 150,
+            fixed: 'left',
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '应用ID',
+            dataIndex: 'applicationId',
+            key: 'applicationId',
+            ellipsis: true,
+            width: 100,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '应用类型',
+            dataIndex: 'type',
+            key: 'type',
+            ellipsis: true,
+            width: 90,
+            align: 'center',
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a === 'ANDROID' ? 'Android' : 'Ios'}</span>
+            }
+        },
+        {
+            title: '创建人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 75,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 140,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '更新人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 75,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            align: 'center',
+            width: 140,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            fixed: 'right',
+            render: (_, b) => {
+                return <Space>
+                    <a style={{ fontSize: 12 }} onClick={() => { update(b) }}>修改</a>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del(b.id)}
+                        okText="是"
+                        cancelText="否"
+                    >
+                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        }
+    ]
+
+    return data
+}
+
+export default columns

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

@@ -126,7 +126,9 @@ export enum MARKETING_TARGET_TYPE_ENUM {
 	MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT = '微信公众号',
 	MARKETING_TARGET_TYPE_SHORT_DRAMA = '短剧',
 	MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT = '微信小程序',
-	MARKETING_TARGET_TYPE_WECHAT_WORK = '企业微信'
+	MARKETING_TARGET_TYPE_WECHAT_WORK = '企业微信',
+	MARKETING_TARGET_TYPE_APP_ANDROID = 'Android应用',
+	MARKETING_TARGET_TYPE_APP_IOS = 'iOS应用',
 }
 
 /** 推广游戏内容资产类型 */
@@ -138,7 +140,9 @@ export enum MARKETING_TARGET_TYPE_GAME_ENUM {
 export enum MARKETING_CARRIER_TYPE_ENUM {
 	MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT = '微信公众号',
 	MARKETING_CARRIER_TYPE_JUMP_PAGE = '页面跳转',
-	MARKETING_CARRIER_TYPE_WECHAT_MINI_GAME = '微信小游戏'
+	MARKETING_CARRIER_TYPE_WECHAT_MINI_GAME = '微信小游戏',
+	MARKETING_CARRIER_TYPE_APP_ANDROID = 'Android应用',
+	MARKETING_CARRIER_TYPE_APP_IOS = 'IOS应用',
 }
 
 /** 腾讯新闻流量场景定向 */
@@ -402,6 +406,9 @@ export const SelectTimeList = [
 	},
 ]
 
+/** 出价分配方式 */
+export const BID_ALL_OCATION_MODE = [{ label: '固定出价', value: 1 }, { label: '随机出价', value: 2 }, { label: '阶梯出价', value: 3 }]
+
 /** 深度优化类型 */
 export enum DEEP_CONVERSION_ENUM {
 	DEEP_CONVERSION_WORTH = '优化ROI',

+ 38 - 21
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsMarketingContent.tsx

@@ -5,13 +5,14 @@ import NewRadio from "@/pages/launchSystemV3/components/NewRadio"
 import { DispatchAd } from "./newCreateAd"
 import { GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, defaultSiteSet, marketingGoalGameList, marketingGoalList, marketingSubGoalGameList } from "../../const"
 import New1Radio from "@/pages/launchSystemV3/components/New1Radio"
-import { useAjax } from "@/Hook/useAjax"
 import { getOptimizationGoalPermissionsV3Api } from "@/services/adqV3/global"
 import { adRules } from "../../rules"
 import { QuestionCircleFilled } from "@ant-design/icons"
 import { SelectMiniProgramWechat } from "@/pages/launchSystemV3/tencenTasset/miniProgramWechat"
 import { SelectCorpWechat } from "@/pages/launchSystemV3/tencenTasset/corpWechat"
-import { SelectGame, SelectGameAppId } from "@/pages/launchSystemV3/tencenTasset/game"
+import { SelectGameAppId } from "@/pages/launchSystemV3/tencenTasset/game"
+import { useRequest } from "ahooks"
+import { SelectApplication } from "@/pages/launchSystemV3/tencenTasset/application"
 
 
 /**
@@ -19,7 +20,7 @@ import { SelectGame, SelectGameAppId } from "@/pages/launchSystemV3/tencenTasset
  * @param value 回填
  * @returns 
  */
-const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
+const AdgroupsMarketingContent: React.FC<{ accountIdList: number[], value?: any }> = ({ accountIdList, value }) => {
 
     /****************************************/
     const { form, OGPParams, setOGPparams, putInType } = useContext(DispatchAd)!;
@@ -45,7 +46,7 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
     const [deepConversionData, setDeepConversionData] = useState<PULLIN.DataType[]>([])
     const [isUpdateOptimizationGoal, setIsUpdateOptimizationGoal] = useState<boolean>(false)
 
-    const queryOptimizationGoalPermissions = useAjax((params) => getOptimizationGoalPermissionsV3Api(params))
+    const queryOptimizationGoalPermissions = useRequest((params) => getOptimizationGoalPermissionsV3Api(params), { manual: true, debounceInterval: 100 })
     /****************************************/
 
     /** 获取深度优化 出价和版位改变时查询 */
@@ -69,7 +70,7 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
                     marketingCarrierId: wxGameAppId
                 }
             }
-            queryOptimizationGoalPermissions.run({ ...obj, taskType: putInType })
+            queryOptimizationGoalPermissions.run({ ...obj, taskType: putInType, accountIdList })
         }
     }
 
@@ -77,12 +78,12 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
         if (OGPParams.marketingCarrierType && OGPParams.marketingTargetType) {
             getOptimizationGoalPermissions()
         }
-    }, [OGPParams, putInType, wxGameAppId])
+    }, [OGPParams, putInType, wxGameAppId, accountIdList])
 
     // 处理深度转化优化
     useEffect(() => {
-        if (optimizationGoal && queryOptimizationGoalPermissions?.data) {
-            let { deepBehaviorOptimizationGoalPermissionList, deepWorthOptimizationGoalPermissionList } = queryOptimizationGoalPermissions?.data
+        if (optimizationGoal && queryOptimizationGoalPermissions?.data?.data) {
+            let { deepBehaviorOptimizationGoalPermissionList, deepWorthOptimizationGoalPermissionList } = queryOptimizationGoalPermissions?.data?.data
             let behavior = deepBehaviorOptimizationGoalPermissionList?.find((item: { optimizationGoal: string }) => item.optimizationGoal === optimizationGoal)
             let worth = deepWorthOptimizationGoalPermissionList?.find((item: { optimizationGoal: string }) => item.optimizationGoal === optimizationGoal)
             let newBehaviorList = behavior?.deepBehaviorOptimizationGoalList || []
@@ -103,7 +104,7 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
                 })
             }
         }
-    }, [optimizationGoal, queryOptimizationGoalPermissions?.data])
+    }, [optimizationGoal, queryOptimizationGoalPermissions?.data?.data])
 
     // 选择营销目的触发
     useEffect(() => {
@@ -131,13 +132,12 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
         }
     }, [marketingTargetType, marketingTargetTypeList, OGPParams, value])
 
-    // // 营销载体类型
+    // 营销载体类型
     useEffect(() => {
         let newMarketingTargetTypeListList: PULLIN.DataType[] = []
         // 推广产品是公众号不展示 营销载体类型
         if (marketingTargetType) {
             let marketingTargetTypeRules: string[] = rules?.[marketingTargetType]?.MARKETING_SUB_GOAL_UNKNOWN
-            console.log(marketingTargetTypeRules)
             newMarketingTargetTypeListList = Object.keys(MARKETING_CARRIER_TYPE_ENUM).filter(key => marketingTargetTypeRules?.[key as keyof typeof marketingTargetTypeRules]).map(key => ({ label: MARKETING_CARRIER_TYPE_ENUM[key as keyof typeof MARKETING_CARRIER_TYPE_ENUM], value: key }))
         }
         setMarketingCarrierTypeList(newMarketingTargetTypeListList)
@@ -154,7 +154,7 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
 
     // 切换 其他 优化目标更新
     useEffect(() => {
-        let optimizationGoalPermissionList: string[] = queryOptimizationGoalPermissions?.data?.optimizationGoalPermissionList
+        let optimizationGoalPermissionList: string[] = queryOptimizationGoalPermissions?.data?.data?.optimizationGoalPermissionList
         if (isUpdateOptimizationGoal && optimizationGoalPermissionList?.length > 0 && !optimizationGoalPermissionList?.includes(optimizationGoal)) {
             if (putInType === 'NOVEL') {
                 form.setFieldsValue({ optimizationGoal: optimizationGoalPermissionList?.includes('OPTIMIZATIONGOAL_ECOMMERCE_ORDER') ? 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER' : optimizationGoalPermissionList?.includes('OPTIMIZATIONGOAL_PAGE_SCAN_CODE') ? 'OPTIMIZATIONGOAL_PAGE_SCAN_CODE' : undefined })
@@ -163,7 +163,7 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
             }
             setIsUpdateOptimizationGoal(false)
         }
-    }, [queryOptimizationGoalPermissions?.data?.optimizationGoalPermissionList, marketingGoal, marketingTargetType, marketingCarrierType, optimizationGoal, value, isUpdateOptimizationGoal, putInType])
+    }, [queryOptimizationGoalPermissions?.data?.data?.optimizationGoalPermissionList, marketingGoal, marketingTargetType, marketingCarrierType, optimizationGoal, value, isUpdateOptimizationGoal, putInType])
 
     const setIsUpdate = () => {
         if (bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') {
@@ -190,7 +190,15 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
         className="cardResetCss"
     >
         <Form.Item name="marketingGoal" label={<strong>营销目的</strong>} rules={[{ required: true, message: '请选择营销目的!' }]} hidden={putInType === 'GAME'}>
-            <MarketingGoal data={putInType === 'GAME' ? marketingGoalGameList : marketingGoalList} onChange={(e) => { setOGPparams({ ...OGPParams, marketingGoal: e as string }); setIsUpdate() }} />
+            <MarketingGoal data={putInType === 'GAME' ? marketingGoalGameList : marketingGoalList} onChange={(e) => {
+                if (e === 'MARKETING_GOAL_LEAD_RETENTION' && ['MARKETING_TARGET_TYPE_APP_ANDROID', 'MARKETING_TARGET_TYPE_APP_IOS'].includes(marketingTargetType)) {
+                    form.setFieldsValue({
+                        marketingTargetType: 'MARKETING_TARGET_TYPE_FICTION',
+                    })
+                }
+                setOGPparams({ ...OGPParams, marketingGoal: e as string }); 
+                setIsUpdate() 
+            }} />
         </Form.Item>
         {putInType === 'GAME' && <Form.Item name="marketingSubGoal" label={<strong>营销目的</strong>} rules={[{ required: true, message: '请选择营销目的!' }]}>
             <MarketingGoal data={marketingSubGoalGameList} onChange={(e) => { setOGPparams({ ...OGPParams, marketingSubGoal: e as string }); setIsUpdate() }} />
@@ -206,18 +214,27 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
                 setIsUpdate()
             }} />
         </Form.Item>}
-        {marketingTargetType === 'MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT' ? <Form.Item label={<strong>微信小程序</strong>} name='sysWechatAppId' rules={[{ required: true, message: '请选择微信小程序' }]}>
+
+        
+
+        {marketingCarrierTypeList?.length > 0 && <Form.Item name="marketingCarrierType" label={<strong>营销载体类型</strong>} rules={[{ required: true, message: '请选择营销载体类型!' }]}>
+            <New1Radio data={marketingCarrierTypeList} onChange={(e) => { setOGPparams({ ...OGPParams, marketingCarrierType: e }); setIsUpdate() }} />
+        </Form.Item>}
+
+        {marketingTargetType === 'MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT' ? <Form.Item name='sysWechatAppId' rules={[{ required: true, message: '请选择微信小程序' }]}>
             <SelectMiniProgramWechat />
-        </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_WORK' ? <Form.Item label={<strong>企业微信</strong>} name='sysCorpWechatId' rules={[{ required: true, message: '请选择企业微信' }]}>
+        </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_WORK' ? <Form.Item name='sysCorpWechatId' rules={[{ required: true, message: '请选择企业微信' }]}>
             <SelectCorpWechat />
-        </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? <Form.Item label={<strong>微信小游戏</strong>} name='wxGameAppId' rules={[{ required: true, message: '请选择微信小游戏' }]}>
+        </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? <Form.Item name='wxGameAppId' rules={[{ required: true, message: '请选择微信小游戏' }]}>
             <SelectGameAppId gameType="WXGAME" />
         </Form.Item> : null}
-        {marketingCarrierTypeList?.length > 0 && <Form.Item name="marketingCarrierType" label={<strong>营销载体类型</strong>} rules={[{ required: true, message: '请选择营销载体类型!' }]}>
-            <New1Radio data={marketingCarrierTypeList} onChange={(e) => { setOGPparams({ ...OGPParams, marketingCarrierType: e }); setIsUpdate() }} />
+
+        {['MARKETING_CARRIER_TYPE_APP_ANDROID', 'MARKETING_CARRIER_TYPE_APP_IOS'].includes(marketingCarrierType) && <Form.Item name='promoteApplicationId' rules={[{ required: true, message: '请选择推广应用' }]}>
+            <SelectApplication type={marketingCarrierType === 'MARKETING_CARRIER_TYPE_APP_ANDROID' ? 'ANDROID' : marketingCarrierType === 'MARKETING_CARRIER_TYPE_APP_IOS' ? 'IOS' : undefined} />
         </Form.Item>}
+
         {(bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') && <>
-            {['MARKETING_CARRIER_TYPE_JUMP_PAGE', 'MARKETING_CARRIER_TYPE_APP_IOS', 'MARKETING_CARRIER_TYPE_APP_QUICK_APP', 'MARKETING_CARRIER_TYPE_WECHAT_MINI_GAME'].includes(marketingCarrierType) && <Form.Item
+            {['MARKETING_CARRIER_TYPE_JUMP_PAGE', 'MARKETING_CARRIER_TYPE_APP_IOS', 'MARKETING_CARRIER_TYPE_APP_ANDROID', 'MARKETING_CARRIER_TYPE_APP_QUICK_APP', 'MARKETING_CARRIER_TYPE_WECHAT_MINI_GAME'].includes(marketingCarrierType) && <Form.Item
                 label={<Space>
                     <strong>转化归因新链路</strong>
                     <Tooltip title={<>
@@ -255,7 +272,7 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
                     placeholder='请选择'
                     loading={queryOptimizationGoalPermissions.loading}
                 >
-                    {queryOptimizationGoalPermissions?.data?.optimizationGoalPermissionList.filter((key: string) => key !== 'UNKNOWN').map((key: string) => {
+                    {queryOptimizationGoalPermissions?.data?.data?.optimizationGoalPermissionList.filter((key: string) => key !== 'UNKNOWN').map((key: string) => {
                         return <Select.Option value={key} key={key}>{OPTIMIZATIONGOAL_ENUM[key as keyof typeof OPTIMIZATIONGOAL_ENUM]}</Select.Option>
                     })}
                 </Select>

+ 26 - 3
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsPrice.tsx

@@ -2,7 +2,7 @@ import { Card, Form, Input, InputNumber, Space, Switch, Tooltip } from "antd"
 import React, { useContext } from "react"
 import { DispatchAd } from "./newCreateAd";
 import New1Radio from "@/pages/launchSystemV3/components/New1Radio";
-import { BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, OPTIMIZATIONGOAL_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
+import { BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, OPTIMIZATIONGOAL_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
 import { QuestionCircleFilled } from "@ant-design/icons";
 
 
@@ -17,6 +17,7 @@ const AdgroupsPrice: React.FC = () => {
 
     const siteSet = Form.useWatch('siteSet', form)
     const bidMode = Form.useWatch('bidMode', form)
+    const bidAllocationMode = Form.useWatch('bidAllocationMode', form)
     const optimizationGoal = Form.useWatch('optimizationGoal', form)
     const smartBidType = Form.useWatch('smartBidType', form)
     const bidScene = Form.useWatch('bidScene', form)
@@ -87,13 +88,35 @@ const AdgroupsPrice: React.FC = () => {
         </>}
 
         {(putInType === 'GAME' ? bidScene !== 'BID_SCENE_NORMAL_MAX' : smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC') && <>
-            <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
+            <Form.Item label={<strong>出价分配方式</strong>} name='bidAllocationMode' rules={[{ required: true, message: '请选择出价分配方式' }]}>
+                <New1Radio data={BID_ALL_OCATION_MODE} />
+            </Form.Item>
+
+            {bidAllocationMode === 1 ? <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
                 <Input
                     placeholder={`请输入价格`}
                     style={{ width: 480 }}
                     suffix={`元/${optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}`}
                 />
-            </Form.Item>
+            </Form.Item> : <Form.Item label={<strong>{bidAllocationMode === 2 ? '随机出价' : '阶梯出价'}</strong>} required>
+                <Space>
+                    <Form.Item name='bidAmountMin' rules={[{ required: true, message: '请输入价格最小值' }]} noStyle>
+                        <Input
+                            placeholder={`请输入价格最小值`}
+                            style={{ width: 200 }}
+                        />
+                    </Form.Item>
+                    <span>-</span>
+                    <Form.Item name='bidAmountMax' rules={[{ required: true, message: '请输入价格最大值' }]} noStyle>
+                        <Input
+                            placeholder={`请输入价格最大值`}
+                            style={{ width: 180 }}
+                        />
+                    </Form.Item>
+                    <span>元/{optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}</span>
+                </Space>
+            </Form.Item>}
+
 
             {deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? <>
                 <Form.Item label={<strong>深度目标出价</strong>} name={['deepConversionSpec', 'deepConversionBehaviorSpec', 'bidAmount']} rules={[{ required: true, message: '请输入深度目标出价' }]}>

+ 37 - 0
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsTarget.tsx

@@ -0,0 +1,37 @@
+import React from "react"
+import { Card, Form, Input, InputNumber } from "antd";
+import style from '../index.less'
+
+const AdgroupsTarget: React.FC = () => {
+
+    return <Card
+        title={<strong style={{ fontSize: 18 }}>定向</strong>}
+        className="cardResetCss"
+    >
+        <Form.Item
+            label={<strong>RTA 策略</strong>}
+        >
+            <Card bordered className="cardResetCss newCss" bodyStyle={{ padding: 0 }}>
+                <div className={style.newSpace}>
+                    <div className={style.newSpace_top}>
+                        <div className={style.newSpace_title}>RTA ID</div>
+                        <Form.Item name="rtaId" style={{ marginBottom: 0 }}>
+                            <InputNumber style={{ width: 480 }} controls={false} keyboard={false} placeholder="请输入RTA ID" />
+                        </Form.Item>
+                    </div>
+                </div>
+                <div className={style.newSpace}>
+                    <div className={style.newSpace_top}>
+                        <div className={style.newSpace_title}>策略 ID</div>
+                        <Form.Item name="rtaTargetId" style={{ marginBottom: 0 }}>
+                            <Input placeholder="请输入策略 ID" style={{ width: 480 }} />
+                        </Form.Item>
+                    </div>
+                </div>
+            </Card>
+        </Form.Item>
+    </Card>
+}
+
+
+export default React.memo(AdgroupsTarget)

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

@@ -4,12 +4,13 @@ import NewCreateAd from "./newCreateAd"
 import { DispatchAddelivery } from "..";
 import { Button, Modal, Typography } from "antd";
 import { EditOutlined } from "@ant-design/icons";
-import { AD_STATUS_ENUM, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
+import { AD_STATUS_ENUM, BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
 import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook";
 import { arraysHaveSameValues } from "@/utils/utils";
 import '../../index.less'
 import { ShowMiniProgramWechatDetail } from "@/pages/launchSystemV3/tencenTasset/miniProgramWechat";
 import { ShowGameAppIdDetail } from "@/pages/launchSystemV3/tencenTasset/game";
+import { ShowApplication } from "@/pages/launchSystemV3/tencenTasset/application";
 
 /**
  * 广告信息
@@ -22,7 +23,8 @@ const Ad: React.FC = () => {
     const { adgroups } = addelivery
     const {
         marketingGoal, marketingSubGoal, marketingAssetOuterSpec, marketingCarrierType, automaticSiteEnabled, explorationStrategy, siteSet, prioritySiteSet, searchExpandTargetingSwitch, bidMode, smartBidType, bidScene, bidAmount, optimizationGoal, isConversion, depthConversionEnabled,
-        deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName, sceneSpec, autoDerivedCreativeEnabled, sysWechatAppId, wxGameAppId
+        deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName, sceneSpec, autoDerivedCreativeEnabled, sysWechatAppId, wxGameAppId, promoteApplicationId, rtaId, rtaTargetId, bidAllocationMode,
+        bidAmountMin, bidAmountMax
     } = adgroups
     const [newVisible, setNewVisible] = useState<boolean>(false)
     /*****************************/
@@ -46,10 +48,11 @@ const Ad: React.FC = () => {
                         {marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT' ?
                             <ShowMiniProgramWechatDetail id={sysWechatAppId} /> :
                             marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ?
-                                <ShowGameAppIdDetail appId={wxGameAppId}/> :
+                                <ShowGameAppIdDetail appId={wxGameAppId} /> :
                                 null
                         }
                         <p>营销载体类型:{MARKETING_CARRIER_TYPE_ENUM[marketingCarrierType as keyof typeof MARKETING_CARRIER_TYPE_ENUM]}</p>
+                        {['MARKETING_CARRIER_TYPE_APP_ANDROID', 'MARKETING_CARRIER_TYPE_APP_IOS'].includes(marketingCarrierType) && <ShowApplication id={promoteApplicationId} />}
                         <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 as keyof typeof SITE_SET_ENUM]).toString()}</Typography.Paragraph>}
                         {automaticSiteEnabled && <>
@@ -57,11 +60,20 @@ const Ad: React.FC = () => {
                             {prioritySiteSet?.length > 0 && <Typography.Paragraph className={style.tpP} style={{ marginBottom: 0 }} ellipsis={{ tooltip: true, rows: 2 }}>优先探索版位:{prioritySiteSet.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>
+                        {rtaId && <p>RTA ID:{rtaId}</p>}
+                        {rtaTargetId && <p>策略 ID:{rtaTargetId}</p>}
                         <p>计费方式:{BID_MODE_ENUM[bidMode as keyof typeof BID_MODE_ENUM]}</p>
                         {putInType === 'GAME' ? <>
                             <p>出价场景:{BID_SCENE_NORMAL_ENUM[bidScene as keyof typeof BID_SCENE_NORMAL_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>
+                        <p style={{ fontWeight: 'bold', color: '#000' }}>出价分配方式:{BID_ALL_OCATION_MODE.find(item => item.value === bidAllocationMode)?.label}</p>
+                        {[2, 3].includes(bidAllocationMode)
+                            ?
+                            <p style={{ fontWeight: 'bold', color: '#000' }}>出价:{bidAmountMin}-{bidAmountMax} 元/{optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}</p>
+                            :
+                            <p style={{ fontWeight: 'bold', color: '#000' }}>出价:{bidAmount} 元/{optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}</p>
+                        }
+
                         {isConversion && <p style={{ fontWeight: 'bold', color: '#000' }}>转化:新链路转化</p>}
                         {optimizationGoal && <p style={{ fontWeight: 'bold', color: '#000' }}>优化目标:{OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM]}</p>}
                         {deepConversionSpec && <>
@@ -101,6 +113,7 @@ const Ad: React.FC = () => {
 
         {/* 新建广告 */}
         {newVisible && <NewCreateAd
+            accountIdList={accountCreateLogs.map(item => item.accountId)}
             putInType={putInType}
             value={addelivery.adgroups}
             visible={newVisible}

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

@@ -8,10 +8,12 @@ import AdgroupsPrice from "./adgroupsPrice"
 import AdgroupsAdSetting from "./adgroupsAdSetting"
 import moment from "moment"
 import { getTimeSeriesList } from "@/pages/launchSystemNew/adq/ad/const"
+import AdgroupsTarget from "./adgroupsTarget"
 
 export const DispatchAd = React.createContext<PULLIN.AdReactContent | null>(null);
 
 interface Props {
+    accountIdList: number[]
     putInType?: 'NOVEL' | 'GAME'
     value?: any,
     visible?: boolean
@@ -24,10 +26,11 @@ interface Props {
  * @param param0 
  * @returns 
  */
-const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose, putInType }) => {
+const NewCreateAd: React.FC<Props> = ({ accountIdList, value, visible, onChange, onClose, putInType }) => {
 
     /***********************************/
     const [form] = Form.useForm();
+    const marketingCarrierType = Form.useWatch('marketingCarrierType', form);
     // 深度优化副作用参数
     const [OGPParams, setOGPparams] = useState<PULLIN.OGPParamsProps>({ bidMode: 'BID_MODE_OCPM', siteSet: defaultSiteSet, automaticSiteEnabled: false, marketingGoal: putInType === 'NOVEL' ? defaultMarketingGoal : defaultGameMarketingGoal, marketingSubGoal: putInType === 'GAME' ? 'MARKETING_SUB_GOAL_MINI_GAME_NEW_CUSTOMER_GROWTH' : undefined })
     /***********************************/
@@ -198,6 +201,7 @@ const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose, putIn
                 deepConversionSpec: {
                     deepConversionType: 'DEEP_CONVERSION_WORTH'
                 },
+                bidAllocationMode: 1,
                 // bidAmount: 1000,
                 timeSeries: getTimeSeriesList(),
                 autoAcquisitionEnabled: false,
@@ -221,9 +225,11 @@ const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose, putIn
             <DispatchAd.Provider value={{ form, OGPParams, setOGPparams, putInType }}>
                 <Space direction="vertical" style={{ width: '100%' }}>
                     {/* 营销内容 */}
-                    <AdgroupsMarketingContent value={value} />
+                    <AdgroupsMarketingContent value={value} accountIdList={accountIdList} />
                     {/* 广告版位 */}
                     <AdgroupsSitSet />
+                    {/* 定向 */}
+                    {['MARKETING_CARRIER_TYPE_APP_ANDROID', 'MARKETING_CARRIER_TYPE_APP_IOS'].includes(marketingCarrierType) && <AdgroupsTarget />}
                     {/* 出价与预算 */}
                     <AdgroupsPrice />
                     {/* 广告设置 */}

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

@@ -247,8 +247,8 @@ const NewDynamic: React.FC<Props> = ({ putInType, value: newValue, visible, onCl
                 pageSpec: marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? [{
                     pageType: "PAGE_TYPE_WECHAT_MINI_GAME"
                 }] : [{
-                    pageType: 'PAGE_TYPE_WECHAT_CANVAS',
-                    overrideCanvasHeadOption: 'OPTION_CREATIVE_OVERRIDE_CANVAS'
+                    pageType: 'PAGE_TYPE_OFFICIAL',
+                    // overrideCanvasHeadOption: 'OPTION_CREATIVE_OVERRIDE_CANVAS'
                 }],
                 brand: undefined
             }
@@ -271,7 +271,8 @@ const NewDynamic: React.FC<Props> = ({ putInType, value: newValue, visible, onCl
                             value: {
                                 linkNameType: linkNameEnumeration?.[0]?.value,
                                 jumpInfo: {
-                                    pageType: marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? "PAGE_TYPE_WECHAT_MINI_GAME" : 'PAGE_TYPE_WECHAT_CANVAS'
+                                    // pageType: marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? "PAGE_TYPE_WECHAT_MINI_GAME" : 'PAGE_TYPE_WECHAT_CANVAS'
+                                    pageType: marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? "PAGE_TYPE_WECHAT_MINI_GAME" : 'PAGE_TYPE_OFFICIAL'
                                 }
                             }
                         }

+ 16 - 10
src/pages/launchSystemV3/tencentAdPutIn/create/MaterialText/newText.tsx

@@ -1,6 +1,6 @@
 import TextAideInput from "@/pages/launchSystemV3/components/TextAideInput"
 import { extractAndFilterBracketsContent, txtLength } from "@/utils/utils"
-import { Button, Card, Form, Modal, Popconfirm, Radio, Space, message } from "antd"
+import { Button, Card, Form, InputNumber, Modal, Popconfirm, Radio, Space, message } from "antd"
 import React, { useEffect, useState } from "react"
 import { TextTypeList } from "../../const"
 import { DeleteOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"
@@ -38,6 +38,8 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
     const [desVisible, setDesVisible] = useState<boolean>(false)
     const [addSTitle, setAddStitle] = useState<string>()
     const [newText, setNewText] = useState<{ description?: string[], title?: string[] }>({})
+    const [maxNumber, setMaxNumber] = useState<number>(1)
+    const [groupNumber, setGroupNumber] = useState<number>(1)
     /*************************************/
 
     const handleOk = (values: any) => {
@@ -68,6 +70,7 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                 }
                 return { label: content.description, restriction: content.restriction, value: item.name, required: item.required, arrayProperty: item?.arrayProperty }
             })
+            setMaxNumber(data?.[0]?.arrayProperty?.maxNumber)
             setNewText(newText)
             if (!(value && Object.keys(value).length)) { form.setFieldsValue({ textDto: [newText] }) }
             setTextList(data)
@@ -220,20 +223,23 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
     }
 
     return <Modal
-        title={<Space>
+        title={<Space align="center">
             <strong style={{ fontSize: 20 }}>创意文案</strong>
             {type !== 0 && <>
                 {textList.some(item => item.value === "description") && <a style={{ fontSize: 12 }} onClick={() => { setDesVisible(true); setAddStitle(textList.find(item => item.value === "description")?.label) }}>批量添加{textList.find(item => item.value === "description")?.label}</a>}
                 {textList.some(item => item.value === "title") && <a style={{ fontSize: 12 }} onClick={() => { setDesVisible(true); setAddStitle(textList.find(item => item.value === "description")?.label) }}>批量添加{textList.find(item => item.value === "description")?.label}</a>}
             </>}
-            {textList.some(item => item.value === "description") && <SelectCopyWriting
-                onClick={() => {
-                    setAddStitle(textList.find(item => item.value === "description")?.label)
-                }}
-                onChange={(valList) => {
-                    setText(valList)
-                }}
-            />}
+            {textList.some(item => item.value === "description") && <>
+                <SelectCopyWriting
+                    onClick={() => {
+                        setAddStitle(textList.find(item => item.value === "description")?.label)
+                    }}
+                    onChange={(valList) => {
+                        setText(valList)
+                    }}
+                />
+                {/* <Space><span style={{ fontSize: 12 }}>每组数量:</span><InputNumber max={maxNumber} size="small" value={groupNumber} onChange={(e) => setGroupNumber(e || 1)} /></Space> */}
+            </>}
         </Space>}
         open={visible}
         onCancel={onClose}

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

@@ -178,6 +178,7 @@ const Create: React.FC = () => {
                             setPutInType(() => taskType)
                             let beginDate = adgroupDTO.beginDate
                             let endDate = adgroupDTO.endDate
+                            adgroupDTO.bidAllocationMode = adgroupDTO?.bidAllocationMode || 1
                             if (adgroupDTO?.deepConversionSpec) {
                                 adgroupDTO.depthConversionEnabled = true
                             } else {
@@ -293,6 +294,7 @@ const Create: React.FC = () => {
                         addelivery.adgroups.endDate = moment().format('YYYY-MM-DD')
                         message.warning('请注意,检测投放结束日期小于今天,已自动改成今天,如需修改,请重新设置')
                     }
+                    addelivery.adgroups.bidAllocationMode = addelivery.adgroups?.bidAllocationMode || 1
                 }
                 setAddelivery({ ...addelivery })
                 setAccountCreateLogs(accountCreateLogs)

+ 1 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/submitModal.tsx

@@ -135,7 +135,7 @@ const SubmitModal: React.FC<Props> = (props) => {
             <Form.Item className="submit_pull" wrapperCol={{ span: 24 }}>
                 <Space>
                     <Button onClick={onClose}>取消</Button>
-                    <Button type="primary" htmlType="submit" className="modalResetCss">
+                    <Button type="primary" htmlType="submit" loading={ajax?.loading} className="modalResetCss">
                         确定
                     </Button>
                 </Space>

+ 6 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/tableConfig.tsx

@@ -4,7 +4,7 @@ const { Text, Title } = Typography;
 import style from './index.less'
 import VideoNews from "@/pages/launchSystemNew/components/newsModal/videoNews";
 import styles from './Material/index.less'
-import { OPTIMIZATIONGOAL_ENUM } from "../const";
+import { MARKETING_TARGET_TYPE_ENUM, OPTIMIZATIONGOAL_ENUM } from "../const";
 
 const columns = (): TableProps<any>['columns'] => {
 
@@ -61,6 +61,11 @@ const columns = (): TableProps<any>['columns'] => {
                                 <Text style={{ fontSize: 12 }}>推广产品:微信小游戏(appId:{b?.adgroupsDto?.wxGameAppId})</Text>
                                 <Text style={{ fontSize: 12 }}>转化归因:{b?.userActionSetsList ? b?.userActionSetsList.map((item: { name: any; }) => item.name).toString() : b?.conversionList ? b?.conversionList.map((item: { conversionName: any; conversionId: any; }) => `${item?.conversionName}(${item.conversionId})`).toString() : '暂未配置'}</Text>
                             </Space>
+                        } else if (['MARKETING_TARGET_TYPE_APP_ANDROID', 'MARKETING_TARGET_TYPE_APP_IOS'].includes(b.adgroupsDto?.marketingAssetOuterSpec?.marketingTargetType)) {
+                            return <Space size={0} direction="vertical">
+                                <Text style={{ fontSize: 12 }}>推广产品:{MARKETING_TARGET_TYPE_ENUM[b.adgroupsDto?.marketingAssetOuterSpec?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]}(applicationId:{b?.adgroupsDto?.promoteApplicationId})</Text>
+                                <Text style={{ fontSize: 12 }}>转化归因:{b?.userActionSetsList ? b?.userActionSetsList.map((item: { name: any; }) => item.name).toString() : b?.conversionList ? b?.conversionList.map((item: { conversionName: any; conversionId: any; }) => `${item?.conversionName}(${item.conversionId})`).toString() : '暂未配置'}</Text>
+                            </Space>
                         }
                         return 'ERROR,请联系管理员'
                     },

+ 88 - 0
src/services/adqV3/global.ts

@@ -901,4 +901,92 @@ export async function delsCopyWritingApi(ids: number[]) {
         method: 'DELETE',
         data: ids
     })
+}
+
+export type ApplicationTypeProps = 'IOS' | 'ANDROID'
+export interface GetApplicationListProps {
+    pageNum: number, 
+    pageSize: number,
+    createBy?: number,
+    applicationId?: string
+    name?: string
+    type?: ApplicationTypeProps
+}
+
+/**
+ * 查询本地推广应用列表
+ * @param data 
+ * @returns 
+ */
+export async function getApplicationListApi(data: GetApplicationListProps) {
+    return request(api + `/adq/promote/application/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 查询本地推广全部应用列表
+ * @param data 
+ * @returns 
+ */
+export async function getApplicationAllApi(data: Omit<GetApplicationListProps, 'pageNum' | 'pageSize'>) {
+    return request(api + `/adq/promote/application/listAll`, {
+        method: 'POST',
+        data
+    })
+}
+
+export interface AddApplicationProps {
+    applicationId: number,
+    name: string,
+    type: ApplicationTypeProps
+    id?: number
+}
+
+/**
+ * 新增推广应用
+ * @param data 
+ * @returns 
+ */
+export async function addApplicationApi(data: AddApplicationProps) {
+    return request(api + `/adq/promote/application/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+/**
+ * 修改推广应用
+ * @param data 
+ * @returns 
+ */
+export async function updateApplicationApi(data: Required<AddApplicationProps>) {
+    return request(api + `/adq/promote/application/update`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 删除本地推广应用
+ * @param id 
+ * @returns 
+ */
+export async function delApplicationApi(id: number) {
+    return request(api + `/adq/promote/application/delById/${id}`, {
+        method: 'DELETE',
+    })
+}
+
+/**
+ * 应用详情
+ * @param id 
+ * @returns 
+ */
+export async function getApplicationDetailApi(id: number) {
+    return request(api + `/adq/promote/application/getById/${id}`, {
+        method: 'GET'
+    })
 }