浏览代码

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

wjx 6 月之前
父节点
当前提交
875dffb8ff
共有 56 个文件被更改,包括 1600 次插入481 次删除
  1. 6 0
      config/routerConfig.ts
  2. 二进制
      public/image/newGameTest.png
  3. 二进制
      public/image/newGameTestSelected.png
  4. 二进制
      public/image/newGameUser.png
  5. 二进制
      public/image/newGameUserSelected.png
  6. 4 4
      src/pages/launchSystemV3/adMonitorListV3/TabAd.tsx
  7. 51 26
      src/pages/launchSystemV3/adqv3/ad/index.tsx
  8. 10 4
      src/pages/launchSystemV3/adqv3/ad/tableConfig.tsx
  9. 1 1
      src/pages/launchSystemV3/adqv3/ad/updateAd3.tsx
  10. 2 2
      src/pages/launchSystemV3/adqv3/creative/tableConfig.tsx
  11. 24 16
      src/pages/launchSystemV3/components/AdgroupTooltip/index.tsx
  12. 12 5
      src/pages/launchSystemV3/components/ConversionSelect/index.tsx
  13. 33 14
      src/pages/launchSystemV3/components/DynamicTooltip/index.tsx
  14. 34 14
      src/pages/launchSystemV3/components/TargetingTooltip/index.tsx
  15. 4 3
      src/pages/launchSystemV3/components/TextAideInput/index.tsx
  16. 1 1
      src/pages/launchSystemV3/material/cloudNew/selectCloudNew.tsx
  17. 2 2
      src/pages/launchSystemV3/tencenTasset/corpWechat/index.tsx
  18. 289 0
      src/pages/launchSystemV3/tencenTasset/game/index.tsx
  19. 75 0
      src/pages/launchSystemV3/tencenTasset/game/modify.tsx
  20. 106 0
      src/pages/launchSystemV3/tencenTasset/game/tableConfig.tsx
  21. 17 2
      src/pages/launchSystemV3/tencenTasset/targeting/index.tsx
  22. 13 2
      src/pages/launchSystemV3/tencenTasset/targeting/tableConfig.tsx
  23. 23 0
      src/pages/launchSystemV3/tencentAdPutIn/const.ts
  24. 2 2
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsAdSetting.tsx
  25. 72 32
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsMarketingContent.tsx
  26. 13 8
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsPrice.tsx
  27. 27 12
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/index.tsx
  28. 2 3
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/marketingGoal.tsx
  29. 13 10
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/newCreateAd.tsx
  30. 79 0
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/SelectBarrage.tsx
  31. 84 82
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/SelectLable.tsx
  32. 39 6
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeConversionAssistant.tsx
  33. 13 10
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeTemplateContent.tsx
  34. 9 3
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/index.tsx
  35. 53 13
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/newDynamic.tsx
  36. 2 1
      src/pages/launchSystemV3/tencentAdPutIn/create/Material/index.tsx
  37. 2 1
      src/pages/launchSystemV3/tencentAdPutIn/create/MaterialText/index.tsx
  38. 3 2
      src/pages/launchSystemV3/tencentAdPutIn/create/MaterialText/newText.tsx
  39. 4 1
      src/pages/launchSystemV3/tencentAdPutIn/create/PageList/index.tsx
  40. 4 3
      src/pages/launchSystemV3/tencentAdPutIn/create/Save/index.tsx
  41. 4 3
      src/pages/launchSystemV3/tencentAdPutIn/create/Save/saveUseImg.tsx
  42. 3 2
      src/pages/launchSystemV3/tencentAdPutIn/create/TacticsS/index.tsx
  43. 3 3
      src/pages/launchSystemV3/tencentAdPutIn/create/TacticsS/tableConfig.tsx
  44. 5 4
      src/pages/launchSystemV3/tencentAdPutIn/create/TacticsS/userTactics.tsx
  45. 80 8
      src/pages/launchSystemV3/tencentAdPutIn/create/Target/addTarget.tsx
  46. 3 1
      src/pages/launchSystemV3/tencentAdPutIn/create/Target/dataItem.tsx
  47. 5 2
      src/pages/launchSystemV3/tencentAdPutIn/create/Target/index.tsx
  48. 7 4
      src/pages/launchSystemV3/tencentAdPutIn/create/Target/selectTarget.tsx
  49. 3 2
      src/pages/launchSystemV3/tencentAdPutIn/create/Target/tableConfig.tsx
  50. 33 24
      src/pages/launchSystemV3/tencentAdPutIn/create/addDynamic.tsx
  51. 182 126
      src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx
  52. 11 0
      src/pages/launchSystemV3/tencentAdPutIn/create/tableConfig.tsx
  53. 27 6
      src/pages/launchSystemV3/tencentAdPutIn/rules.ts
  54. 14 9
      src/pages/launchSystemV3/tencentAdPutIn/taskList/tableConfig.tsx
  55. 10 2
      src/pages/launchSystemV3/tencentAdPutIn/typings.d.ts
  56. 82 0
      src/services/adqV3/global.ts

+ 6 - 0
config/routerConfig.ts

@@ -180,6 +180,12 @@ const launchSystemV3 = {
                     path: '/launchSystemV3/tencenTasset/corpWechat',
                     access: 'corpWechat',
                     component: './launchSystemV3/tencenTasset/corpWechat',
+                },
+                {
+                    name: '游戏库',
+                    path: '/launchSystemV3/tencenTasset/game',
+                    access: 'game',
+                    component: './launchSystemV3/tencenTasset/game',
                 }
             ],
         },

二进制
public/image/newGameTest.png


二进制
public/image/newGameTestSelected.png


二进制
public/image/newGameUser.png


二进制
public/image/newGameUserSelected.png


+ 4 - 4
src/pages/launchSystemV3/adMonitorListV3/TabAd.tsx

@@ -89,10 +89,10 @@ const TabAd: React.FC<Props> = ({ accountId, adgroupId }) => {
                             <Typography.Text ellipsis={{ tooltip: true }}>{adgroupName}</Typography.Text>
                         </div>
                     </Descriptions.Item>
-                    <Descriptions.Item labelStyle={{ width: 100 }} label="出价">{`${BidModeEnum[bidMode]} ${bidAmount}元/${bidMode === 'BID_MODE_CPM' ? '千次曝光' : bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[optimizationGoal]}`}</Descriptions.Item>
+                    <Descriptions.Item labelStyle={{ width: 100 }} label="出价">{`${BidModeEnum[bidMode as keyof typeof BidModeEnum]} ${bidAmount}元/${bidMode === 'BID_MODE_CPM' ? '千次曝光' : bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[optimizationGoal as keyof typeof OptimizationGoalEnum]}`}</Descriptions.Item>
                     <Descriptions.Item labelStyle={{ width: 100 }} label="广告ID">{adgroupId}</Descriptions.Item>
                     <Descriptions.Item labelStyle={{ width: 100 }} label="状态">
-                        {ADGROUP_STATUS[systemStatus] || '--'}
+                        {ADGROUP_STATUS[systemStatus as keyof typeof ADGROUP_STATUS] || '--'}
                     </Descriptions.Item>
                     <Descriptions.Item label="广告账号" labelStyle={{ width: 100 }}>{accountId}</Descriptions.Item>
                     <Descriptions.Item label="出价类型" labelStyle={{ width: 100 }}>{smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</Descriptions.Item>
@@ -102,7 +102,7 @@ const TabAd: React.FC<Props> = ({ accountId, adgroupId }) => {
                     <Descriptions.Item label="首日开始时间" labelStyle={{ width: 100 }}>{firstDayBeginTime}</Descriptions.Item>
                     <Descriptions.Item label="日预算" labelStyle={{ width: 100 }}>{dailyBudget}</Descriptions.Item>
                     <Descriptions.Item label="创意名称" labelStyle={{ width: 100 }}>{creativeName}</Descriptions.Item>
-                    <Descriptions.Item label="出价策略" labelStyle={{ width: 100 }}>{BidStrategyEnum[bidStrategy]}</Descriptions.Item>
+                    <Descriptions.Item label="出价策略" labelStyle={{ width: 100 }}>{BidStrategyEnum[bidStrategy as keyof typeof BidStrategyEnum]}</Descriptions.Item>
                 </Descriptions>
             </Spin>
         }
@@ -159,7 +159,7 @@ const TabAd: React.FC<Props> = ({ accountId, adgroupId }) => {
                     ajax={getAdgroupDetails}
                     dataSource={data[key]}
                     leftChild={<Space>
-                        <strong>{EWTypeEnum[key]}</strong>
+                        <strong>{EWTypeEnum[key as keyof typeof EWTypeEnum]}</strong>
                         {key === 'ADGROUP' && <SetEarlyWarnings accountId={accountId} adgroupId={adgroupId} onChange={() => getAdgroupDetails.refresh()} />}
                     </Space>}
                     loading={getAdgroupDetails?.loading}

+ 51 - 26
src/pages/launchSystemV3/adqv3/ad/index.tsx

@@ -10,17 +10,19 @@ import UpdateAd from "./updateAd";
 import TableData from "@/pages/launchSystemNew/components/TableData";
 import AddDynamic from "../../tencentAdPutIn/create/addDynamic";
 import { arraysHaveSameValues } from "@/utils/utils";
-import { MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, SITE_SET_ENUM } from "../../tencentAdPutIn/const";
+import { MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, SITE_SET_ENUM } from "../../tencentAdPutIn/const";
 import Log from "../components/log";
 import '../../tencentAdPutIn/index.less'
 import UserTactics from "../../tencentAdPutIn/create/TacticsS/userTactics";
 import UpdateAd3 from "./updateAd3";
+import { useLocalStorageState } from "ahooks";
 const { Text } = Typography;
 
 const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
 
     /*****************************************/
-    const [queryFrom, set_queryFrom] = useState<ADQV3.GetAdListProps>({ pageNum: 1, pageSize: 20, useType: 1 })
+    const [useType, setUseType] = useLocalStorageState<1 | 2>('AD_USETYPE', 1);
+    const [queryFrom, set_queryFrom] = useState<ADQV3.GetAdListProps>({ pageNum: 1, pageSize: 20, useType: useType || 1 })
     const [isClearSelect, setIsClearSelect] = useState(true)
     const [selectedRows, setSelectedRows] = useState<any[]>([])
     const [tactics, setTactics] = useState<any>()
@@ -36,7 +38,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
     /*****************************************/
 
     useEffect(() => {
-        getList({ pageNum: 1, pageSize: 20, useType: 1 })
+        getList({ pageNum: 1, pageSize: 20, useType: useType || 1 })
     }, [userId])
 
     // 获取列表
@@ -93,9 +95,12 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                     filterOption={(input: any, option: any) =>
                         (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                     }
-                    value={queryFrom.useType}
+                    value={useType}
                     onChange={(value: any) => {
-                        set_queryFrom({ ...queryFrom, useType: value })
+                        let params = { ...queryFrom, useType: value, pageNum: 1 }
+                        setUseType(value)
+                        set_queryFrom(params)
+                        getList(params)
                     }}
                 >
                     <Select.Option value={1}>小说</Select.Option>
@@ -232,7 +237,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
         </Row>
         <TableData
             isCard={false}
-            columns={() => tableConfig(() => getAdqV3AdList.refresh(), creativeHandle)}
+            columns={() => tableConfig(() => getAdqV3AdList.refresh(), creativeHandle, useType)}
             ajax={getAdqV3AdList}
             syncAjax={sync}
             fixed={{ left: 2, right: 5 }}
@@ -268,23 +273,40 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                             setUpdateDate({ visible: true, type: '删除' })
                         }}>删除</Button></Col>
                         <Col><Dropdown
-                            overlay={<Menu>
-                                <Menu.Item disabled={selectedRows.length === 0} onClick={() => {
-                                    setUpdateDate({ visible: true, type: '修改出价' })
-                                }}><span style={{ display: 'inline-block', width: 120 }}>修改出价</span></Menu.Item>
-                                <Menu.Item disabled={selectedRows.length === 0} onClick={() => {
-                                    setUpdateDate({ visible: true, type: '修改名称' })
-                                }}>修改名称</Menu.Item>
-                                <Menu.Item disabled={selectedRows.length === 0} onClick={() => {
-                                    setUpdateDate({ visible: true, type: '修改日限额' })
-                                }}>修改日限额</Menu.Item>
-                                <Menu.Item disabled={selectedRows.length === 0} onClick={() => {
-                                    setUpdateDate({ visible: true, type: '修改投放时间' })
-                                }}>修改投放日期</Menu.Item>
-                                <Menu.Item disabled={selectedRows.length === 0} onClick={() => {
-                                    setUpdateDate({ visible: true, type: '修改投放首日开始时间' })
-                                }}>修改投放首日开始时间</Menu.Item>
-                            </Menu>}
+                            menu={{
+                                items: [
+                                    {
+                                        label: <span style={{ display: 'inline-block', width: 120 }}>修改出价</span>,
+                                        key: '1',
+                                        disabled: selectedRows.length === 0,
+                                        onClick: () => { setUpdateDate({ visible: true, type: '修改出价' }) }
+                                    },
+                                    {
+                                        label: '修改名称',
+                                        key: '2',
+                                        disabled: selectedRows.length === 0,
+                                        onClick: () => { setUpdateDate({ visible: true, type: '修改名称' }) }
+                                    },
+                                    {
+                                        label: '修改日限额',
+                                        key: '3',
+                                        disabled: selectedRows.length === 0,
+                                        onClick: () => { setUpdateDate({ visible: true, type: '修改日限额' }) }
+                                    },
+                                    {
+                                        label: '修改投放日期',
+                                        key: '4',
+                                        disabled: selectedRows.length === 0,
+                                        onClick: () => { setUpdateDate({ visible: true, type: '修改投放时间' }) }
+                                    },
+                                    {
+                                        label: '修改投放首日开始时间',
+                                        key: '5',
+                                        disabled: selectedRows.length === 0,
+                                        onClick: () => { setUpdateDate({ visible: true, type: '修改投放首日开始时间' }) }
+                                    }
+                                ]
+                            }}
                             placement="bottomLeft"
                             arrow
                         >
@@ -303,6 +325,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                                 addDynamic()
                             }}
                             userId={userId}
+                            putInType={useType === 1 ? 'NOVEL' : 'GAME'}
                         /></Col>
                         <Col><Button type='primary' icon={<PlusOutlined />} disabled={selectedRows.length === 0} onClick={addDynamic}>添加创意</Button></Col>
                         <Col>
@@ -313,8 +336,8 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                                 {selectedRows?.length > 0 && <div style={{ maxWidth: '380px' }}>
                                     <Text type="danger" ellipsis={{ tooltip: true }} strong style={{ fontSize: 12 }}>
                                         {`当前广告选择:
-                                        营销目的:${MARKETING_GOAL_ENUM[selectedRows?.[0]?.marketingGoal as keyof typeof MARKETING_GOAL_ENUM]},
-                                        推广产品类型:${MARKETING_TARGET_TYPE_ENUM[selectedRows?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]},
+                                        营销目的:${useType === 2 ? MARKETING_SUB_GOAL_ENUM[selectedRows?.[0]?.marketingSubGoal as keyof typeof MARKETING_SUB_GOAL_ENUM] : MARKETING_GOAL_ENUM[selectedRows?.[0]?.marketingGoal as keyof typeof MARKETING_GOAL_ENUM]},
+                                        推广产品类型:${useType === 2 ? MARKETING_TARGET_TYPE_GAME_ENUM[selectedRows?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM] : MARKETING_TARGET_TYPE_ENUM[selectedRows?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]},
                                         营销载体类型:${MARKETING_CARRIER_TYPE_ENUM[selectedRows?.[0]?.marketingCarrierType as keyof typeof MARKETING_CARRIER_TYPE_ENUM]},
                                         版位选择:${selectedRows?.[0]?.automaticSiteEnabled ? '自动版位' : '选择特定版位'},
                                         ${!selectedRows?.[0]?.automaticSiteEnabled && `广告版位:${selectedRows?.[0]?.siteSet.map((item: string | number) => SITE_SET_ENUM[item as keyof typeof SITE_SET_ENUM]).toString()}`}
@@ -335,10 +358,11 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                 // hideSelectAll: handleType === 3,
                 getCheckboxProps: (record: any) => {
                     if (handleType === 2 && selectedRows?.length > 0) {
-                        const { siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled } = selectedRows[0]
+                        const { siteSet, marketingCarrierType, marketingGoal, marketingSubGoal, marketingTargetType, sceneSpec, automaticSiteEnabled } = selectedRows[0]
                         return {
                             disabled: record.isDeleted || !(
                                 record?.marketingGoal === marketingGoal &&  // 营销内容
+                                record?.marketingSubGoal === marketingSubGoal &&  // 二级营销内容
                                 record?.marketingCarrierType === marketingCarrierType && // 营销载体
                                 record?.marketingTargetType === marketingTargetType && // 推广产品
                                 record?.automaticSiteEnabled === automaticSiteEnabled &&   // 自动版位
@@ -435,6 +459,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
         {/* 新增创意 */}
         {addDynamicVisible && <AddDynamic
             adData={selectedRows}
+            putInType={useType === 1 ? 'NOVEL' : 'GAME'}
             visible={addDynamicVisible}
             onClose={() => {
                 setAddDynamicVisible(false)

+ 10 - 4
src/pages/launchSystemV3/adqv3/ad/tableConfig.tsx

@@ -7,8 +7,8 @@ import { ADGROUP_STATUS } from '../const'
 import SwitchStatus from './switchStatus'
 import TimeSeriesLook from '@/pages/launchSystemNew/adq/ad/timeSeriesLook'
 import CreativePreview from '../../adMonitorListV3/CreativePreview'
-import { BID_MODE_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM } from '../../tencentAdPutIn/const'
-function tableConfig(onChange: () => void, creativeHandle?: (id: number) => void): any {
+import { BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM } from '../../tencentAdPutIn/const'
+function tableConfig(onChange: () => void, creativeHandle?: (id: number) => void, useType?: 1 | 2): any {
     return [
         {
             title: '启停',
@@ -150,13 +150,16 @@ function tableConfig(onChange: () => void, creativeHandle?: (id: number) => void
             }
         },
         {
-            title: '出价策略',
+            title: useType === 2 ? '出价场景' : '出价策略',
             dataIndex: 'bidStrategy',
             key: 'bidStrategy',
             align: 'center',
             width: 70,
             ellipsis: true,
-            render: (a: string, b: { endDate: string }) => {
+            render: (a: string, b: { bidScene: string }) => {
+                if (useType === 2) {
+                    return BID_SCENE_NORMAL_ENUM[b?.bidScene as keyof typeof BID_SCENE_NORMAL_ENUM] || '--'
+                }
                 return BidStrategyEnum[a as keyof typeof BidStrategyEnum]
             }
         },
@@ -186,6 +189,9 @@ function tableConfig(onChange: () => void, creativeHandle?: (id: number) => void
             width: 80,
             ellipsis: true,
             render: (a: any) => {
+                if (useType === 2) {
+                    return MARKETING_TARGET_TYPE_GAME_ENUM[a as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM] || '--'
+                }
                 return MARKETING_TARGET_TYPE_ENUM[a as keyof typeof MARKETING_TARGET_TYPE_ENUM]
             }
         },

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

@@ -129,7 +129,7 @@ const UpdateAd3: React.FC<Props> = ({ visible, type, onClose, onChange, updateDa
     return <>
         <Modal
             title={<strong>{type}</strong>}
-            visible={visible}
+            open={visible}
             footer={null}
             onCancel={onClose}
             bodyStyle={{ padding: '0 0 40px', position: 'relative', borderRadius: '0 0 8px 8px' }}

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

@@ -7,7 +7,7 @@ import { ELEMENT_ENUM, SITE_SET_ENUM, creativeTemplate } from '../../tencentAdPu
 import { AD_STATUS } from '.'
 const { Text } = Typography;
 
-function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle: (b: any, suspend: '启动' | '暂停') => void): any {
+function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle?: (b: any, suspend: '启动' | '暂停') => void): any {
     return [
         {
             title: '启停',
@@ -17,7 +17,7 @@ function tableConfig(reviewStatusDetails: (value: any) => void, suspendHandle: (
             width: 40,
             fixed: 'left',
             render: (a: string, b: any) => {
-                return <Switch size="small" checked={a === 'AD_STATUS_NORMAL'} onChange={(checked) => { suspendHandle(b, checked ? '启动' : '暂停') }} />
+                return <Switch size="small" checked={a === 'AD_STATUS_NORMAL'} onChange={(checked) => { suspendHandle?.(b, checked ? '启动' : '暂停') }} />
             }
         },
         {

+ 24 - 16
src/pages/launchSystemV3/components/AdgroupTooltip/index.tsx

@@ -1,12 +1,13 @@
 import React from "react"
 import style from '../../tencentAdPutIn/create/index.less'
-import { AD_STATUS_ENUM, BID_MODE_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../tencentAdPutIn/const"
+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 { Typography } from "antd"
 import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook"
 
 
 interface Props {
     data: any
+    taskType?: 'GAME' | 'NOVEL'
 }
 
 /**
@@ -14,35 +15,42 @@ interface Props {
  * @param param0 
  * @returns 
  */
-const AdgroupTooltip: React.FC<Props> = ({ data: adgroups }) => {
+const AdgroupTooltip: React.FC<Props> = ({ data: adgroups, taskType }) => {
 
     /************************************/
     const {
-        marketingGoal, marketingAssetOuterSpec, marketingCarrierType, automaticSiteEnabled, siteSet, searchExpandTargetingSwitch, bidMode, smartBidType, bidAmount, optimizationGoal,
+        marketingGoal, marketingSubGoal, marketingAssetOuterSpec, marketingCarrierType, automaticSiteEnabled, siteSet, searchExpandTargetingSwitch, bidMode, smartBidType, bidScene, bidAmount, optimizationGoal,
         deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName
     } = adgroups
     /************************************/
 
     return <div className={style.detail_body} style={{ height: 'auto' }}>
         {(adgroups && Object.keys(adgroups).length > 0) && <>
-            <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>
+            {taskType === 'NOVEL' ? <>
+                <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_SUB_GOAL_ENUM[marketingSubGoal as keyof typeof MARKETING_SUB_GOAL_ENUM]}</p>
+                <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>
             <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>
+            {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>
+            {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>
                 </>}
             </>}
@@ -52,7 +60,7 @@ const AdgroupTooltip: React.FC<Props> = ({ data: adgroups }) => {
             <p style={{ fontWeight: 'bold', color: '#000' }}>投放日期:{beginDate} 至 {endDate}</p>
             <p>投放时段:{timeSeries.includes('0') ? <TimeSeriesLook timeSeries={timeSeries} /> : '全天'}</p>
             <p>首日开始时间:{firstDayBeginTime ? firstDayBeginTime : '关闭'}</p>
-            <p>广告状态:{AD_STATUS_ENUM[configuredStatus]}</p>
+            <p>广告状态:{AD_STATUS_ENUM[configuredStatus as keyof typeof AD_STATUS_ENUM]}</p>
             <p>广告名称:{adgroupName}</p>
         </>}
     </div>

+ 12 - 5
src/pages/launchSystemV3/components/ConversionSelect/index.tsx

@@ -13,6 +13,7 @@ import { useUpdateEffect } from "ahooks"
  */
 interface Props {
     adgroups: any
+    putInType?: 'NOVEL' | 'GAME'
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: PULLIN.AccountCreateLogsProps[]) => void,
@@ -21,7 +22,7 @@ interface Props {
 const ConversionSelect: React.FC<Props> = (props) => {
 
     /************************/
-    const { visible, onClose, data: data1, onChange, adgroups } = props
+    const { putInType, visible, onClose, data: data1, onChange, adgroups } = props
     const [tableData, setTableData] = useState<any[]>([])//table数据
     const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
     const [data, setData] = useState<PULLIN.AccountCreateLogsProps[]>(data1)
@@ -42,12 +43,18 @@ const ConversionSelect: React.FC<Props> = (props) => {
         } else {
             setTableData([])
         }
-    }, [accountId, adgroups, conversionName])
+    }, [accountId, adgroups, conversionName, putInType])
 
     // 获取公众号列表
     const getList = (accountId: number) => {
-        let { optimizationGoal, deepConversionSpec, siteSet } = adgroups
-        getConversionInfo.run({ accountId, pageNum: 1, pageSize: 100, conversionName, createSourceType: 'SELF_CREATED', optimizationGoal, deepWorthOptimizationGoal: deepConversionSpec?.deepConversionWorthSpec?.goal, siteSet }).then(res => {
+        let { optimizationGoal, deepConversionSpec, siteSet, marketingSubGoal, marketingGoal } = adgroups
+        let params: { [x: string]: any } = {}
+        if (putInType === 'NOVEL') {
+            params = { accountId, pageNum: 1, pageSize: 100, conversionName, createSourceType: 'SELF_CREATED', optimizationGoal, deepWorthOptimizationGoal: deepConversionSpec?.deepConversionWorthSpec?.goal, siteSet, taskType: putInType }
+        } else {
+            params = { accountId, pageNum: 1, pageSize: 100, marketingGoal, marketingSubGoal, conversionName, createSourceType: 'SELF_CREATED', optimizationGoal, deepWorthOptimizationGoal: deepConversionSpec?.deepConversionWorthSpec?.goal, siteSet, taskType: putInType }
+        }
+        getConversionInfo.run(params).then(res => {
             setTableData(res?.records || [])
         })
     }
@@ -159,7 +166,7 @@ const ConversionSelect: React.FC<Props> = (props) => {
             </div>
             <div className={style.right}>
                 <Space style={{ marginBottom: 10 }} align="end" size={0}>
-                    <Input.Search enterButton allowClear onSearch={(value) => setConversionName(value)} placeholder="搜索转化名称"/>
+                    <Input.Search enterButton allowClear onSearch={(value) => setConversionName(value)} placeholder="搜索转化名称" />
                     <Button icon={<SyncOutlined />} type='link' loading={getConversionInfo?.loading} onClick={() => { getList(data[selectAdz - 1].accountId) }}>刷新</Button>
                     {/* {data?.length > 1 && <Button disabled={!data[selectAdz - 1]['newConversionList']?.length} onClick={() => setOnekey()} type="link" loading={getConversionInfo.loading}>
                         <Space>

+ 33 - 14
src/pages/launchSystemV3/components/DynamicTooltip/index.tsx

@@ -1,7 +1,7 @@
 import React, { useEffect, useState } from "react"
 import style from '../../tencentAdPutIn/create/index.less'
-import { AD_STATUS_ENUM, CONVERSION_DATA_ENUM, CONVERSION_TARGET_ENUM, DELIVERY_MODE_ENUM, PAGE_TYPE_ENUM, TEXT_LINK_TYPE_ENUM, pageSpecFieldConVertUn } from "../../tencentAdPutIn/const"
-import { Space } from "antd"
+import { AD_STATUS_ENUM, CONVERSION_DATA_ENUM, CONVERSION_TARGET_ENUM, DELIVERY_MODE_ENUM, FLOATING_ZONE_TYPE_ENUM, PAGE_TYPE_ENUM, TEXT_LINK_TYPE_ENUM } from "../../tencentAdPutIn/const"
+import { Space, Tag } from "antd"
 import { getProfilesApi } from "@/services/adqV3/global"
 import { useAjax } from "@/Hook/useAjax"
 
@@ -19,7 +19,7 @@ const DynamicTooltip: React.FC<Props> = ({ data: dynamicData }) => {
 
     /************************************/
     const { deliveryMode, creativeTemplateId, dynamicCreativeName, creativeComponents, configuredStatus } = dynamicData
-    const { textLink, actionButton, showData, brand, mainJumpInfo } = creativeComponents || {}
+    const { textLink, actionButton, showData, brand, mainJumpInfo, creativeLabelDTOS, creativeBarrageDTOS, floatingZoneComponent } = creativeComponents || {}
 
     const [profileData, setprofileData] = useState<any>()
     const getProfiles = useAjax((params) => getProfilesApi(params))
@@ -34,15 +34,15 @@ const DynamicTooltip: React.FC<Props> = ({ data: dynamicData }) => {
             })
         }
     }, [brand])
-    
+
     return <div className={style.detail_body} style={{ height: 'auto' }}>
         {dynamicData && Object.keys(dynamicData).length > 0 && <>
             <p>创意名称:{dynamicCreativeName}</p>
-            <p style={{ fontWeight: 'bold', color: '#000' }}>创意状态:{AD_STATUS_ENUM[configuredStatus]}</p>
-            <p>投放模式:{DELIVERY_MODE_ENUM[deliveryMode]}</p>
+            <p style={{ fontWeight: 'bold', color: '#000' }}>创意状态:{AD_STATUS_ENUM[configuredStatus as keyof typeof AD_STATUS_ENUM]}</p>
+            <p>投放模式:{DELIVERY_MODE_ENUM[deliveryMode as keyof typeof DELIVERY_MODE_ENUM]}</p>
             <p>创意形式ID:{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>
@@ -57,23 +57,42 @@ const DynamicTooltip: React.FC<Props> = ({ data: dynamicData }) => {
                 </Space>}
             </>}
             <p style={{ fontWeight: 'bold', color: '#000' }}>跳转类型:{mainJumpInfo?.map((item: any) => {
-                let pageSpec = item.value.pageSpec
-                return PAGE_TYPE_ENUM[pageSpecFieldConVertUn[Object.keys(pageSpec)?.[0]]]
+                // let pageSpec = item.value.pageSpec
+                // return PAGE_TYPE_ENUM[pageSpecFieldConVertUn[Object.keys(pageSpec)?.[0] as keyof typeof pageSpecFieldConVertUn] as keyof typeof PAGE_TYPE_ENUM]
+                let pageType = item.value.pageType
+                return PAGE_TYPE_ENUM[pageType 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>}
+            </>}
+            {creativeLabelDTOS?.length > 0 && <>
+                <p style={{ fontWeight: 'bold', color: '#000' }}>标签:</p>
+                {creativeLabelDTOS.map((item: { content: string }, index: number) => <Tag key={index}>{item.content}</Tag>)}
+            </>}
+            {creativeBarrageDTOS?.length > 0 && <>
+                <p style={{ fontWeight: 'bold', color: '#000' }}>弹幕:</p>
+                {creativeBarrageDTOS.map((item: { text: string }, index: number) => <Tag key={index}>{item.text}</Tag>)}
             </>}
             {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>
+            </>}
+            {floatingZoneComponent && Object.keys(floatingZoneComponent).length > 0 && <>
+                <p style={{ fontWeight: 'bold', color: '#000' }}>浮层卡片:{floatingZoneComponent?.floatingZoneSwitch ? '开启' : '关闭'}</p>
+                {floatingZoneComponent?.floatingZoneType && <p>复层卡片类型:{FLOATING_ZONE_TYPE_ENUM[floatingZoneComponent.floatingZoneType as keyof typeof FLOATING_ZONE_TYPE_ENUM]}</p>}
+                {floatingZoneComponent?.floatingZoneImageUrl && <p style={{ display: 'flex', alignItems: 'flex-start' }}>创意图片:<img src={floatingZoneComponent?.floatingZoneImageUrl} width={40} /></p>}
+                {floatingZoneComponent?.floatingZoneSingleImageUrl && <p style={{ display: 'flex', alignItems: 'flex-start' }}>创意图片:<img src={floatingZoneComponent?.floatingZoneSingleImageUrl} width={40} /></p>}
+                {floatingZoneComponent?.floatingZoneName && <p>文案:{floatingZoneComponent.floatingZoneName}</p>}
+                {floatingZoneComponent?.floatingZoneDesc && <p>文案:{floatingZoneComponent.floatingZoneDesc}</p>}
+                {floatingZoneComponent?.floatingZoneButtonText && <p>按钮文案:{floatingZoneComponent.floatingZoneButtonText}</p>}
             </>}
         </>}
     </div>

+ 34 - 14
src/pages/launchSystemV3/components/TargetingTooltip/index.tsx

@@ -1,8 +1,8 @@
 import React, { useEffect, useState } from "react"
 import './index.less'
-import { DEVICE_PRICE_ENUM, EDUCATION_ENUM, EXCLUDED_DIMENSION_ENUM, LOCATION_TYPES_ENUM, MARITAL_STATUS_ENUM, NETWORK_ENUM, OPTIMIZATIONGOAL_ENUM, USER_OS_ENUM, WECHAT_AD_NEHAVIOR_ENUM } from "../../tencentAdPutIn/const"
+import { DEVICE_PRICE_ENUM, EDUCATION_ENUM, EXCLUDED_DIMENSION_ENUM, GAME_CONSUMPTION_LEVEL_ENUM, LOCATION_TYPES_ENUM, MARITAL_STATUS_ENUM, NETWORK_ENUM, OPTIMIZATIONGOAL_ENUM, USER_OS_ENUM, WECHAT_AD_NEHAVIOR_ENUM, WECHAT_AD_NEHAVIOR_GAME_ENUM } from "../../tencentAdPutIn/const"
 
-const targetingData = [
+let targetingData = [
     { key: 'geoLocation', name: '地域' },
     { key: 'age', name: '年龄' },
     { key: 'gender', name: '性别' },
@@ -22,6 +22,7 @@ interface Props {
     geoLocationList: any // 所有地域
     modelList: any  // 所有品牌手机
     targetingName?: string
+    taskType?: 'NOVEL' | 'GAME'
 }
 interface ContentProps {
     unlimited?: string,         // 不限
@@ -43,6 +44,7 @@ interface ContentProps {
     devicePrice?: string[]      // 手机价格
     userOs?: string[]           // 手机系统
     excludedOs?: string[]           // 手机系统
+    gameConsumptionLevel?: string[] // 游戏消费能力
     excludedConvertedAudience?: {
         conversionBehaviorList: string[],
         excludedDimension: string
@@ -55,9 +57,14 @@ interface ContentProps {
 const TargetingTooltip: React.FC<Props> = (props) => {
 
     /**********************/
-    const { data, geoLocationList, modelList, targetingName } = props
+    const { data, geoLocationList, modelList, targetingName, taskType } = props
     const [content, setContent] = useState<ContentProps>({})
     /**********************/
+
+    if (taskType == 'GAME') {
+        targetingData = [...targetingData, { key: 'gameConsumptionLevel', name: '游戏消费能力' }]
+    }
+
     useEffect(() => {
         if (data && geoLocationList) {
             let newConten: ContentProps = {}
@@ -114,6 +121,9 @@ const TargetingTooltip: React.FC<Props> = (props) => {
                     case 'devicePrice':
                         newConten.devicePrice = data[item]
                         break
+                    case 'gameConsumptionLevel':
+                        newConten.gameConsumptionLevel = data[item]
+                        break
                     case 'userOs':
                         newConten.userOs = data[item]
                         break
@@ -136,7 +146,7 @@ const TargetingTooltip: React.FC<Props> = (props) => {
     return <div className='targetingTooltip'>
         {targetingName && <div style={{ fontWeight: 'bold', marginBottom: 4 }}>{targetingName}</div>}
         {content?.geoLocation && <div>
-            <strong>地域:</strong><span>{(content?.location && content?.location?.length > 0) && `(${content?.location?.map((key: string) => LOCATION_TYPES_ENUM[key]).toString()})`}{content?.geoLocation?.toString()}</span>
+            <strong>地域:</strong><span>{(content?.location && content?.location?.length > 0) && `(${content?.location?.map((key: string) => LOCATION_TYPES_ENUM[key as keyof typeof LOCATION_TYPES_ENUM]).toString()})`}{content?.geoLocation?.toString()}</span>
         </div>}
         {content?.age && <div>
             <strong>年龄:</strong><span>{content?.age}</span>
@@ -145,22 +155,25 @@ const TargetingTooltip: React.FC<Props> = (props) => {
             <strong> 性别:</strong><span>{content?.gender === 'MALE' ? '男' : '女'}</span>
         </div>}
         {(content?.education && content?.education?.length > 0) && <div>
-            <strong>学历:</strong><span>{content?.education?.map((key: string) => EDUCATION_ENUM[key]).toString()}</span>
+            <strong>学历:</strong><span>{content?.education?.map((key: string) => EDUCATION_ENUM[key as keyof typeof EDUCATION_ENUM]).toString()}</span>
         </div>}
         {(content?.networkType && content?.networkType?.length > 0) && <div>
-            <strong>联网方式:</strong><span>{content?.networkType?.map((key: string) => NETWORK_ENUM[key]).toString()}</span>
+            <strong>联网方式:</strong><span>{content?.networkType?.map((key: string) => NETWORK_ENUM[key as keyof typeof NETWORK_ENUM]).toString()}</span>
         </div>}
         {(content?.devicePrice && content?.devicePrice?.length > 0) && <div>
-            <strong> 手机价格:</strong><span>{content?.devicePrice?.map((key: string) => DEVICE_PRICE_ENUM[key]).toString()}</span>
+            <strong>手机价格:</strong><span>{content?.devicePrice?.map((key: string) => DEVICE_PRICE_ENUM[key as keyof typeof DEVICE_PRICE_ENUM]).toString()}</span>
+        </div>}
+        {(content?.gameConsumptionLevel && content?.gameConsumptionLevel?.length > 0) && <div>
+            <strong>游戏消费能力:</strong><span>{content?.gameConsumptionLevel?.map((key: string) => GAME_CONSUMPTION_LEVEL_ENUM[key as keyof typeof GAME_CONSUMPTION_LEVEL_ENUM]).toString()}</span>
         </div>}
         {(content?.userOs && content?.userOs?.length > 0) && <div>
-            <strong>操作系统:</strong><span>{content?.userOs?.map((key: string) => USER_OS_ENUM[key]).toString()}</span>
+            <strong>操作系统:</strong><span>{content?.userOs?.map((key: string) => USER_OS_ENUM[key as keyof typeof USER_OS_ENUM]).toString()}</span>
         </div>}
         {(content?.excludedOs && content?.excludedOs?.length > 0) && <div>
-            <strong>排除操作系统:</strong><span>{content?.excludedOs?.map((key: string) => USER_OS_ENUM[key]).toString()}</span>
+            <strong>排除操作系统:</strong><span>{content?.excludedOs?.map((key: string) => USER_OS_ENUM[key as keyof typeof USER_OS_ENUM]).toString()}</span>
         </div>}
         {(content?.maritalStatus && content?.maritalStatus?.length > 0) && <div>
-            <strong>婚恋:</strong><span>{content?.maritalStatus?.map((key: string) => MARITAL_STATUS_ENUM[key]).toString()}</span>
+            <strong>婚恋:</strong><span>{content?.maritalStatus?.map((key: string) => MARITAL_STATUS_ENUM[key as keyof typeof MARITAL_STATUS_ENUM]).toString()}</span>
         </div>}
         {content?.deviceBrandModel && <>
             {(content?.deviceBrandModel?.includedList && content?.deviceBrandModel?.includedList?.length > 0) && <div>
@@ -170,16 +183,23 @@ const TargetingTooltip: React.FC<Props> = (props) => {
                 <strong>排除品牌型号:</strong><span>{content?.deviceBrandModel?.excludedList?.toString()}</span>
             </div>}
         </>}
-        {content?.wechatAdBehavior && <>
+        {content?.wechatAdBehavior && taskType === 'GAME' ? <>
+            {(content?.wechatAdBehavior?.actions && content?.wechatAdBehavior?.actions?.length > 0) && <div>
+                <strong>再营销:</strong><span>{content?.wechatAdBehavior?.actions?.map((key: string) => WECHAT_AD_NEHAVIOR_GAME_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_GAME_ENUM]).toString()}</span>
+            </div>}
+            {(content?.wechatAdBehavior?.excludedActions && content?.wechatAdBehavior?.excludedActions?.length > 0) && <div>
+                <strong>排除营销:</strong><span>{content?.wechatAdBehavior?.excludedActions?.map((key: string) => WECHAT_AD_NEHAVIOR_GAME_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_GAME_ENUM]).toString()}</span>
+            </div>}
+        </> : <>
             {(content?.wechatAdBehavior?.actions && content?.wechatAdBehavior?.actions?.length > 0) && <div>
-                <strong> 再营销:</strong><span>{content?.wechatAdBehavior?.actions?.map((key: string) => WECHAT_AD_NEHAVIOR_ENUM[key]).toString()}</span>
+                <strong>再营销:</strong><span>{content?.wechatAdBehavior?.actions?.map((key: string) => WECHAT_AD_NEHAVIOR_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_ENUM]).toString()}</span>
             </div>}
             {(content?.wechatAdBehavior?.excludedActions && content?.wechatAdBehavior?.excludedActions?.length > 0) && <div>
-                <strong>排除营销:</strong><span>{content?.wechatAdBehavior?.excludedActions?.map((key: string) => WECHAT_AD_NEHAVIOR_ENUM[key]).toString()}</span>
+                <strong>排除营销:</strong><span>{content?.wechatAdBehavior?.excludedActions?.map((key: string) => WECHAT_AD_NEHAVIOR_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_ENUM]).toString()}</span>
             </div>}
         </>}
         {content?.excludedConvertedAudience && <div>
-            <strong>排除已转化用户:</strong><span>{EXCLUDED_DIMENSION_ENUM[content?.excludedConvertedAudience?.excludedDimension]}{`(自定义转化行为:${content?.excludedConvertedAudience?.conversionBehaviorList?.map(item => OPTIMIZATIONGOAL_ENUM[item])?.toString()})`}</span>
+            <strong>排除已转化用户:</strong><span>{EXCLUDED_DIMENSION_ENUM[content?.excludedConvertedAudience?.excludedDimension as keyof typeof EXCLUDED_DIMENSION_ENUM]}{`(自定义转化行为:${content?.excludedConvertedAudience?.conversionBehaviorList?.map(item => OPTIMIZATIONGOAL_ENUM[item as keyof typeof OPTIMIZATIONGOAL_ENUM])?.toString()})`}</span>
         </div>}
         {content?.unlimited && <div>
             <strong>不限:</strong><span>{!content?.geoLocation && '地域,'}{content?.unlimited}</span>

+ 4 - 3
src/pages/launchSystemV3/components/TextAideInput/index.tsx

@@ -17,6 +17,7 @@ interface Props {
     maxTextLength?: number;
     isShowAjax?: boolean
     isSelectEmoji?: boolean
+    putInType?: 'NOVEL' | 'GAME'
 }
 
 /**
@@ -27,7 +28,7 @@ interface Props {
 const TextAideInput: React.FC<Props> = (props) => {
 
     /************************/
-    const { value, onChange, style, placeholder, maxTextLength = 10, isShowAjax = true, isSelectEmoji = true } = props
+    const { value, onChange, style, placeholder, maxTextLength = 10, isShowAjax = true, isSelectEmoji = true, putInType } = props
     const [text, setText] = useState<any>(value)
     const [descriptionShow, setDescriptionshow] = useState(false)
     const [cursorPosition, setCursorPosition] = useState<number | null>(null);
@@ -43,7 +44,7 @@ const TextAideInput: React.FC<Props> = (props) => {
 
     // 文案助手
     const textList = (keyword?: any) => {
-        getTextLsit.run({ keyword, maxTextLength })
+        getTextLsit.run({ keyword, maxTextLength, taskType: putInType })
     }
 
     const insertTextAtCursor = (emoji: string) => {
@@ -102,7 +103,7 @@ const TextAideInput: React.FC<Props> = (props) => {
                     onBlur={(e) => {
                         setCursorPosition(e.target.selectionStart)
                         if (!emojiOpen) {
-                            setTimeout(() => { setDescriptionshow(false) }, 0)
+                            setTimeout(() => { setDescriptionshow(false) }, 500)
                         }
                     }}
                     onChange={(e) => {

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

@@ -231,7 +231,7 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
                                                         filterOption={(input, option) =>
                                                             (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
                                                         }
-                                                        options={showFieldList}
+                                                        options={showFieldList.filter(item => item.value !== 'description')}
                                                         value={sortData.sortField}
                                                         onChange={(value) => {
                                                             setSortData({ ...sortData, sortField: value as any })

+ 2 - 2
src/pages/launchSystemV3/tencenTasset/corpWechat/index.tsx

@@ -1,12 +1,12 @@
 import { useAjax } from "@/Hook/useAjax";
-import { delCorpWechatApi, getCorpWechatAllApi, getCorpWechatApi, getCorpWechatDetailApi, getWechatAppletAllApi, getWechatAppletDetailApi } from "@/services/adqV3/global";
+import { delCorpWechatApi, getCorpWechatAllApi, getCorpWechatApi, getCorpWechatDetailApi } from "@/services/adqV3/global";
 import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
 import { Button, Card, Divider, Input, message, Select, Table, Typography } from "antd";
 import React, { useEffect, useState } from "react"
 import '../../tencentAdPutIn/index.less'
 import Modify from "./modify";
 import columns from "./tableConfig";
-const { Text, Link, Paragraph } = Typography;
+const { Text, Paragraph } = Typography;
 
 /**
  * 企业微信

+ 289 - 0
src/pages/launchSystemV3/tencenTasset/game/index.tsx

@@ -0,0 +1,289 @@
+import { useAjax } from "@/Hook/useAjax";
+import { delGameApi, getGameLibraryApi, getGameLibraryAppIdDetailApi, getGameLibraryDetailApi, getGameLibraryListApi } from "@/services/adqV3/global";
+import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
+import { Button, Card, Divider, Input, message, Select, Spin, Table, Typography } from "antd";
+import React, { useEffect, useState } from "react"
+import '../../tencentAdPutIn/index.less'
+import Modify from "./modify";
+import columns from "./tableConfig";
+const { Text, Paragraph } = Typography;
+
+/**
+ * 游戏库
+ * @returns 
+ */
+const Game: React.FC = () => {
+
+    /**********************************/
+    const [queryForm, setQueryForm] = useState<{ pageNum: number, pageSize: number, gameName?: string, idList?: number[], gameType?: string }>({ pageNum: 1, pageSize: 20 })
+    const [queryFormNew, setQueryFormNew] = useState<{ pageNum: number, pageSize: number, gameName?: string, idList?: number[], gameType?: string }>({ pageNum: 1, pageSize: 20 })
+    const [visible, setVisible] = useState<boolean>(false)
+
+    const getGameLibraryList = useAjax((params) => getGameLibraryListApi(params))
+    const delGame = useAjax((params) => delGameApi(params))
+    /**********************************/
+
+    useEffect(() => {
+        getGameLibraryList.run(queryFormNew)
+    }, [queryFormNew])
+
+    const del = (id: number) => {
+        delGame.run(id).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getGameLibraryList.refresh()
+            }
+        })
+    }
+
+    return <Card
+        className="cardResetCss"
+        title={<div className="flexStart" style={{ gap: 8 }}>
+            <Input style={{ width: 200 }} placeholder="请输入游戏名称" value={queryForm?.gameName} allowClear onChange={(e) => setQueryForm({ ...queryForm, gameName: e.target.value, pageNum: 1 })} />
+            <Select
+                showSearch
+                placeholder="请选择游戏类型"
+                onChange={(e) => setQueryForm({ ...queryForm, gameType: e, pageNum: 1 })}
+                filterOption={(input, option) =>
+                    (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
+                }
+                allowClear
+                style={{ width: 150 }}
+                options={[
+                    {
+                        value: 'WXGAME',
+                        label: '微信小游戏',
+                    }
+                ]}
+            />
+            <Input.TextArea
+                style={{ width: 300 }}
+                placeholder="请输入游戏ID(多个逗号隔开)"
+                allowClear
+                rows={1}
+                onChange={(e) => {
+                    let value = e.target.value
+                    let arr: any[] = []
+                    if (value) {
+                        value = value.replace(/[,,\s]/g, ',')
+                        arr = value.split(',').filter((a: any) => a)
+                    }
+                    setQueryForm({ ...queryForm, idList: arr, 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)}
+            dataSource={getGameLibraryList.data?.records}
+            size="small"
+            loading={getGameLibraryList?.loading}
+            scroll={{ y: 600 }}
+            bordered
+            rowKey={'id'}
+            pagination={{
+                defaultPageSize: 20,
+                current: getGameLibraryList.data?.current || 1,
+                pageSize: getGameLibraryList.data?.size || 10,
+                total: getGameLibraryList.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}
+            onClose={() => {
+                setVisible(false)
+            }}
+            onChange={() => {
+                setVisible(false)
+                getGameLibraryList.refresh()
+            }}
+        />}
+    </Card>
+}
+
+
+/**
+ * 选择游戏
+ * @param param0 
+ * @returns 
+ */
+export const SelectGame: React.FC<{ gameType: 'WXGAME', value?: string, onChange?: (value?: string, appId?: string) => void }> = ({ gameType, value, onChange }) => {
+
+    /*******************************/
+    const [visible, setVisible] = useState<boolean>(false)
+    const getGameLibrary = useAjax((params) => getGameLibraryApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        getGameLibrary.run({ gameType })
+    }, [gameType])
+
+    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/game'
+                    }}>前往管理</Button>
+                    <Button type="link" style={{ paddingLeft: 0, paddingRight: 0 }} onClick={() => {
+                        setVisible(true)
+                    }}>新增</Button>
+                    {/* <Link href="/#/launchSystemV3/tencenTasset/miniProgramWechat" target="_blank">
+                    前往管理
+                </Link> */}
+                </div>
+            </>}
+            value={value}
+            onChange={(e, options) => onChange?.(e, options?.appId)}
+        >
+            {getGameLibrary?.data?.map((item: { id: number, appId: string, gameName: string }) => {
+                return <Select.Option value={item.id} key={item.id} appId={item.appId} name={item.gameName + `(${item.appId})`}><Text strong>{item.gameName}</Text><Text type="secondary">{`(${item.appId})`}</Text></Select.Option>
+            })}
+        </Select>
+        {/* 新增修改 */}
+        {visible && <Modify
+            visible={visible}
+            onClose={() => {
+                setVisible(false)
+            }}
+            onChange={() => {
+                setVisible(false)
+                getGameLibrary.refresh()
+            }}
+        />}
+    </>
+}
+
+/**
+ * 选择游戏
+ * @param param0 
+ * @returns 
+ */
+export const SelectGameAppId: React.FC<{ gameType: 'WXGAME', value?: string[], onChange?: (value?: string[]) => void }> = ({ gameType, value, onChange }) => {
+
+    /*******************************/
+    const [visible, setVisible] = useState<boolean>(false)
+    const getGameLibrary = useAjax((params) => getGameLibraryApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        getGameLibrary.run({ gameType })
+    }, [gameType])
+
+    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/game'
+                    }}>前往管理</Button>
+                    <Button type="link" style={{ paddingLeft: 0, paddingRight: 0 }} onClick={() => {
+                        setVisible(true)
+                    }}>新增</Button>
+                    {/* <Link href="/#/launchSystemV3/tencenTasset/miniProgramWechat" target="_blank">
+                    前往管理
+                </Link> */}
+                </div>
+            </>}
+            value={value}
+            onChange={(e) => onChange?.(e)}
+        >
+            {getGameLibrary?.data?.map((item: { id: number, appId: string, gameName: string }) => {
+                return <Select.Option value={item.appId} key={item.id} name={item.gameName + `(${item.appId})`}><Text strong>{item.gameName}</Text><Text type="secondary">{`(${item.appId})`}</Text></Select.Option>
+            })}
+        </Select>
+        {/* 新增修改 */}
+        {visible && <Modify
+            visible={visible}
+            onClose={() => {
+                setVisible(false)
+            }}
+            onChange={() => {
+                setVisible(false)
+                getGameLibrary.refresh()
+            }}
+        />}
+    </>
+}
+
+
+/**
+ * 展示游戏
+ * @param param0 
+ * @returns 
+ */
+export const ShowGameDetail: React.FC<{ id: number }> = ({ id }) => {
+
+    /*******************************/
+    const getGameLibraryDetail = useAjax((params) => getGameLibraryDetailApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        if (id) {
+            getGameLibraryDetail.run(id)
+        }
+    }, [id])
+
+    return <Spin spinning={getGameLibraryDetail.loading}>
+        <Paragraph style={{ fontSize: 12, wordBreak: 'break-all', marginBottom: 0 }} ellipsis={{ rows: 2 }}>
+            {getGameLibraryDetail.data ? <>
+                <Text strong>游戏:{getGameLibraryDetail.data?.gameName}</Text><Text type="secondary">{`(${getGameLibraryDetail.data?.appId})`}</Text>
+            </> : id}
+        </Paragraph>
+    </Spin>
+}
+
+export const ShowGameAppIdDetail: React.FC<{ appId: number }> = ({ appId }) => {
+
+    /*******************************/
+    const getGameLibraryAppIdDetail = useAjax((params) => getGameLibraryAppIdDetailApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        if (appId) {
+            getGameLibraryAppIdDetail.run(appId)
+        }
+    }, [appId])
+
+    return <Spin spinning={getGameLibraryAppIdDetail.loading}>
+        <Paragraph style={{ fontSize: 12, wordBreak: 'break-all', marginBottom: 0 }} ellipsis={{ rows: 2 }}>
+            {getGameLibraryAppIdDetail.data ? <>
+                <Text strong>游戏:{getGameLibraryAppIdDetail.data?.gameName}</Text><Text type="secondary">{`(${getGameLibraryAppIdDetail.data?.appId})`}</Text>
+            </> : appId}
+        </Paragraph>
+    </Spin>
+}
+
+export default Game

+ 75 - 0
src/pages/launchSystemV3/tencenTasset/game/modify.tsx

@@ -0,0 +1,75 @@
+import { useAjax } from "@/Hook/useAjax"
+import { addGameApi } from "@/services/adqV3/global"
+import { Form, Input, message, Modal, Radio } from "antd"
+import React from "react"
+import '../../tencentAdPutIn/index.less'
+
+
+interface Props {
+    visible?: boolean
+    onChange?: () => void
+    onClose?: () => void
+}
+
+/**
+ * 新增游戏
+ * @returns 
+ */
+const Modify: React.FC<Props> = ({ visible, onChange, onClose }) => {
+
+    /********************************/
+    const [form] = Form.useForm()
+    const addGame = useAjax((params) => addGameApi(params))
+    /********************************/
+
+    const handleOk = () => {
+        form.validateFields().then(valid => {
+            console.log(valid)
+            addGame.run(valid).then(res => {
+                if (res) {
+                    message.success('新增成功')
+                    onChange?.()
+                }
+            })
+        })
+    }
+
+    return <Modal
+        title={<strong>{`新增游戏`}</strong>}
+        open={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        className="modalResetCss"
+        confirmLoading={addGame.loading}
+    >
+        <Form
+            name="basicMiniProgramWechat"
+            form={form}
+            layout='vertical'
+            autoComplete="off"
+            initialValues={{
+                gameType: 'WXGAME'
+            }}
+        >
+            <Form.Item label={<strong>游戏类型</strong>} name="gameType" rules={[{ required: true, message: '请选择游戏类型!' }]}>
+                <Radio.Group buttonStyle="solid">
+                    <Radio.Button value="WXGAME">微信小游戏</Radio.Button>
+                </Radio.Group>
+            </Form.Item>
+            <Form.Item label={<strong>游戏名称</strong>} name="gameName" rules={[{ required: true, message: '请输入游戏名称!' }]}>
+                <Input placeholder="请输入游戏名称" allowClear />
+            </Form.Item>
+            <Form.Item
+                label={<strong>appId</strong>}
+                name="appId"
+                rules={[
+                    { required: true, message: '请输入appId!' }
+                ]}
+            >
+                <Input placeholder="请输入appId" allowClear />
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(Modify)

+ 106 - 0
src/pages/launchSystemV3/tencenTasset/game/tableConfig.tsx

@@ -0,0 +1,106 @@
+import { copy } from "@/utils/utils"
+import { Space, Popconfirm, TableProps, Tag } from "antd"
+import React from "react"
+
+
+const columns = (del: (id: number) => void): TableProps<any>['columns'] => {
+
+
+    const data: TableProps<any>['columns'] = [
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 70,
+            render: (_, b) => {
+                return <Space>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del(b.id)}
+                        okText="是"
+                        cancelText="否"
+                    >
+                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            width: 50,
+            align: 'center',
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '游戏名称',
+            dataIndex: 'gameName',
+            key: 'gameName',
+            ellipsis: true,
+            width: 180,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '游戏类型',
+            dataIndex: 'gameType',
+            key: 'gameType',
+            width: 100,
+            align: 'center',
+            render: (a) => {
+                return a === 'WXGAME' ? <Tag color="#f50">微信小游戏</Tag> : '--'
+            }
+        },
+        {
+            title: 'appId',
+            dataIndex: 'appId',
+            key: 'appId',
+            ellipsis: true,
+            render: (a) => {
+                return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a}</a>
+            }
+        },
+        {
+            title: '创建人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 75,
+            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: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 140,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        }
+    ]
+
+    return data
+}
+
+export default columns

+ 17 - 2
src/pages/launchSystemV3/tencenTasset/targeting/index.tsx

@@ -42,12 +42,13 @@ const Targeting: React.FC = () => {
 
     /** 编辑 复制 */
     const editHandle = (data: any, isCopy?: boolean) => {
-        const { targetingName, targeting, description, id, accountId } = data
+        const { targetingName, targeting, description, id, accountId, taskType } = data
         let newModifyDta = {
             ...targeting,
             targetingName,
             description,
-            accountId: accountId || undefined
+            accountId: accountId || undefined,
+            taskType
         }
         if (!isCopy) {
             newModifyDta.id = id
@@ -73,6 +74,20 @@ const Targeting: React.FC = () => {
         className="cardResetCss"
         title={<div className="flexStart" style={{ gap: 8 }}>
             <Input style={{ width: 180 }} placeholder="请输入关键字查找" value={queryParams?.targetingName} allowClear onChange={(e) => setQueryParams({ ...queryParams, targetingName: e.target.value, pageNum: 1 })} />
+            <Select
+                style={{ width: 180 }}
+                showSearch
+                filterOption={(input, option) =>
+                    (option!.children as unknown as string)?.toLowerCase()?.includes(input?.toLowerCase())
+                }
+                allowClear
+                value={queryParams?.taskType}
+                onChange={(e) => setQueryParams({ ...queryParams, taskType: e })}
+                placeholder='请选择模板投放类型'
+            >
+                <Select.Option value={"GAME"}>游戏</Select.Option>
+                <Select.Option value={"NOVEL"}>小说</Select.Option>
+            </Select>
             <Select
                 style={{ width: 250 }}
                 showSearch

+ 13 - 2
src/pages/launchSystemV3/tencenTasset/targeting/tableConfig.tsx

@@ -1,4 +1,4 @@
-import { Button, Popconfirm, Popover, Space, Typography } from "antd";
+import { Button, Popconfirm, Popover, Space, Tag, Typography } from "antd";
 import { ColumnsType } from "antd/es/table";
 import React from "react";
 import TargetingTooltip from "../../components/TargetingTooltip";
@@ -24,6 +24,7 @@ export function TableConfig(geoLocationList: any, modelList: any, editHandle: (d
                             data={records?.targeting}
                             geoLocationList={geoLocationList}
                             modelList={modelList}
+                            taskType={records?.taskType}
                         />}
                     >
                         <QuestionCircleFilled />
@@ -31,6 +32,16 @@ export function TableConfig(geoLocationList: any, modelList: any, editHandle: (d
                 </div>
             }
         },
+        {
+            title: '类型',
+            dataIndex: 'taskType',
+            key: 'taskType',
+            align: 'center',
+            width: 70,
+            render(value) {
+                return value === 'GAME' ? <Tag color="#f50" style={{ fontSize: 12 }}>游戏</Tag> : value === 'NOVEL' ? <Tag color="#2db7f5" style={{ fontSize: 12 }}>小说</Tag> : <span style={{ fontSize: 12 }}>--</span>
+            },
+        },
         {
             title: '关联账户',
             dataIndex: 'accountId',
@@ -91,7 +102,7 @@ export function TableConfig(geoLocationList: any, modelList: any, editHandle: (d
             key: 'cz',
             align: 'center',
             fixed: 'right',
-            width: 100,
+            width: 120,
             render: (a: any, b: any) => {
                 return <Space wrap>
                     <Button type="link" style={{ padding: 0, fontSize: 12 }} onClick={() => { editHandle(b) }} >编辑</Button>

文件差异内容过多而无法显示
+ 23 - 0
src/pages/launchSystemV3/tencentAdPutIn/const.ts


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

@@ -6,7 +6,7 @@ import moment from "moment";
 import style from '../index.less'
 import TimeInSelect from "@/pages/launchSystemNew/components/timeInSelect";
 import { QuestionCircleFilled } from "@ant-design/icons";
-import { AD_STATUS_ENUM, MARKETING_TARGET_TYPE_ENUM, SelectTimeList, marketingGoalList } from "../../const";
+import { AD_STATUS_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, SelectTimeList, marketingGoalList } from "../../const";
 import New1Radio from "@/pages/launchSystemV3/components/New1Radio";
 import { txtLength } from "@/utils/utils";
 import InputName from "@/components/InputName";
@@ -28,7 +28,7 @@ const AdgroupsAdSetting: React.FC<{ value?: any }> = ({ value }) => {
     useEffect(() => {
         if (!(value && Object.keys(value).length)) {
             form.setFieldsValue({
-                adgroupName: marketingGoalList.find(item => item.value === marketingGoal)?.label + '_' + MARKETING_TARGET_TYPE_ENUM[marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM] + '_' + localStorage.getItem('userId')// + '_' + moment().format('MM_DD_HH:mm:ss')
+                adgroupName: marketingGoalList.find(item => item.value === marketingGoal)?.label + '_' + (MARKETING_TARGET_TYPE_ENUM[marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM] || MARKETING_TARGET_TYPE_GAME_ENUM[marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM]) + '_' + localStorage.getItem('userId')// + '_' + moment().format('MM_DD_HH:mm:ss')
             })
         }
     }, [marketingGoal, marketingTargetType])

+ 72 - 32
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsMarketingContent.tsx

@@ -3,7 +3,7 @@ import React, { useContext, useEffect, useState } from "react"
 import MarketingGoal from "./marketingGoal"
 import NewRadio from "@/pages/launchSystemV3/components/NewRadio"
 import { DispatchAd } from "./newCreateAd"
-import { GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_TARGET_TYPE_ENUM, OPTIMIZATIONGOAL_ENUM, defaultSiteSet } from "../../const"
+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"
@@ -11,6 +11,7 @@ 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"
 
 
 /**
@@ -21,14 +22,17 @@ import { SelectCorpWechat } from "@/pages/launchSystemV3/tencenTasset/corpWechat
 const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
 
     /****************************************/
-    const { form, OGPParams, setOGPparams } = useContext(DispatchAd)!;
+    const { form, OGPParams, setOGPparams, putInType } = useContext(DispatchAd)!;
 
     const marketingGoal = Form.useWatch('marketingGoal', form);
+    const marketingSubGoal = Form.useWatch<string>('marketingSubGoal', form);
     const marketingTargetType = Form.useWatch('marketingTargetType', form);
     const marketingCarrierType = Form.useWatch('marketingCarrierType', form);
     const bidMode = Form.useWatch('bidMode', form);
     const optimizationGoal = Form.useWatch('optimizationGoal', form);
     const smartBidType = Form.useWatch('smartBidType', form);
+    const bidScene = Form.useWatch('bidScene', form);
+    const wxGameAppId = Form.useWatch('wxGameAppId', form);
     const depthConversionEnabled = Form.useWatch('depthConversionEnabled', form);
     const deepConversionType = Form.useWatch(['deepConversionSpec', 'deepConversionType'], form);
     const goal = Form.useWatch(['deepConversionSpec', deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? 'deepConversionBehaviorSpec' : 'deepConversionWorthSpec', 'goal'], form);
@@ -56,11 +60,16 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
             marketingCarrierType = 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT'
         }
         if ((bidMode && siteSet && siteSet?.length > 0 && marketingGoal && marketingCarrierType && marketingTargetType) || automaticSiteEnabled) {
-            let obj: any = { siteSet: automaticSiteEnabled ? defaultSiteSet : siteSet, marketingGoal, marketingCarrierType: marketingCarrierType, marketingTargetType, marketingSubGoal: 'MARKETING_SUB_GOAL_UNKNOWN' }
+            let obj: any = { siteSet: automaticSiteEnabled ? defaultSiteSet : siteSet, marketingGoal, marketingCarrierType: marketingCarrierType, marketingTargetType, marketingSubGoal: OGPParams?.marketingSubGoal || 'MARKETING_SUB_GOAL_UNKNOWN' }
             if (bidMode === 'BID_MODE_OCPC' || bidMode === 'BID_MODE_OCPM') {
                 obj.bidMode = bidMode
             }
-            queryOptimizationGoalPermissions.run(obj)
+            if (putInType === 'GAME' && marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' && wxGameAppId) {
+                obj.marketingCarrierDetail = {
+                    marketingCarrierId: wxGameAppId
+                }
+            }
+            queryOptimizationGoalPermissions.run({ ...obj, taskType: putInType })
         }
     }
 
@@ -68,7 +77,7 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
         if (OGPParams.marketingCarrierType && OGPParams.marketingTargetType) {
             getOptimizationGoalPermissions()
         }
-    }, [OGPParams])
+    }, [OGPParams, putInType, wxGameAppId])
 
     // 处理深度转化优化
     useEffect(() => {
@@ -84,42 +93,51 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
             (newBehaviorList?.length > 0 && newDeepConversionData.push({ label: '优化转化行为', value: 'DEEP_CONVERSION_BEHAVIOR' }))
             { newWorthList?.length > 0 && newDeepConversionData.push({ label: '优化ROI', value: 'DEEP_CONVERSION_WORTH' }) }
             setDeepConversionData(newDeepConversionData)
+            const inputInstance = form.getFieldValue('deepConversionSpec');
             let deepConversionType = newBehaviorList?.length > 0 ? 'DEEP_CONVERSION_BEHAVIOR' : newWorthList?.length > 0 ? "DEEP_CONVERSION_WORTH" : ''
-            form.setFieldsValue({
-                deepConversionSpec: {
-                    deepConversionType
-                }
-            })
+            if (inputInstance?.deepConversionType ? inputInstance?.deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? !newBehaviorList?.length : !newWorthList?.length : true) {
+                form.setFieldsValue({
+                    deepConversionSpec: {
+                        deepConversionType
+                    }
+                })
+            }
         }
-    }, [optimizationGoal, queryOptimizationGoalPermissions?.data, goal])
+    }, [optimizationGoal, queryOptimizationGoalPermissions?.data])
 
     // 选择营销目的触发
     useEffect(() => {
         let newRule: any = {}
         let newMarketingTargetTypeList: PULLIN.DataType[] = []
         if (marketingGoal) {
-            newRule = adRules[marketingGoal as keyof typeof adRules]
             // 根据const里数据对比选出可展示数据
-            newMarketingTargetTypeList = Object.keys(MARKETING_TARGET_TYPE_ENUM).filter(key => newRule?.[key]).map(key => ({ label: MARKETING_TARGET_TYPE_ENUM[key as keyof typeof MARKETING_TARGET_TYPE_ENUM], value: key }))
+            if (putInType === 'NOVEL') {
+                newRule = adRules[marketingGoal]
+                newMarketingTargetTypeList = Object.keys(MARKETING_TARGET_TYPE_ENUM).filter(key => newRule?.[key]).map(key => ({ label: MARKETING_TARGET_TYPE_ENUM[key as keyof typeof MARKETING_TARGET_TYPE_ENUM], value: key }))
+            } else if (putInType === 'GAME') {
+                newRule = adRules[marketingGoal][marketingSubGoal]
+                newMarketingTargetTypeList = Object.keys(MARKETING_TARGET_TYPE_GAME_ENUM).filter(key => newRule?.[key]).map(key => ({ label: MARKETING_TARGET_TYPE_GAME_ENUM[key as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM], value: key }))
+            }
         }
         setMarketingTargetTypeList(newMarketingTargetTypeList)
         setRules(newRule)
-    }, [marketingGoal])
+    }, [marketingGoal, marketingSubGoal, putInType])
+
 
     // 推广产品设置默认值
     useEffect(() => {
         if (!(value && Object.keys(value).length > 0) && marketingTargetTypeList && (!marketingTargetType || (marketingTargetType && !marketingTargetTypeList.some(item => item.value === marketingTargetType)))) {
             form.setFieldsValue({ marketingTargetType: marketingTargetTypeList?.[0]?.value })
-            // setOGPparams({ ...OGPParams, marketingTargetType: marketingTargetTypeList?.[0]?.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)
@@ -134,13 +152,18 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
         }
     }, [marketingCarrierType, marketingCarrierTypeList, OGPParams, value, marketingTargetType])
 
+    // 切换 其他 优化目标更新
     useEffect(() => {
         let optimizationGoalPermissionList: string[] = queryOptimizationGoalPermissions?.data?.optimizationGoalPermissionList
         if (isUpdateOptimizationGoal && optimizationGoalPermissionList?.length > 0 && !optimizationGoalPermissionList?.includes(optimizationGoal)) {
-            form.setFieldsValue({ optimizationGoal: optimizationGoalPermissionList?.includes('OPTIMIZATIONGOAL_ECOMMERCE_ORDER') ? 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER' : optimizationGoalPermissionList?.includes('OPTIMIZATIONGOAL_PAGE_SCAN_CODE') ? 'OPTIMIZATIONGOAL_PAGE_SCAN_CODE' : undefined })
+            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 })
+            } else if (putInType === 'GAME') {
+                form.setFieldsValue({ optimizationGoal: (optimizationGoalPermissionList?.includes('OPTIMIZATIONGOAL_ECOMMERCE_ORDER') ? 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER' : optimizationGoalPermissionList?.[0]) || undefined })
+            }
             setIsUpdateOptimizationGoal(false)
         }
-    }, [queryOptimizationGoalPermissions?.data?.optimizationGoalPermissionList, marketingGoal, marketingTargetType, marketingCarrierType, optimizationGoal, value, isUpdateOptimizationGoal])
+    }, [queryOptimizationGoalPermissions?.data?.optimizationGoalPermissionList, marketingGoal, marketingTargetType, marketingCarrierType, optimizationGoal, value, isUpdateOptimizationGoal, putInType])
 
     const setIsUpdate = () => {
         if (bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') {
@@ -166,9 +189,12 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
         title={<strong style={{ fontSize: 18 }}>营销内容</strong>}
         className="cardResetCss"
     >
-        <Form.Item name="marketingGoal" label={<strong>营销目的</strong>} rules={[{ required: true, message: '请选择营销目的!' }]}>
-            <MarketingGoal onChange={(e) => { setOGPparams({ ...OGPParams, marketingGoal: e as string }); setIsUpdate() }} />
+        <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() }} />
         </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() }} />
+        </Form.Item>}
         {marketingTargetTypeList?.length > 0 && <Form.Item name="marketingTargetType" label={<strong>推广产品</strong>} rules={[{ required: true, message: '请选择推广产品!' }]}>
             <NewRadio data={marketingTargetTypeList} onChange={(e) => {
                 if (e === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') { // 公众号
@@ -184,12 +210,14 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
             <SelectMiniProgramWechat />
         </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_WORK' ? <Form.Item label={<strong>企业微信</strong>} 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: '请选择微信小游戏' }]}>
+            <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() }} />
         </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'].includes(marketingCarrierType) && <Form.Item
+            {['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
                 label={<Space>
                     <strong>转化归因新链路</strong>
                     <Tooltip title={<>
@@ -209,13 +237,20 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
             >
                 <New1Radio data={[{ label: '开启', value: true }, { label: '关闭', value: false }]} />
             </Form.Item>}
-            <Form.Item label={<strong>优化目标</strong>} name='optimizationGoal' rules={[{ required: true, message: '请选择优化目标' }]}>
+            <Form.Item
+                label={<strong>优化目标</strong>}
+                name='optimizationGoal'
+                rules={[{ required: true, message: '请选择优化目标' }]}
+                help={(marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' && !wxGameAppId) ? '请先选择微信小游戏' : undefined}
+                validateStatus={(marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' && !wxGameAppId) ? 'error' : undefined}
+            >
                 <Select
                     style={{ width: 480 }}
                     showSearch
                     filterOption={(input, option) =>
                         (option!.children as unknown as string)?.toLowerCase()?.includes(input?.toLowerCase())
                     }
+                    disabled={(marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' && !wxGameAppId)}
                     allowClear
                     placeholder='请选择'
                     loading={queryOptimizationGoalPermissions.loading}
@@ -226,17 +261,22 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
                 </Select>
             </Form.Item>
             {/* 深度优化 */}
-            {((behaviorList?.length > 0 || worthList?.length > 0) && smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC') && <>
+            {((behaviorList?.length > 0 || worthList?.length > 0) && (marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? !!wxGameAppId : true) && (putInType === 'GAME' ? bidScene !== 'BID_SCENE_NORMAL_MAX' : smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC')) && <>
                 <Form.Item label={<strong>深度转化优化</strong>} name='depthConversionEnabled' valuePropName="checked">
-                    <Switch checkedChildren="开启" unCheckedChildren="关闭" onChange={(e) => {
-                        if (e) {
-                            form.setFieldsValue({
-                                deepConversionSpec: {
-                                    deepConversionType: behaviorList?.length > 0 ? 'DEEP_CONVERSION_BEHAVIOR' : worthList?.length > 0 ? "DEEP_CONVERSION_WORTH" : ''
-                                }
-                            })
-                        }
-                    }} />
+                    <Switch
+                        disabled={(marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' && !wxGameAppId)}
+                        checkedChildren="开启"
+                        unCheckedChildren="关闭"
+                        onChange={(e) => {
+                            if (e) {
+                                form.setFieldsValue({
+                                    deepConversionSpec: {
+                                        deepConversionType: behaviorList?.length > 0 ? 'DEEP_CONVERSION_BEHAVIOR' : worthList?.length > 0 ? "DEEP_CONVERSION_WORTH" : ''
+                                    }
+                                })
+                            }
+                        }}
+                    />
                 </Form.Item>
                 {depthConversionEnabled && <>
                     <Form.Item label={<strong>深度优化类型</strong>} name={['deepConversionSpec', 'deepConversionType']} rules={[{ required: true, message: '请选择深度优化类型' }]}>

+ 13 - 8
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, OPTIMIZATIONGOAL_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
+import { BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, OPTIMIZATIONGOAL_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
 import { QuestionCircleFilled } from "@ant-design/icons";
 
 
@@ -13,12 +13,13 @@ import { QuestionCircleFilled } from "@ant-design/icons";
 const AdgroupsPrice: React.FC = () => {
 
     /****************************************/
-    const { form, setOGPparams, OGPParams } = useContext(DispatchAd)!;
+    const { form, setOGPparams, OGPParams, putInType } = useContext(DispatchAd)!;
 
     const siteSet = Form.useWatch('siteSet', form)
     const bidMode = Form.useWatch('bidMode', form)
     const optimizationGoal = Form.useWatch('optimizationGoal', form)
     const smartBidType = Form.useWatch('smartBidType', form)
+    const bidScene = Form.useWatch('bidScene', form)
     const autoAcquisitionEnabled = Form.useWatch('autoAcquisitionEnabled', form)
     const automaticSiteEnabled = Form.useWatch('automaticSiteEnabled', form)
     const deepConversionType = Form.useWatch(['deepConversionSpec', 'deepConversionType'], form);
@@ -55,6 +56,7 @@ const AdgroupsPrice: React.FC = () => {
                         form.setFieldsValue({
                             optimizationGoal: null,
                             smartBidType: null,
+                            bidScene: null,
                             // bidAmount:null,
                             bidStrategy: null,
                             autoAcquisitionEnabled: false,
@@ -65,6 +67,7 @@ const AdgroupsPrice: React.FC = () => {
                         form.setFieldsValue({
                             optimizationGoal: "OPTIMIZATIONGOAL_ECOMMERCE_ORDER",
                             smartBidType: "SMART_BID_TYPE_CUSTOM",
+                            bidScene: "BID_SCENE_NORMAL_AVERAGE",
                             bidAmount: '1000',
                             bidStrategy: "BID_STRATEGY_TARGET_COST",
                             autoAcquisitionEnabled: false,
@@ -76,12 +79,14 @@ const AdgroupsPrice: React.FC = () => {
             />
         </Form.Item>
         {(bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') && <>
-            <Form.Item label={<strong>出价类型</strong>} name='smartBidType' rules={[{ required: true, message: '请选择出价类型' }]}>
+            {putInType === 'GAME' ? <Form.Item label={<strong>出价场景</strong>} name='bidScene' rules={[{ required: true, message: '请选择出价场景' }]}>
+                <New1Radio data={Object.keys(BID_SCENE_NORMAL_ENUM).map(key => ({ label: BID_SCENE_NORMAL_ENUM[key as keyof typeof BID_SCENE_NORMAL_ENUM], value: key }))} />
+            </Form.Item> : <Form.Item label={<strong>出价类型</strong>} name='smartBidType' rules={[{ required: true, message: '请选择出价类型' }]}>
                 <New1Radio data={Object.keys(SMART_BID_TYPE_ENUM).map(key => ({ label: SMART_BID_TYPE_ENUM[key as keyof typeof SMART_BID_TYPE_ENUM], value: key }))} />
-            </Form.Item>
+            </Form.Item>}
         </>}
 
-        {smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC' && <>
+        {(putInType === 'GAME' ? bidScene !== 'BID_SCENE_NORMAL_MAX' : smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC') && <>
             <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
                 <Input
                     placeholder={`请输入价格`}
@@ -116,7 +121,7 @@ const AdgroupsPrice: React.FC = () => {
                     </Form.Item>
                 </> : null}
 
-            {(bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') && <>
+            {((bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') && (putInType === 'GAME' ? bidScene !== 'BID_SCENE_NORMAL_MAX' : smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC')) && <>
                 <Form.Item
                     style={{ marginBottom: 10 }}
                     label={<Space>
@@ -154,8 +159,8 @@ const AdgroupsPrice: React.FC = () => {
             </>}
         </>}
 
-        <Form.Item label={<strong>广告日预算</strong>} name='dailyBudget' rules={[{ required: smartBidType === 'SMART_BID_TYPE_SYSTEMATIC', message: '请输入广告日预算' }]}>
-            <Input placeholder={`广告日预算${smartBidType === 'SMART_BID_TYPE_SYSTEMATIC' ? '' : ', 不填默认为不限'}`} style={{ width: 480 }} suffix="元/天" />
+        <Form.Item label={<strong>广告日预算</strong>} name='dailyBudget' rules={[{ required: (smartBidType === 'SMART_BID_TYPE_SYSTEMATIC' || bidScene === 'BID_SCENE_NORMAL_MAX'), message: '请输入广告日预算' }]}>
+            <Input placeholder={`广告日预算${(smartBidType === 'SMART_BID_TYPE_SYSTEMATIC' || bidScene === 'BID_SCENE_NORMAL_MAX') ? '' : ', 不填默认为不限'}`} style={{ width: 480 }} suffix="元/天" />
         </Form.Item>
 
     </Card>

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

@@ -4,11 +4,12 @@ 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, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM, SMART_BID_TYPE_ENUM } from "../../const";
+import { AD_STATUS_ENUM, BID_MODE_ENUM, 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";
 
 /**
  * 广告信息
@@ -17,11 +18,11 @@ import { ShowMiniProgramWechatDetail } from "@/pages/launchSystemV3/tencenTasset
 const Ad: React.FC = () => {
 
     /*****************************/
-    const { addelivery, setAddelivery, accountCreateLogs, clearData, setAccountCreateLogs } = useContext(DispatchAddelivery)!;
+    const { addelivery, setAddelivery, accountCreateLogs, clearData, setAccountCreateLogs, putInType } = useContext(DispatchAddelivery)!;
     const { adgroups } = addelivery
     const {
-        marketingGoal, marketingAssetOuterSpec, marketingCarrierType, automaticSiteEnabled, siteSet, searchExpandTargetingSwitch, bidMode, smartBidType, bidAmount, optimizationGoal, isConversion, depthConversionEnabled,
-        deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName, sceneSpec, autoDerivedCreativeEnabled, sysWechatAppId
+        marketingGoal, marketingSubGoal, marketingAssetOuterSpec, marketingCarrierType, automaticSiteEnabled, siteSet, searchExpandTargetingSwitch, bidMode, smartBidType, bidScene, bidAmount, optimizationGoal, isConversion, depthConversionEnabled,
+        deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName, sceneSpec, autoDerivedCreativeEnabled, sysWechatAppId, wxGameAppId
     } = adgroups
     const [newVisible, setNewVisible] = useState<boolean>(false)
     /*****************************/
@@ -35,15 +36,27 @@ const Ad: React.FC = () => {
                 <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 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>
-                        {marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT' && <ShowMiniProgramWechatDetail id={sysWechatAppId} />}
+                        {putInType === 'NOVEL' ? <>
+                            <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_SUB_GOAL_ENUM[marketingSubGoal as keyof typeof MARKETING_SUB_GOAL_ENUM]}</p>
+                            <p style={{ fontWeight: 'bold', color: '#000' }}>推广产品类型:{MARKETING_TARGET_TYPE_GAME_ENUM[marketingAssetOuterSpec?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM]}</p>
+                        </>}
+                        {marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT' ?
+                            <ShowMiniProgramWechatDetail id={sysWechatAppId} /> :
+                            marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ?
+                                <ShowGameAppIdDetail appId={wxGameAppId}/> :
+                                null
+                        }
                         <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 as keyof typeof SITE_SET_ENUM]).toString()}</Typography.Paragraph>}
                         <p>搜索场景扩量:{searchExpandTargetingSwitch === 'SEARCH_EXPAND_TARGETING_SWITCH_OPEN' ? '开启' : '关闭'}</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>
+                        {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>
                         {isConversion && <p style={{ fontWeight: 'bold', color: '#000' }}>转化:新链路转化</p>}
                         {optimizationGoal && <p style={{ fontWeight: 'bold', color: '#000' }}>优化目标:{OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM]}</p>}
@@ -84,6 +97,7 @@ const Ad: React.FC = () => {
 
         {/* 新建广告 */}
         {newVisible && <NewCreateAd
+            putInType={putInType}
             value={addelivery.adgroups}
             visible={newVisible}
             onClose={() => {
@@ -105,8 +119,9 @@ const Ad: React.FC = () => {
                             return item
                         }))
                     }
-                    setAddelivery({ ...addelivery, adgroups })
                     setNewVisible(false)
+                    setAddelivery({ ...addelivery, adgroups })
+
                     clearData()
                 } else {
                     setAccountCreateLogs(accountCreateLogs.map(item => ({ accountId: item.accountId })))
@@ -125,20 +140,20 @@ const Ad: React.FC = () => {
                             cancelText: '不清空',
                             keyboard: false,
                             onOk() { // 清空
-                                setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
                                 setNewVisible(false)
+                                setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
                                 clearData()
                             },
                             onCancel() { // 不清空
-                                setAddelivery({ ...addelivery, adgroups })
                                 setNewVisible(false)
+                                setAddelivery({ ...addelivery, adgroups })
                                 clearData()
                             },
                             className: 'modalResetCss'
                         })
                     } else {
-                        setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
                         setNewVisible(false)
+                        setAddelivery({ adgroups, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
                         clearData()
                     }
                 }

+ 2 - 3
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/marketingGoal.tsx

@@ -1,6 +1,5 @@
 import React from "react"
 import style from './index.less'
-import { marketingGoalList } from "../../const"
 import { CheckCircleFilled } from "@ant-design/icons"
 
 /**
@@ -8,12 +7,12 @@ import { CheckCircleFilled } from "@ant-design/icons"
  * @param param0 
  * @returns 
  */
-const MarketingGoal: React.FC<PULLIN.FormItemDataProps> = ({ value, onChange, id }) => {
+const MarketingGoal: React.FC<PULLIN.FormItemDataProps> = ({ data, value, onChange, id }) => {
 
 
     return <div className={style.marketingGoal} id={id}>
         <div className={style.marketingGoal_row}>
-            {marketingGoalList.map(item => <div key={item.value} onClick={() => onChange?.(item.value)} className={`${style.marketingGoal_col} ${value === item.value ? style.marketingGoal_active : ''}`}>
+            {data.map(item => <div key={item.value} onClick={() => onChange?.(item.value)} className={`${style.marketingGoal_col} ${value === item.value ? style.marketingGoal_active : ''}`}>
                 {value === item.value ? <>
                     <img src={item.iconSelected} />
                     <div><CheckCircleFilled /></div>

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

@@ -1,7 +1,7 @@
 import { Button, Form, Modal, Space, message } from "antd"
 import React, { useEffect, useState } from "react"
 import '../../index.less'
-import { defaultMarketingGoal, defaultSiteSet } from "../../const"
+import { defaultGameMarketingGoal, defaultMarketingGoal, defaultSiteSet } from "../../const"
 import AdgroupsMarketingContent from "./adgroupsMarketingContent"
 import AdgroupsSitSet from "./adgroupsSitSet"
 import AdgroupsPrice from "./adgroupsPrice"
@@ -12,6 +12,7 @@ import { getTimeSeriesList } from "@/pages/launchSystemNew/adq/ad/const"
 export const DispatchAd = React.createContext<PULLIN.AdReactContent | null>(null);
 
 interface Props {
+    putInType?: 'NOVEL' | 'GAME'
     value?: any,
     visible?: boolean
     onClose?: () => void
@@ -23,12 +24,12 @@ interface Props {
  * @param param0 
  * @returns 
  */
-const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose }) => {
+const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose, putInType }) => {
 
     /***********************************/
     const [form] = Form.useForm();
     // 深度优化副作用参数
-    const [OGPParams, setOGPparams] = useState<PULLIN.OGPParamsProps>({ bidMode: 'BID_MODE_OCPM', siteSet: defaultSiteSet, automaticSiteEnabled: false, marketingGoal: defaultMarketingGoal })
+    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 })
     /***********************************/
 
     const handleOk = (values: any) => {
@@ -49,7 +50,7 @@ const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose }) =>
             isSetfirstDayBeginTime,  // 首日开始时间是否开启
             ...surplusValues
         } = values
-
+        console.log(values)
         let adgroupsValues: any = {
             ...surplusValues,
             beginDate: moment(date?.[0]).format('YYYY-MM-DD'),
@@ -79,7 +80,7 @@ const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose }) =>
 
     // 数据回填
     useEffect(() => {
-        if (value && Object.keys(value).length > 0) {
+        if (value && Object.keys(value).length > 0 && visible) {
             const {
                 firstDayBeginTime,
                 timeSeries,
@@ -151,12 +152,13 @@ const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose }) =>
                 siteSet: adgroupsValues.siteSet,
                 automaticSiteEnabled: adgroupsValues.automaticSiteEnabled,
                 marketingGoal: adgroupsValues.marketingGoal,
+                marketingSubGoal: adgroupsValues.marketingSubGoal,
                 marketingCarrierType: adgroupsValues?.marketingCarrierType,
                 marketingTargetType: adgroupsValues?.marketingTargetType
             })
             form.setFieldsValue({ ...adgroupsValues })
         }
-    }, [value])
+    }, [value, visible])
 
     return <Modal
         title={<strong style={{ fontSize: 20 }}>广告的基本信息</strong>}
@@ -184,14 +186,15 @@ const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose }) =>
             }}
             onFinish={handleOk}
             initialValues={{
-                marketingGoal: defaultMarketingGoal,
+                marketingGoal: putInType === 'NOVEL' ? defaultMarketingGoal : putInType === 'GAME' ? defaultGameMarketingGoal : undefined,
+                marketingSubGoal: putInType === 'GAME' ? 'MARKETING_SUB_GOAL_MINI_GAME_NEW_CUSTOMER_GROWTH' : undefined,
                 automaticSiteEnabled: false,
                 siteSet: defaultSiteSet,
                 searchExpandTargetingSwitch: 'SEARCH_EXPAND_TARGETING_SWITCH_OPEN',
                 bidMode: 'BID_MODE_OCPM',
                 smartBidType: 'SMART_BID_TYPE_CUSTOM',
-                optimizationGoal: 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER',
-                depthConversionEnabled: true,
+                bidScene: 'BID_SCENE_NORMAL_AVERAGE',
+                optimizationGoal: putInType === 'NOVEL' ? 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER' : 'OPTIMIZATIONGOAL_MOBILE_APP_AD_INCOME',
                 deepConversionSpec: {
                     deepConversionType: 'DEEP_CONVERSION_WORTH'
                 },
@@ -214,7 +217,7 @@ const NewCreateAd: React.FC<Props> = ({ value, visible, onChange, onClose }) =>
                 isConversion: false
             }}
         >
-            <DispatchAd.Provider value={{ form, OGPParams, setOGPparams }}>
+            <DispatchAd.Provider value={{ form, OGPParams, setOGPparams, putInType }}>
                 <Space direction="vertical" style={{ width: '100%' }}>
                     {/* 营销内容 */}
                     <AdgroupsMarketingContent value={value} />

+ 79 - 0
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/SelectBarrage.tsx

@@ -0,0 +1,79 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getBarrageRecommendListApi } from "@/services/adqV3/global"
+import { Select } from "antd"
+import React, { useEffect, useState } from "react"
+
+interface Props {
+    arrayProperty: { maxNumber: number, minNumber: number }
+    value?: { text: string, id: number }[]
+    onChange?: (value?: { text: string, id: number }[]) => void
+    putInType?: 'NOVEL' | 'GAME'
+}
+
+/**
+ * 选择弹幕
+ * @param param0 
+ * @returns 
+ */
+const SelectBarrage: React.FC<Props> = ({ arrayProperty, value = [], onChange, putInType }) => {
+
+    /****************************************/
+    const [barrageList, setBarrageList] = useState<{ text: string, id: number }[]>([])
+
+    const getBarrageRecommendList = useAjax((params) => getBarrageRecommendListApi(params))
+    /****************************************/
+
+    useEffect(() => {
+        getBarrageRecommendList.run({ taskType: putInType }).then(res => {
+            if (res?.list?.length) {
+                setBarrageList(res?.list || [])
+            } else {
+                setBarrageList([])
+            }
+        })
+    }, [putInType])
+
+    let barrageValue = value?.map(item => item.id)
+    return <div
+        style={{
+            border: '1px solid #d9d9d9',
+            borderRadius: 6,
+            display: 'flex',
+            gap: 10,
+            borderColor: barrageValue?.length > arrayProperty.maxNumber ? 'red' : '#d9d9d9',
+            alignItems: 'center'
+        }}
+    >
+        <Select
+            showSearch
+            placeholder="请选择弹幕"
+            onChange={(_, options) => {
+                onChange?.(options?.length > 0 ? options.map((item: { text: any; value: any }) => ({ text: item.text, id: item.value })) : [])
+            }}
+            value={barrageValue}
+            mode="multiple"
+            filterOption={(input, option) =>
+                ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+            }
+            allowClear
+            loading={getBarrageRecommendList.loading}
+            bordered={false}
+        >
+            {barrageList.map((item: { text: string, id: number }) => <Select.Option key={item.id} value={item.id} text={item.text} disabled={barrageValue?.length > arrayProperty.maxNumber && !barrageValue.includes(item.id)}>{item.text}</Select.Option>)}
+        </Select>
+        <div
+            style={{
+                // borderLeft: '1px solid #d9d9d9',
+                display: 'flex',
+                alignContent: 'center',
+                justifyContent: 'center',
+                paddingLeft: 10,
+                paddingRight: 10
+            }}
+        >
+            <span>{value?.length || 0}/{arrayProperty.maxNumber}</span>
+        </div>
+    </div>
+}
+
+export default React.memo(SelectBarrage)

+ 84 - 82
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/SelectLable.tsx

@@ -11,6 +11,7 @@ const { Text } = Typography;
 
 interface Props {
     arrayProperty: { maxNumber: number, minNumber: number }
+    putInType?: 'NOVEL' | 'GAME'
     value?: { content: string, type: LABEL_TYPE_ENUM }[]
     onChange?: (value?: { content: string, type: LABEL_TYPE_ENUM }[]) => void
 }
@@ -19,7 +20,7 @@ interface Props {
  * 选择标签
  * @returns 
  */
-const SelectLabel: React.FC<Props> = ({ value, onChange, arrayProperty }) => {
+const SelectLabel: React.FC<Props> = ({ value, onChange, arrayProperty, putInType }) => {
 
     /************************************/
     const [visible, setVisible] = useState<boolean>(false)
@@ -39,9 +40,9 @@ const SelectLabel: React.FC<Props> = ({ value, onChange, arrayProperty }) => {
 
     useEffect(() => {
         if (visible) {
-            getAdLabel.run({})
+            getAdLabel.run({ taskType: putInType })
         }
-    }, [visible])
+    }, [visible, putInType])
 
     const handleOk = () => {
         onChange?.(oldValue)
@@ -98,89 +99,90 @@ const SelectLabel: React.FC<Props> = ({ value, onChange, arrayProperty }) => {
                 accessKey={activeKey}
                 onChange={(e) => setActiveKey(e)}
                 tabBarExtraContent={<Text type="secondary" style={{ fontSize: 12 }}>最多可选 {arrayProperty.maxNumber} 个标签,且标签总字数上限不超过 16 个字,中英文均算 1 个字</Text>}
-            >
-                <Tabs.TabPane tab="选择已有" key="1" >
-                    <Spin spinning={getAdLabel.loading}>
-                        <div className={style.labelContent}>
-                            <div className={style.tips}>
-                                <strong>行业通用</strong>
+                items={[
+                    {
+                        label: '选择已有', key: '1', children: <Spin spinning={getAdLabel.loading}>
+                            <div className={style.labelContent}>
+                                <div className={style.tips}>
+                                    <strong>行业通用</strong>
+                                </div>
+                                <Form
+                                    name="basicSelectLabel"
+                                    labelCol={{ span: 3 }}
+                                    wrapperCol={{ span: 21 }}
+                                    labelAlign="left"
+                                    colon={false}
+                                >
+                                    {getAdLabel.data?.map((item: { labelCategory: string, label: string[], labelTypeName: string }, index: number) => <Form.Item className={style.toggleLabelForm} label={<strong>{item.labelCategory}</strong>} key={index}>
+                                        <div className={style.toggleLabels}>
+                                            {item.label.map((text, tIndex) => {
+                                                let activeIndex = oldValue?.findIndex(i => i.content === text)
+                                                return <div
+                                                    key={tIndex}
+                                                    className={`${style.toggleLabel} ${(activeIndex !== -1) ? style.active : ''}`}
+                                                    onClick={() => {
+                                                        let newValue: { content: string, type: LABEL_TYPE_ENUM }[] = JSON.parse(JSON.stringify(oldValue))
+                                                        if (activeIndex !== -1) {
+                                                            newValue = newValue.filter((_, index) => activeIndex !== index)
+                                                        } else {
+                                                            newValue.push({ content: text, type: (item.labelTypeName === '普通文本标签' ? 'LABEL_TYPE_COMMON' : 'LABEL_TYPE_UNKNOWN') as LABEL_TYPE_ENUM })
+                                                        }
+                                                        setOldValue(newValue)
+                                                    }}
+                                                >{text}</div>
+                                            })}
+                                        </div>
+                                    </Form.Item>)}
+                                </Form>
                             </div>
-                            <Form
-                                name="basicSelectLabel"
-                                labelCol={{ span: 3 }}
-                                wrapperCol={{ span: 21 }}
-                                labelAlign="left"
-                                colon={false}
+                        </Spin>
+                    },
+                    {
+                        label: '新建标签', key: '2', children: <div className={style.spauiInputGroup} style={{ borderColor: isFocus ? '#296bef' : '#dfe1e6' }}>
+                            {oldValue?.filter(item => item.type === 'LABEL_TYPE_CUSTOMIZETEXT' as LABEL_TYPE_ENUM)?.map((item, tIndex) => <div
+                                key={tIndex}
+                                className={`${style.toggleLabel} ${style.customizeLabel}`}
                             >
-                                {getAdLabel.data?.map((item: { labelCategory: string, label: string[], labelTypeName: string }, index: number) => <Form.Item className={style.toggleLabelForm} label={<strong>{item.labelCategory}</strong>} key={index}>
-                                    <div className={style.toggleLabels}>
-                                        {item.label.map((text, tIndex) => {
-                                            let activeIndex = oldValue?.findIndex(i => i.content === text)
-                                            return <div
-                                                key={tIndex}
-                                                className={`${style.toggleLabel} ${(activeIndex !== -1) ? style.active : ''}`}
-                                                onClick={() => {
-                                                    let newValue: { content: string, type: LABEL_TYPE_ENUM }[] = JSON.parse(JSON.stringify(oldValue))
-                                                    if (activeIndex !== -1) {
-                                                        newValue = newValue.filter((_, index) => activeIndex !== index)
-                                                    } else {
-                                                        newValue.push({ content: text, type: (item.labelTypeName === '普通文本标签' ? 'LABEL_TYPE_COMMON' : 'LABEL_TYPE_UNKNOWN') as LABEL_TYPE_ENUM })
-                                                    }
-                                                    setOldValue(newValue)
-                                                }}
-                                            >{text}</div>
-                                        })}
-                                    </div>
-                                </Form.Item>)}
-                            </Form>
-                        </div>
-                    </Spin>
-                </Tabs.TabPane>
-                <Tabs.TabPane tab="新建标签" key="2" >
-                    <div className={style.spauiInputGroup} style={{ borderColor: isFocus ? '#296bef' : '#dfe1e6' }}>
-                        {oldValue?.filter(item => item.type === 'LABEL_TYPE_CUSTOMIZETEXT' as LABEL_TYPE_ENUM)?.map((item, tIndex) => <div
-                            key={tIndex}
-                            className={`${style.toggleLabel} ${style.customizeLabel}`}
-                        >
-                            <Space>
-                                <span>{item.content}</span>
-                                <CloseOutlined className={style.clearLable} onClick={() => setOldValue(oldValue.filter((_, index) => tIndex !== index))} />
-                            </Space>
-                        </div>)}
-                        <div className={style.divInput}>
-                            <Input
-                                placeholder="请输入自定义标签文案,按回车键生成标签,单标签 2-7 字"
-                                bordered={false}
-                                onFocus={() => setIsFocus(true)}
-                                onBlur={() => setIsFocus(false)}
-                                value={customizetext}
-                                maxLength={7}
-                                minLength={2}
-                                onChange={(e) => setCustomizetext(e.target.value)}
-                                onPressEnter={() => {
-                                    if (customizetext) {
-                                        if (oldValue.some(item => item.content === customizetext)) {
-                                            message.error('存在相同名称标签')
-                                            return
+                                <Space>
+                                    <span>{item.content}</span>
+                                    <CloseOutlined className={style.clearLable} onClick={() => setOldValue(oldValue.filter((_, index) => tIndex !== index))} />
+                                </Space>
+                            </div>)}
+                            <div className={style.divInput}>
+                                <Input
+                                    placeholder="请输入自定义标签文案,按回车键生成标签,单标签 2-7 字"
+                                    bordered={false}
+                                    onFocus={() => setIsFocus(true)}
+                                    onBlur={() => setIsFocus(false)}
+                                    value={customizetext}
+                                    maxLength={7}
+                                    minLength={2}
+                                    onChange={(e) => setCustomizetext(e.target.value)}
+                                    onPressEnter={() => {
+                                        if (customizetext) {
+                                            if (oldValue.some(item => item.content === customizetext)) {
+                                                message.error('存在相同名称标签')
+                                                return
+                                            }
+                                            let yyLables = getAdLabel?.data?.reduce((pre: any, cur: { label: any }) => [...pre, ...(cur.label || [])], [])
+                                            if (yyLables.some((item: string) => item === customizetext)) {
+                                                message.warn(`标签【${customizetext}】为已有标签,已为您自动选择`)
+                                                return
+                                            }
+                                            let newValue: { content: string, type: LABEL_TYPE_ENUM }[] = JSON.parse(JSON.stringify(oldValue))
+                                            newValue.push({ content: customizetext, type: 'LABEL_TYPE_CUSTOMIZETEXT' as LABEL_TYPE_ENUM })
+                                            setOldValue(newValue)
+                                            setCustomizetext(undefined)
+                                        } else {
+                                            message.error('请填写内容')
                                         }
-                                        let yyLables = getAdLabel?.data?.reduce((pre: any, cur: { label: any }) => [...pre, ...(cur.label || [])], [])
-                                        if (yyLables.some((item: string) => item === customizetext)) {
-                                            message.warn(`标签【${customizetext}】为已有标签,已为您自动选择`)
-                                            return
-                                        }
-                                        let newValue: { content: string, type: LABEL_TYPE_ENUM }[] = JSON.parse(JSON.stringify(oldValue))
-                                        newValue.push({ content: customizetext, type: 'LABEL_TYPE_CUSTOMIZETEXT' as LABEL_TYPE_ENUM })
-                                        setOldValue(newValue)
-                                        setCustomizetext(undefined)
-                                    } else {
-                                        message.error('请填写内容')
-                                    }
-                                }}
-                            />
+                                    }}
+                                />
+                            </div>
                         </div>
-                    </div>
-                </Tabs.TabPane>
-            </Tabs>
+                    }
+                ]}
+            />
         </Modal>}
     </>
 }

+ 39 - 6
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeConversionAssistant.tsx

@@ -10,12 +10,13 @@ import TextAideInput from "@/pages/launchSystemV3/components/TextAideInput";
 import styles from '../Material/index.less'
 import SelectLabel from "./SelectLable"
 import SelectCloudNew from "@/pages/launchSystemV3/material/cloudNew/selectCloudNew";
+import SelectBarrage from "./SelectBarrage";
 
 /**
  * 营销组件
  * @returns 
  */
-const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean }> = ({ automaticSiteEnabled }) => {
+const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean, putInType?: 'NOVEL' | 'GAME' }> = ({ automaticSiteEnabled, putInType }) => {
 
     /**************************************/
     const { creativeComponents, form, isUpdate, setIsUpdate } = useContext(DispatchDynamic)!;
@@ -135,7 +136,13 @@ const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean }>
             linkNameEnumeration = (linkNameType.enumProperty.enumeration as { value: string, description: string }[]).map(item => ({ label: item.description, value: item.value }))
             linkNamePageType = textLink?.children?.page_type
             if (linkNamePageType) {
-                linkNamePageTypeEnumeration = (linkNamePageType?.enumProperty?.enumeration as { value: string, description: string }[]).filter(item => item.value === pageSpecPageType).map(item => ({ label: item.description, value: item.value }))
+                linkNamePageTypeEnumeration = (linkNamePageType?.enumProperty?.enumeration as { value: string, description: string }[]).filter(item => item.value === pageSpecPageType || item.value === 'PAGE_TYPE_WECHAT_OFFICIAL_ACCOUNT_DETAIL').map(item => ({ label: item.description, value: item.value }))
+            }
+            if (putInType === 'GAME') {
+                let jumpInfoPageType = creativeComponents?.jump_info?.children?.page_type?.enumProperty?.enumeration
+                if (jumpInfoPageType?.length && jumpInfoPageType?.length !== linkNamePageType?.enumProperty?.enumeration?.length) {
+                    return
+                }
             }
 
             return <Form.Item style={{ marginBottom: 0 }}>
@@ -186,7 +193,7 @@ const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean }>
             </Form.Item>
         }
         return null
-    }, [creativeComponents?.text_link, pageSpec, textLinkShow, creativeTemplateId, automaticSiteEnabled])
+    }, [creativeComponents?.text_link, pageSpec, textLinkShow, creativeTemplateId, automaticSiteEnabled, putInType])
 
     /** 行动按钮 */
     const actionButtonContent = useMemo(() => {
@@ -270,11 +277,36 @@ const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean }>
             const list = label.children.list
 
             return <Form.Item name={'creativeLabelDTOS'} style={{ marginTop: 16, marginBottom: 0 }} label={<strong>&nbsp;&nbsp;&nbsp;标签{label.required ? '' : '(选填)'}</strong>}>
-                <SelectLabel arrayProperty={list.arrayProperty} />
+                <SelectLabel arrayProperty={list.arrayProperty} putInType={putInType} />
+            </Form.Item>
+        }
+        return null
+    }, [creativeComponents?.label, cardType, putInType])
+
+    /** 弹幕 */
+    const barrageContent = useMemo(() => {
+        let barrage = creativeComponents?.barrage;
+        if (barrage) {
+            return <Form.Item 
+                name={'creativeBarrageDTOS'} 
+                style={{ marginTop: 16, marginBottom: 0 }} 
+                label={<strong>&nbsp;&nbsp;&nbsp;弹幕{barrage.required ? '' : '(选填)'}</strong>}
+                rules={[
+                    {
+                        required: barrage.required, validator(_, value) {
+                            if (value && (value?.length < 3 || value?.length > 20)) {
+                                return Promise.reject('弹幕数量须为3-20条')
+                            }
+                            return Promise.resolve()
+                        }
+                    }
+                ]}
+            >
+                <SelectBarrage arrayProperty={{ minNumber: 0, maxNumber: 20 }} putInType={putInType} />
             </Form.Item>
         }
         return null
-    }, [creativeComponents?.label, cardType])
+    }, [creativeComponents?.barrage, putInType])
 
     /** 数据外显 */
     const showDataContent = useMemo(() => {
@@ -562,7 +594,7 @@ const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean }>
         }
     }, [cardType])
 
-    if (Object.keys(creativeComponents).some(key => ['text_link', 'action_button', 'show_data', 'floating_zone', 'label'].includes(key))) {
+    if (Object.keys(creativeComponents).some(key => ['text_link', 'action_button', 'show_data', 'floating_zone', 'label', 'barrage'].includes(key))) {
         return <Card
             title={<strong style={{ fontSize: 18 }}>营销组件</strong>}
             className="cardResetCss"
@@ -610,6 +642,7 @@ const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean }>
             {textLinkContent}
             {actionButtonContent}
             {labelContent}
+            {barrageContent}
             {showDataContent}
             {floatingZoneContent}
 

+ 13 - 10
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeTemplateContent.tsx

@@ -1,17 +1,18 @@
-import { Button, Card, Form, Radio, Space } from "antd"
+import { Button, Card, Form, Radio, Space, Tooltip } from "antd"
 import React, { useContext, useMemo } from "react"
 import { DispatchDynamic } from "./newDynamic";
 import New1Radio from "@/pages/launchSystemV3/components/New1Radio";
 import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
 import BrandImage from "@/pages/launchSystemV3/components/BrandImage";
 import { SelectProfiles } from "@/pages/launchSystemV3/tencenTasset/profiles";
+import { PAGE_TYPE_ENUM } from "../../const";
 
 
 /**
  * 创意内容
  * @returns 
  */
-const CreativeTemplateContent: React.FC<{ automaticSiteEnabled: boolean }> = ({ automaticSiteEnabled }) => {
+const CreativeTemplateContent: React.FC<{ automaticSiteEnabled: boolean, putInType?: 'NOVEL' | 'GAME' }> = ({ automaticSiteEnabled, putInType }) => {
 
     /******************************************/
     const { creativeComponents, form, adgroups: { siteSet } } = useContext(DispatchDynamic)!;
@@ -40,12 +41,12 @@ 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", "PAGE_TYPE_OFFICIAL", "PAGE_TYPE_WECHAT_MINI_PROGRAM"].includes(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", "PAGE_TYPE_WECHAT_MINI_PROGRAM", "PAGE_TYPE_WECHAT_MINI_GAME"].includes(item.value) }))
                     break
             }
         })
-        
-        if (!pageTypeList.some(item => item.value === "PAGE_TYPE_OFFICIAL") && creativeTemplateId !== 910) {
+
+        if (!pageTypeList.some(item => item.value === "PAGE_TYPE_OFFICIAL") && creativeTemplateId !== 910 && putInType !== 'GAME') {
             pageTypeList.push({ label: "灵鹊落地页", value: "PAGE_TYPE_OFFICIAL", disabled: false })
         }
 
@@ -154,18 +155,20 @@ const CreativeTemplateContent: React.FC<{ automaticSiteEnabled: boolean }> = ({
                                 </Space>
                             ))}
                             {pageSpec?.length < jumpInfoNumber && <Form.Item>
-                                <Button type="dashed" onClick={() => add({ pageId: null, pageType: pageSpec?.[0]?.pageType || 'PAGE_TYPE_WECHAT_CANVAS', overrideCanvasHeadOption: 'OPTION_CREATIVE_OVERRIDE_CANVAS' })} block icon={<PlusOutlined />}>
+                                {pageSpec?.[0]?.pageType === "PAGE_TYPE_WECHAT_MINI_GAME" ? <Tooltip placement="top" title={`「${PAGE_TYPE_ENUM[pageSpec?.[0]?.pageType as keyof typeof PAGE_TYPE_ENUM]}」仅支持单独使用,如需添加多组,请修改跳转类型。`}>
+                                    <Button type="dashed" disabled block icon={<PlusOutlined />}>
+                                        还可以添加 {jumpInfoNumber - pageSpec?.length} 组
+                                    </Button>
+                                </Tooltip> : <Button type="dashed" onClick={() => add({ pageId: null, pageType: pageSpec?.[0]?.pageType || 'PAGE_TYPE_WECHAT_CANVAS', overrideCanvasHeadOption: 'OPTION_CREATIVE_OVERRIDE_CANVAS' })} block icon={<PlusOutlined />}>
                                     还可以添加 {jumpInfoNumber - pageSpec?.length} 组
-                                </Button>
+                                </Button>}
                             </Form.Item>}
                         </>
                     )}
                 </Form.List>
             </Form.Item>
-
-
         </>
-    }, [creativeComponents, pageType, pageSpec, siteSet, creativeTemplateId])
+    }, [creativeComponents, pageType, pageSpec, siteSet, creativeTemplateId, putInType])
 
     return <Card
         title={<strong style={{ fontSize: 18 }}>创意内容</strong>}

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

@@ -15,10 +15,10 @@ import { useAjax } from "@/Hook/useAjax"
 const Dynamic: React.FC<{ creativeTemplateAppellation?: string, creativeTemplateStyle?: string }> = ({ creativeTemplateAppellation, creativeTemplateStyle }) => {
 
     /***************************************/
-    const { addelivery, setAddelivery, clearData, setAccountCreateLogs, accountCreateLogs } = useContext(DispatchAddelivery)!;
+    const { addelivery, setAddelivery, clearData, setAccountCreateLogs, accountCreateLogs, putInType } = useContext(DispatchAddelivery)!;
     const { adgroups, dynamic: dynamicData } = addelivery;
     const { deliveryMode, creativeTemplateId, dynamicCreativeName, creativeComponents, configuredStatus } = dynamicData
-    const { textLink, actionButton, showData, brand, mainJumpInfo, floatingZoneComponent, creativeLabelDTOS } = creativeComponents || {}
+    const { textLink, actionButton, showData, brand, mainJumpInfo, floatingZoneComponent, creativeLabelDTOS, creativeBarrageDTOS } = creativeComponents || {}
     const [newVisible, setNewVisible] = useState<boolean>(false);
     const [profileData, setprofileData] = useState<any>()
 
@@ -79,6 +79,10 @@ const Dynamic: React.FC<{ creativeTemplateAppellation?: string, creativeTemplate
                         <p style={{ fontWeight: 'bold', color: '#000' }}>标签:</p>
                         {creativeLabelDTOS.map((item: { content: string }, index: number) => <Tag key={index}>{item.content}</Tag>)}
                     </>}
+                    {creativeBarrageDTOS?.length > 0 && <>
+                        <p style={{ fontWeight: 'bold', color: '#000' }}>弹幕:</p>
+                        {creativeBarrageDTOS.map((item: { text: string }, index: number) => <Tag key={index}>{item.text}</Tag>)}
+                    </>}
                     {showData?.length > 0 && <>
                         <p style={{ fontWeight: 'bold', color: '#000' }}>数据外显:开启</p>
                         <p>数据类型:{CONVERSION_DATA_ENUM[showData?.[0]?.value?.conversionDataType as keyof typeof CONVERSION_DATA_ENUM]}</p>
@@ -99,9 +103,11 @@ const Dynamic: React.FC<{ creativeTemplateAppellation?: string, creativeTemplate
                 <Button disabled={!(adgroups && Object.keys(adgroups)?.length > 0)} type="link" icon={<EditOutlined />} style={{ padding: 0, fontSize: 12 }} onClick={() => setNewVisible(true)}>编辑</Button>
             </div>
         </div>
-
+        
+        {/* 添加创意 */}
         {newVisible && <NewDynamic
             visible={newVisible}
+            putInType={putInType}
             creativeTemplateStyle={creativeTemplateStyle}
             value={dynamicData}
             onClose={() => {

+ 53 - 13
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/newDynamic.tsx

@@ -17,6 +17,7 @@ import CreativeTemplateSetup from "./creativeTemplateSetup";
 export const DispatchDynamic = React.createContext<PULLIN.DynamicReactContent | null>(null);
 
 interface Props {
+    putInType?: 'NOVEL' | 'GAME'
     value?: any,
     visible?: boolean
     creativeTemplateStyle?: string,
@@ -29,7 +30,7 @@ interface Props {
  * @param param0 
  * @returns 
  */
-const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChange, creativeTemplateStyle: oldCreativeTemplateStyle }) => {
+const NewDynamic: React.FC<Props> = ({ putInType, value: newValue, visible, onClose, onChange, creativeTemplateStyle: oldCreativeTemplateStyle }) => {
 
     /**********************************/
     const { addelivery, setMaterialData, setTextData } = useContext(DispatchAddelivery)!;
@@ -42,7 +43,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
     const [adcreativeTemplateList, setAdcreativeTemplateList] = useState<PULLIN.AdcreativeTemplateList[]>([])
     const [creativeComponents, setCreativeComponents] = useState<any>({})
     const [isUpdate, setIsUpdate] = useState<boolean>(false)
-    const { marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec, automaticSiteEnabled } = adgroups
+    const { marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec, automaticSiteEnabled, marketingSubGoal } = adgroups
 
     const [newMaterialData, setNewMaterialData] = useState<any>({}) // 素材数据
     const [newTextData, setNewTextData] = useState<any>({})
@@ -55,13 +56,18 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
 
     useEffect(() => {
         if (deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') { // 自定义创意
-            getCreativeTemplateList.run({
+            let params: any = {
                 marketingGoal,
                 marketingTargetType: marketingAssetOuterSpec.marketingTargetType,
                 marketingCarrierType,
                 siteSet,
-                wechatSceneSpecPosition: sceneSpec?.wechatPosition || []
-            }).then((res: any) => {
+                wechatSceneSpecPosition: sceneSpec?.wechatPosition || [],
+                taskType: putInType
+            }
+            if (putInType === 'GAME') {
+                params.marketingSubGoal = marketingSubGoal
+            }
+            getCreativeTemplateList.run(params).then((res: any) => {
                 let newArr: any = []
                 let newData: any[] = []
                 // 过滤掉相同的和即将下线的
@@ -135,7 +141,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
         } else if (deliveryMode === 'DELIVERY_MODE_COMPONENT') {  // 组件化创意
             getTemplate()
         }
-    }, [deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, value, automaticSiteEnabled])
+    }, [deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, value, automaticSiteEnabled, putInType])
 
     useEffect(() => {
         if (!(value && Object.keys(value).length > 0) && adcreativeTemplateList?.length > 0 && marketingGoalTypeList?.length > 0) {
@@ -172,6 +178,10 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
             } else {
                 params.siteSet = siteSet
             }
+            params.taskType = putInType
+            if (putInType === 'GAME') {
+                params.marketingSubGoal = marketingSubGoal
+            }
             getCreativeDetails.run(params).then(res => {
                 if (res?.adcreativeTemplateStructAdpermits?.length > 0) {
                     let adcreativeTemplateStructAdpermits = res?.adcreativeTemplateStructAdpermits[0]
@@ -193,6 +203,26 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
         let creativeTemplateId = adcreativeTemplateStructAdpermits?.creativeTemplateId
         let creativeTemplateAppellation = adcreativeTemplateStructAdpermits?.creativeTemplateAppellation
         let result = processData(creativeComponents);
+        const noBarrage = [713, 714, 722, 618, 1529, 2109, 972, 2107]
+        const siteSetBarrage = [
+            "SITE_SET_KANDIAN",
+            "SITE_SET_MOBILE_UNION",
+            "SITE_SET_QQ_MUSIC_GAME",
+            "SITE_SET_SEARCH_SCENE",
+            "SITE_SET_TENCENT_NEWS",
+            "SITE_SET_TENCENT_VIDEO"
+        ]
+        if (!result.barrage && (
+            automaticSiteEnabled ?
+                deliveryMode === 'DELIVERY_MODE_COMPONENT' ?
+                    creativeTemplateId ? !noBarrage.includes(creativeTemplateId) : true :
+                    !noBarrage.includes(creativeTemplateId) :
+                siteSet.some((item: string) => siteSetBarrage.includes(item)) ?
+                    !noBarrage.includes(creativeTemplateId) :
+                    false
+        )) {
+            result.barrage = { required: false }
+        }
         console.log('result-->', result);
         setCreativeComponents(result)
 
@@ -213,7 +243,9 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
         if (!(value && Object.keys(value).length > 0) || isAntTemplateId) {
             let values: any = {
                 dynamicCreativeName: creativeTemplateAppellation ? '自定义创意' + '_' + creativeTemplateAppellation + '_' + localStorage.getItem('userId') + '_' + randomString(true, 3, 5) : '动态创意' + '_' + localStorage.getItem('userId') + '_' + randomString(true, 3, 5),
-                pageSpec: [{
+                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'
                 }],
@@ -238,7 +270,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
                             value: {
                                 linkNameType: linkNameEnumeration?.[0]?.value,
                                 jumpInfo: {
-                                    pageType: 'PAGE_TYPE_WECHAT_CANVAS'
+                                    pageType: marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? "PAGE_TYPE_WECHAT_MINI_GAME" : 'PAGE_TYPE_WECHAT_CANVAS'
                                 }
                             }
                         }
@@ -252,7 +284,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
                             value: {
                                 buttonText: butttonTextEnumeration?.[0]?.value,
                                 jumpInfo: {
-                                    pageType: 'PAGE_TYPE_WECHAT_CANVAS'
+                                    pageType: marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? "PAGE_TYPE_WECHAT_MINI_GAME" : 'PAGE_TYPE_WECHAT_CANVAS'
                                 }
                             }
                         }
@@ -301,6 +333,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
             showData,
             floatingZone,
             creativeLabelDTOS,
+            creativeBarrageDTOS,
             ...surplusValues
         } = values
 
@@ -413,6 +446,11 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
             creativeComponents.creativeLabelDTOS = creativeLabelDTOS
         }
 
+        // 弹幕
+        if (creativeBarrageDTOS?.length) {
+            creativeComponents.creativeBarrageDTOS = creativeBarrageDTOS
+        }
+
         // 数据外显
         if (showDataShow) {
             creativeComponents.showData = [showData]
@@ -456,13 +494,15 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
                     showData,
                     mainJumpInfo,
                     floatingZoneComponent,
-                    creativeLabelDTOS
+                    creativeLabelDTOS,
+                    creativeBarrageDTOS
                 },
                 ...surplusValues
             } = JSON.parse(JSON.stringify(value))
             let dynamicValues: any = {
                 ...surplusValues,
-                creativeLabelDTOS
+                creativeLabelDTOS,
+                creativeBarrageDTOS
             }
             // if (value.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
             //     getTemplate(value.creativeTemplateId)
@@ -629,9 +669,9 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
 
                         {Object.keys(creativeComponents).length > 0 && <>
                             {/* 创意内容 */}
-                            <CreativeTemplateContent automaticSiteEnabled={automaticSiteEnabled} />
+                            <CreativeTemplateContent automaticSiteEnabled={automaticSiteEnabled} putInType={putInType} />
                             {/* 营销组件 */}
-                            <CreativeConversionAssistant automaticSiteEnabled={automaticSiteEnabled} />
+                            <CreativeConversionAssistant automaticSiteEnabled={automaticSiteEnabled} putInType={putInType} />
                             {/* 创意设置 */}
                             <CreativeTemplateSetup />
                         </>}

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

@@ -13,7 +13,7 @@ const { Title } = Typography;
 const Material: React.FC<{ adData?: any[] }> = ({ adData }) => {
 
     /***************************************/
-    const { materialData, addelivery, setAddelivery, clearData, accountCreateLogs } = useContext(DispatchAddelivery)!;
+    const { materialData, addelivery, setAddelivery, clearData, accountCreateLogs, putInType } = useContext(DispatchAddelivery)!;
     const { dynamic, dynamicMaterialDTos, mediaType, targeting, adgroups: { marketingAssetOuterSpec, marketingCarrierType }, dynamicCreativesTextDTOS } = addelivery
     const { creativeTemplateId, deliveryMode } = dynamic
     const [adLength, setAdLength] = useState<number>(0)
@@ -175,6 +175,7 @@ const Material: React.FC<{ adData?: any[] }> = ({ adData }) => {
                     选择素材
                 </Button>
                 {dynamic && deliveryMode && <SaveUseImg
+                    putInType={putInType}
                     type={deliveryMode === "DELIVERY_MODE_CUSTOMIZE" ? creativeTemplateId : deliveryMode}
                     onChange={({ dynamicMaterialDTos, mediaType, dynamicCreativesTextDTOS }) => {
                         setAddelivery({ ...addelivery, dynamicMaterialDTos, mediaType: mediaType as any, dynamicCreativesTextDTOS })

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

@@ -10,7 +10,7 @@ const { Title, Text } = Typography;
 const MaterialText: React.FC = () => {
 
     /*************************************/
-    const { textData, addelivery, setAddelivery, clearData } = useContext(DispatchAddelivery)!;
+    const { textData, addelivery, setAddelivery, clearData, putInType } = useContext(DispatchAddelivery)!;
     const { dynamic, dynamicCreativesTextDTOS, dynamicMaterialDTos, mediaType } = addelivery
 
     const [addVisible, setAddVisible] = useState<boolean>(false)
@@ -70,6 +70,7 @@ const MaterialText: React.FC = () => {
             mediaType={mediaType}
             textData={textData}
             visible={addVisible}
+            putInType={putInType}
             deliveryMode={dynamic?.deliveryMode}
             onClose={() => {
                 setAddVisible(false)

+ 3 - 2
src/pages/launchSystemV3/tencentAdPutIn/create/MaterialText/newText.tsx

@@ -18,6 +18,7 @@ interface Props {
     visible?: boolean
     onClose?: () => void
     onChange?: (value: any) => void
+    putInType?: 'NOVEL' | 'GAME'
 }
 
 /**
@@ -25,7 +26,7 @@ interface Props {
  * @param param0 
  * @returns 
  */
-const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData, dynamicMaterialDTos, mediaType, deliveryMode }) => {
+const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData, dynamicMaterialDTos, mediaType, deliveryMode, putInType }) => {
 
     /*************************************/
     const [form] = Form.useForm();
@@ -239,7 +240,7 @@ const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData,
                                                         }
                                                     }]}
                                                 >
-                                                    <TextAideInput placeholder={'请输入' + item.label} style={{ width: 580 }} maxTextLength={item.restriction.textRestriction.maxLength} />
+                                                    <TextAideInput placeholder={'请输入' + item.label} style={{ width: 580 }} maxTextLength={item.restriction.textRestriction.maxLength} putInType={putInType} />
                                                 </Form.Item>
                                             ))}
                                             {deliveryMode === 'DELIVERY_MODE_COMPONENT' && item.arrayProperty?.maxNumber > 1 && textDto?.[num]?.[item.value]?.length < item.arrayProperty?.maxNumber && <Form.Item style={{ marginTop: 6, marginBottom: 0 }}>

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

@@ -81,7 +81,7 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
 
     return <div className={`${style.settingsBody_content_row} ${style.row6}`}>
         <div className={style.title}>
-            <span>{pageType === 'PAGE_TYPE_OFFICIAL' ? '灵鹊落地页(官方落地页)' : pageType === "PAGE_TYPE_WECHAT_MINI_PROGRAM" ? '微信小程序' : '原生推广页'}</span>
+            <span>{PAGE_TYPE_ENUM[pageType as keyof typeof PAGE_TYPE_ENUM] || '原生推广页'}</span>
         </div>
         <div className={style.detail}>
             <div className={style.detail_body}>
@@ -94,6 +94,9 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
                             <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} style={{ margin: 0, fontSize: 12 }} imageStyle={{ height: 18 }} />}
                     </div>
                 })}
+                {pageType === "PAGE_TYPE_WECHAT_MINI_GAME" && <div className={style.ad_config}>
+                    <div className={style.ad_config_item}>无需设置落地页</div>
+                </div>}
             </div>
             <div className={style.detail_footer}>
                 <Button

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

@@ -1,12 +1,13 @@
 import { useAjax } from "@/Hook/useAjax";
 import { addV3StrategyApi } from "@/services/adqV3";
-import { Button, Dropdown, Form, Input, Menu, message, Modal } from "antd";
+import { Button, Dropdown, Form, Input, message, Modal } from "antd";
 import React, { useState } from "react";
 
 
 
 interface Props {
     addelivery: PULLIN.AddeliveryProps
+    putInType?: 'NOVEL' | 'GAME'
 }
 
 /**
@@ -14,7 +15,7 @@ interface Props {
  * @param param0 
  * @returns 
  */
-const Save: React.FC<Props> = ({ addelivery: { dynamic: { deliveryMode, creativeTemplateId }, dynamicMaterialDTos, mediaType, dynamicCreativesTextDTOS } }) => {
+const Save: React.FC<Props> = ({ addelivery: { dynamic: { deliveryMode, creativeTemplateId }, dynamicMaterialDTos, mediaType, dynamicCreativesTextDTOS }, putInType }) => {
 
     /*********************************/
     const [form] = Form.useForm();
@@ -39,7 +40,7 @@ const Save: React.FC<Props> = ({ addelivery: { dynamic: { deliveryMode, creative
                 }
                 params.strategyValue = JSON.stringify({ mediaType, dynamicMaterialDTos, deliveryMode, creativeTemplateId, dynamicCreativesTextDTOS })
             }
-            addV3Strategy.run(params).then(res => {
+            addV3Strategy.run({ ...params, taskType: putInType }).then(res => {
                 if (res) {
                     message.success('保存成功')
                     form.resetFields()

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

@@ -10,12 +10,13 @@ import { DiffOutlined } from "@ant-design/icons"
 interface Props {
     type: string,
     onChange?: (value: { dynamicMaterialDTos: any, mediaType: number, deliveryMode: string, creativeTemplateId: number, dynamicCreativesTextDTOS: any }) => void
+    putInType?: 'NOVEL' | 'GAME'
 }
 /**
  * 使用素材
  * @returns 
  */
-const SaveUseImg: React.FC<Props> = ({ onChange, type }) => {
+const SaveUseImg: React.FC<Props> = ({ onChange, type, putInType }) => {
 
     /***********************/
     const [visible, setVisible] = useState<boolean>(false)
@@ -31,10 +32,10 @@ const SaveUseImg: React.FC<Props> = ({ onChange, type }) => {
         if (visible) {
             getList()
         }
-    }, [visible, queryFormNew])
+    }, [visible, queryFormNew, putInType])
 
     const getList = () => {
-        getStrategy.run(queryFormNew)
+        getStrategy.run({ ...queryFormNew, taskType: putInType })
     }
 
     const use = () => {

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

@@ -12,12 +12,13 @@ interface Props {
         materialData: any,
         textData: any
     }
+    putInType?: 'NOVEL' | 'GAME'
 }
 /**
  * 存为策略组
  * @returns 
  */
-const TacticsS: React.FC<Props> = ({ strategyValue }) => {
+const TacticsS: React.FC<Props> = ({ strategyValue, putInType }) => {
 
     /*********************************/
     const [form] = Form.useForm();
@@ -32,7 +33,7 @@ const TacticsS: React.FC<Props> = ({ strategyValue }) => {
 
     const handleOk = () => {
         form.validateFields().then(values => {
-            addV3Strategy.run({ ...values, strategyValue: JSON.stringify(strategyValue), type: 'updateAd' }).then(res => {
+            addV3Strategy.run({ ...values, strategyValue: JSON.stringify(strategyValue), type: 'updateAd', taskType: putInType }).then(res => {
                 if (res) {
                     message.success('保存成功')
                     form.resetFields()

+ 3 - 3
src/pages/launchSystemV3/tencentAdPutIn/create/TacticsS/tableConfig.tsx

@@ -1,6 +1,6 @@
 import { Popconfirm, TableProps, Typography } from "antd";
 import React from "react";
-import { MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, SITE_SET_ENUM } from "../../const";
+import { MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_SUB_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, SITE_SET_ENUM } from "../../const";
 const { Paragraph } = Typography;
 
 export const Columns = (del: (id: number) => void): TableProps<any>['columns'] => {
@@ -71,8 +71,8 @@ export const Columns = (del: (id: number) => void): TableProps<any>['columns'] =
                 let strategyValue = b.strategyValue
                 let { adData } = JSON.parse(strategyValue)
                 return <span dangerouslySetInnerHTML={{
-                    __html: `营销目的:<span style="color: #52c41a">${MARKETING_GOAL_ENUM[adData?.[0]?.marketingGoal as keyof typeof MARKETING_GOAL_ENUM]}</span>,
-                    推广产品类型:<span style="color: #52c41a">${MARKETING_TARGET_TYPE_ENUM[adData?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]}</span>,
+                    __html: `营销目的:<span style="color: #52c41a">${b?.taskType === 'GAME' ?  MARKETING_SUB_GOAL_ENUM[adData?.[0]?.marketingSubGoal as keyof typeof MARKETING_SUB_GOAL_ENUM] : MARKETING_GOAL_ENUM[adData?.[0]?.marketingGoal as keyof typeof MARKETING_GOAL_ENUM]}</span>,
+                    推广产品类型:<span style="color: #52c41a">${b?.taskType === 'GAME' ? MARKETING_TARGET_TYPE_GAME_ENUM[adData?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM] : MARKETING_TARGET_TYPE_ENUM[adData?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]}</span>,
                     营销载体类型:<span style="color: #52c41a">${MARKETING_CARRIER_TYPE_ENUM[adData?.[0]?.marketingCarrierType as keyof typeof MARKETING_CARRIER_TYPE_ENUM]}</span>,
                     版位选择:<span style="color: #52c41a">${adData?.[0]?.automaticSiteEnabled ? '自动版位' : '选择特定版位'}</span>,
                     ${!adData?.[0]?.automaticSiteEnabled && `广告版位:<span style="color: #52c41a">${adData?.[0]?.siteSet.map((item: string | number) => SITE_SET_ENUM[item as keyof typeof SITE_SET_ENUM]).toString()}</span>`}

+ 5 - 4
src/pages/launchSystemV3/tencentAdPutIn/create/TacticsS/userTactics.tsx

@@ -9,13 +9,14 @@ import '../../index.less'
 interface Props {
     userId: number
     type: string,
+    putInType?: 'NOVEL' | 'GAME'
     onChange?: (value: any) => void
 }
 /**
  * 使用策略组
  * @returns 
  */
-const UserTactics: React.FC<Props> = ({ onChange, type, userId }) => {
+const UserTactics: React.FC<Props> = ({ onChange, type, userId, putInType }) => {
 
     /***********************/
     const [visible, setVisible] = useState<boolean>(false)
@@ -31,10 +32,10 @@ const UserTactics: React.FC<Props> = ({ onChange, type, userId }) => {
         if (visible) {
             getList()
         }
-    }, [visible, queryFormNew])
+    }, [visible, queryFormNew, putInType])
 
     const getList = () => {
-        getStrategy.run(queryFormNew)
+        getStrategy.run({ ...queryFormNew, taskType: putInType })
     }
 
     const use = () => {
@@ -44,7 +45,7 @@ const UserTactics: React.FC<Props> = ({ onChange, type, userId }) => {
     const handleOk = () => {
         if (selectedRowKeys.length > 0) {
             const hide = message.loading('正在检测广告是否被删除...', 0)
-            let {strategyValue} = selectedRowKeys[0]
+            let { strategyValue } = selectedRowKeys[0]
             let { adData } = JSON.parse(strategyValue)
             getAdqV3AdList.run({ pageNum: 1, pageSize: 100, useType: 1, userId, adgroupIdList: adData.map((item: { adgroupId: any; }) => item.adgroupId) }).then(res => {
                 if (res?.data) {

+ 80 - 8
src/pages/launchSystemV3/tencentAdPutIn/create/Target/addTarget.tsx

@@ -1,7 +1,7 @@
 import { Button, Card, Checkbox, Form, Input, Modal, Radio, Select, Space, Spin, Tooltip, TreeSelect, Typography, message } from "antd"
 import React, { useEffect, useState } from "react"
 import style from '../index.less'
-import { DEVICE_PRICE_ENUM, EDUCATION_ENUM, EXCLUDED_DIMENSION_ENUM, GENDER_ENUM, LOCATION_TYPES_ENUM, MARITAL_STATUS_ENUM, NETWORK_ENUM, OPTIMIZATIONGOAL_ENUM, USER_OS_ENUM, WECHAT_AD_NEHAVIOR_ENUM } from "../../const"
+import { DEVICE_PRICE_ENUM, EDUCATION_ENUM, EXCLUDED_DIMENSION_ENUM, GAME_CONSUMPTION_LEVEL_ENUM, GENDER_ENUM, LOCATION_TYPES_ENUM, MARITAL_STATUS_ENUM, NETWORK_ENUM, OPTIMIZATIONGOAL_ENUM, USER_OS_ENUM, WECHAT_AD_NEHAVIOR_ENUM, WECHAT_AD_NEHAVIOR_GAME_ENUM } from "../../const"
 import { QuestionCircleFilled } from "@ant-design/icons"
 import { getTargetingGagsApi } from "@/services/adqV3/global"
 import { useAjax } from "@/Hook/useAjax"
@@ -16,13 +16,14 @@ const { Title, Paragraph } = Typography;
 
 interface Props {
     isBackVal?: boolean
+    putInType?: 'NOVEL' | 'GAME'
     value?: any,
     visible?: boolean
     onClose?: () => void
     onChange?: (targeting?: any) => void
 }
 
-const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClose }) => {
+const AddTarget: React.FC<Props> = ({ isBackVal, putInType, value, visible, onChange, onClose }) => {
 
     /******************************/
     const [form] = Form.useForm();
@@ -37,10 +38,12 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
     const education = Form.useWatch('education', form);
     const networkType = Form.useWatch('networkType', form);
     const devicePrice = Form.useWatch('devicePrice', form);
+    const gameConsumptionLevel = Form.useWatch('gameConsumptionLevel', form);
     const excludedDimension = Form.useWatch(['excludedConvertedAudience', 'excludedDimension'], form);
     const conversionBehaviorList = Form.useWatch(['excludedConvertedAudience', 'conversionBehaviorList'], form);
     const actions = Form.useWatch(['wechatAdBehavior', 'actions'], form);
     const excludedActions = Form.useWatch(['wechatAdBehavior', 'excludedActions'], form);
+    const taskType: 'NOVEL' | 'GAME' = Form.useWatch('taskType', form) || putInType
 
     const [regionsList, setRegionsList] = useState<any[]>([])
     const [modelList, setModelList] = useState([])
@@ -158,8 +161,13 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
                 }
             }
         })
-        setOsList([{ label: 'iOS系统', value: 'IOS', children: iosChildren }, { label: 'Android系统', value: 'ANDROID', children: androidChildren }, { label: 'Harmony', value: 'HARMONY', children: harmonyChildren }])
-    }, [USER_OS_ENUM])
+        let newOsList = [{ label: 'iOS系统', value: 'IOS', children: iosChildren }, { label: 'Android系统', value: 'ANDROID', children: androidChildren }, { label: 'Harmony', value: 'HARMONY', children: harmonyChildren }]
+        if (taskType === 'GAME') {
+            newOsList.push({ label: 'Windows系统', value: 'WINDOWS', children: [] })
+            newOsList.push({ label: 'Mac系统', value: 'MAC', children: [] })
+        }
+        setOsList(newOsList)
+    }, [USER_OS_ENUM, taskType])
 
     const handleOk = async (values: any) => {
         console.log(values)
@@ -183,7 +191,9 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
             os,
             isExcludedOs,
             devicePrice,
+            gameConsumptionLevel,
             wechatAdBehaviorType,
+            taskType,
             ...surplusValues
         } = values
 
@@ -243,7 +253,12 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
         if (!devicePrice?.includes('0')) {
             targetValues.devicePrice = devicePrice
         }
+        // 游戏消费能力
+        if (!gameConsumptionLevel?.includes('0')) {
+            targetValues.gameConsumptionLevel = gameConsumptionLevel
+        }
         let targetingDTO = {
+            taskType: taskType || putInType,
             targetingName,
             description,
             accountId,
@@ -258,7 +273,7 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
 
         let checkData = await checkTargeting.run(value?.id ? { ...targetingDTO, id: value?.id } : targetingDTO)
         if (checkData?.[0]?.isSame) {
-            message.error('存在相同模板名称,请修改')
+            message.error('存在相同模板名称或内容,请修改')
             return
         }
         if (value?.id) {
@@ -296,6 +311,7 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
                 excludedOs,
                 userOs,
                 devicePrice,
+                gameConsumptionLevel,
                 wechatAdBehavior,
                 ...surplusValues
             } = JSON.parse(JSON.stringify(value))
@@ -307,6 +323,7 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
                 networkType: networkType || '0',
                 excludedConvertedAudience: excludedConvertedAudience || { excludedDimension: '0' },
                 devicePrice: devicePrice || '0',
+                gameConsumptionLevel: gameConsumptionLevel || '0',
                 wechatAdBehavior,
                 maritalStatus
             }
@@ -400,6 +417,7 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
             }}
             onFinish={handleOk}
             initialValues={{
+                taskType: 'NOVEL',
                 geoLocationType: '0',
                 maritalStatusType: '0',
                 deviceBrandModelType: '0',
@@ -414,6 +432,7 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
                 education: ['0'],
                 networkType: ['0'],
                 devicePrice: ['0'],
+                gameConsumptionLevel: ['0'],
                 excludedConvertedAudience: {
                     excludedDimension: '0'
                 },
@@ -423,6 +442,29 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
                 targetingName: (isBackVal ? '定向' : '定向模板') + '_' + localStorage.getItem('userId') + '_' + moment().format('MM_DD_HH:mm:ss')
             }}
         >
+            {!putInType && <Card
+                title={<strong style={{ fontSize: 18 }}>定向类型</strong>}
+                className="cardResetCss newCss"
+                bodyStyle={{ padding: '4px 6px' }}
+                style={{ marginBottom: 8 }}
+            >
+                <div className={style.newSpace}>
+                    <Form.Item name="taskType" label={<strong>投放类型</strong>} style={{ marginBottom: 0 }} rules={[{ required: true, message: '请选择定向类型' }]}>
+                        <Radio.Group onChange={(e) => {
+                            form.setFieldsValue({
+                                excludedConvertedAudience: {
+                                    excludedDimension: '0'
+                                },
+                                userOsType: '0',
+                                wechatAdBehaviorType: ['0']
+                            })
+                        }}>
+                            <Radio value="NOVEL">小说</Radio>
+                            <Radio value="GAME">游戏</Radio>
+                        </Radio.Group>
+                    </Form.Item>
+                </div>
+            </Card>}
             <Card
                 title={<strong style={{ fontSize: 18 }}>定向选择</strong>}
                 className="cardResetCss newCss"
@@ -622,7 +664,7 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
                     >
                         <Radio.Group>
                             <Radio value="0">不限</Radio>
-                            {Object.keys(EXCLUDED_DIMENSION_ENUM).map(key => {
+                            {Object.keys(EXCLUDED_DIMENSION_ENUM).filter(key => taskType === 'GAME' ? ['EXCLUDED_DIMENSION_UID', 'EXCLUDED_DIMENSION_BUSINESS_MANAGER', 'EXCLUDED_DIMENSION_COMPANY_ACCOUNT', 'EXCLUDED_DIMENSION_APP'].includes(key) : true).map(key => {
                                 return <Radio value={key} key={key}>{EXCLUDED_DIMENSION_ENUM[key as keyof typeof EXCLUDED_DIMENSION_ENUM]}</Radio>
                             })}
                         </Radio.Group>
@@ -686,6 +728,28 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
                     </div>}
                 </div>
 
+                {taskType === 'GAME' && <div className={style.newSpace}>
+                    <Form.Item
+                        name="gameConsumptionLevel"
+                        label={<strong>游戏消费能力</strong>}
+                        style={{ marginBottom: 0 }}
+                        getValueFromEvent={(e: string[]) => {
+                            if (e.length > 1 && !gameConsumptionLevel.includes('0') && e.includes('0')) {
+                                return ['0'];
+                            }
+                            return e.length > 0 ? (e.length > 1 && e.includes('0') ? e.filter(item => item !== '0') : e) : ['0'];
+                        }}
+                    >
+                        <Checkbox.Group
+                            options={[
+                                { label: '不限', value: '0' },
+                                ...Object.keys(GAME_CONSUMPTION_LEVEL_ENUM)?.map(key => ({ label: GAME_CONSUMPTION_LEVEL_ENUM[key as keyof typeof GAME_CONSUMPTION_LEVEL_ENUM], value: key }))
+                            ]}
+                        />
+                    </Form.Item>
+                </div>}
+
+
                 <div className={style.newSpace}>
                     <Form.Item name="userOsType" label={<strong>操作系统版本</strong>} style={{ marginBottom: 0 }}>
                         <Radio.Group>
@@ -771,13 +835,21 @@ const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClo
                         {wechatAdBehaviorType.includes('actions') && <>
                             <Title level={5} style={{ fontSize: 14 }}>再营销</Title>
                             <Form.Item style={{ marginBottom: 10 }} name={['wechatAdBehavior', 'actions']}>
-                                <Checkbox.Group options={Object.keys(WECHAT_AD_NEHAVIOR_ENUM).filter(item => !['WE_COM_CORP_ID_ADDED', 'WECHAT_WORK_CONTACTS_ADDED'].includes(item)).map(key => ({ label: WECHAT_AD_NEHAVIOR_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_ENUM], value: key, disabled: excludedActions?.some((k: string) => k === key) }))} />
+                                {taskType === 'GAME' ?
+                                    <Checkbox.Group options={Object.keys(WECHAT_AD_NEHAVIOR_GAME_ENUM).filter(item => !['WE_COM_CORP_ID_ADDED', 'WECHAT_WORK_CONTACTS_ADDED'].includes(item)).map(key => ({ label: WECHAT_AD_NEHAVIOR_GAME_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_GAME_ENUM], value: key, disabled: excludedActions?.some((k: string) => k === key) }))} />
+                                    :
+                                    <Checkbox.Group options={Object.keys(WECHAT_AD_NEHAVIOR_ENUM).filter(item => !['WE_COM_CORP_ID_ADDED', 'WECHAT_WORK_CONTACTS_ADDED'].includes(item)).map(key => ({ label: WECHAT_AD_NEHAVIOR_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_ENUM], value: key, disabled: excludedActions?.some((k: string) => k === key) }))} />
+                                }
                             </Form.Item>
                         </>}
                         {wechatAdBehaviorType.includes('excludedActions') && <>
                             <Title level={5} style={{ fontSize: 14 }}>排除营销</Title>
                             <Form.Item name={['wechatAdBehavior', 'excludedActions']}>
-                                <Checkbox.Group options={Object.keys(WECHAT_AD_NEHAVIOR_ENUM).map(key => ({ label: WECHAT_AD_NEHAVIOR_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_ENUM], value: key, disabled: actions?.some((k: string) => k === key) }))} />
+                                {taskType === 'GAME' ?
+                                    <Checkbox.Group options={Object.keys(WECHAT_AD_NEHAVIOR_GAME_ENUM).map(key => ({ label: WECHAT_AD_NEHAVIOR_GAME_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_GAME_ENUM], value: key, disabled: actions?.some((k: string) => k === key) }))} />
+                                    :
+                                    <Checkbox.Group options={Object.keys(WECHAT_AD_NEHAVIOR_ENUM).map(key => ({ label: WECHAT_AD_NEHAVIOR_ENUM[key as keyof typeof WECHAT_AD_NEHAVIOR_ENUM], value: key, disabled: actions?.some((k: string) => k === key) }))} />
+                                }
                             </Form.Item>
                         </>}
                         {excludedActions?.includes('WE_COM_CORP_ID_ADDED') && <>

+ 3 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/Target/dataItem.tsx

@@ -6,13 +6,14 @@ import { CloseOutlined, CopyOutlined, FormOutlined } from "@ant-design/icons"
 
 interface Props {
     targeting: any
+    taksType: 'NOVEL' | 'GAME',
     geoLocationList?: any,
     modelList?: any
     onModify?: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => void
     onClear?: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => void
     onCopy?: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => void
 }
-const DataItem: React.FC<Props> = ({ targeting, onModify, onClear, onCopy, geoLocationList, modelList }) => {
+const DataItem: React.FC<Props> = ({ targeting, taksType, onModify, onClear, onCopy, geoLocationList, modelList }) => {
 
 
     return <Popover
@@ -24,6 +25,7 @@ const DataItem: React.FC<Props> = ({ targeting, onModify, onClear, onCopy, geoLo
             data={targeting}
             geoLocationList={geoLocationList}
             modelList={modelList}
+            taskType={taksType}
         />}
     >
         <div className={style.dataItem}>

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

@@ -18,7 +18,7 @@ const Target: React.FC = () => {
 
     /***************************************/
     const { geoLocationList, modelList } = useModel('useLaunchV3.useTargeting')
-    const { addelivery, setAddelivery, clearData } = useContext(DispatchAddelivery)!;
+    const { addelivery, setAddelivery, clearData, putInType } = useContext(DispatchAddelivery)!;
     const { targeting, adgroups } = addelivery
     const [addVisible, setAddVisible] = useState<boolean>(false)
     const [addTemVisible, setAddTemVisible] = useState<boolean>(false)
@@ -39,11 +39,12 @@ const Target: React.FC = () => {
                 <div className={style.detail_body}>
                     {targeting?.length > 0 && <>
                         <Title level={5} style={{ fontSize: 12 }}>全部相同</Title>
-                        {targeting?.map((item: { targetingName: any; targeting: any, id?: number }, index: number) => <div key={index}>
+                        {targeting?.map((item: { targetingName: any; targeting: any, taskType: 'NOVEL' | 'GAME', id?: number }, index: number) => <div key={index}>
                             <DataItem
                                 geoLocationList={geoLocationList}
                                 modelList={modelList}
                                 key={index}
+                                taksType={item.taskType || putInType}
                                 targeting={{ targetingName: item.targetingName, ...item.targeting }}
                                 onClear={() => {
                                     let newTargeting: any[] = JSON.parse(JSON.stringify(targeting))
@@ -86,6 +87,7 @@ const Target: React.FC = () => {
         {addVisible && <SelectTarget
             value={targeting?.filter(item => item?.id)}
             visible={addVisible}
+            putInType={putInType}
             onClose={() => {
                 setAddVisible(false)
             }}
@@ -101,6 +103,7 @@ const Target: React.FC = () => {
         {addTemVisible && <AddTarget
             value={modifyDta}
             isBackVal={true}
+            putInType={putInType}
             visible={addTemVisible}
             onClose={() => {
                 setAddTemVisible(false)

+ 7 - 4
src/pages/launchSystemV3/tencentAdPutIn/create/Target/selectTarget.tsx

@@ -15,6 +15,7 @@ const { Title, Text } = Typography;
 
 interface Props {
     value?: any,
+    putInType?: 'NOVEL' | 'GAME'
     visible?: boolean
     onClose?: () => void
     onChange?: (value: any) => void
@@ -25,7 +26,7 @@ interface Props {
  * @param param0 
  * @returns 
  */
-const SelectTarget: React.FC<Props> = ({ value = [], visible, onChange, onClose }) => {
+const SelectTarget: React.FC<Props> = ({ value = [], putInType, visible, onChange, onClose }) => {
 
     /*****************************/
     const [distributionRule, setDistributionRule] = useState<'1' | '2'>('1')
@@ -66,17 +67,18 @@ const SelectTarget: React.FC<Props> = ({ value = [], visible, onChange, onClose
     }, [])
 
     useEffect(() => {
-        getTargetingList.run(queryParamsNew)
+        getTargetingList.run({...queryParamsNew, taskType: putInType})
     }, [queryParamsNew])
 
     /** 编辑 复制 */
     const editHandle = (data: any, isCopy?: boolean) => {
-        const { targetingName, targeting, description, id, accountId } = data
+        const { targetingName, targeting, description, id, accountId, taskType } = data
         let newModifyDta = {
             ...targeting,
             targetingName,
             description,
-            accountId: accountId || undefined
+            accountId: accountId || undefined,
+            taskType
         }
         if (!isCopy) {
             newModifyDta.id = id
@@ -186,6 +188,7 @@ const SelectTarget: React.FC<Props> = ({ value = [], visible, onChange, onClose
         {addTemVisible && <AddTarget
             value={modifyDta}
             visible={addTemVisible}
+            putInType={putInType}
             onClose={() => {
                 setAddTemVisible(false)
                 setModifyDta(undefined)

+ 3 - 2
src/pages/launchSystemV3/tencentAdPutIn/create/Target/tableConfig.tsx

@@ -23,6 +23,7 @@ export function TableConfig(geoLocationList: any, modelList: any, editHandle: (d
                             data={records?.targeting}
                             geoLocationList={geoLocationList}
                             modelList={modelList}
+                            taskType={records?.taskType}
                         />}
                     >
                         <QuestionCircleFilled />
@@ -79,8 +80,8 @@ export function TableConfig(geoLocationList: any, modelList: any, editHandle: (d
             width: 120,
             render: (a: any, b: any) => {
                 return <Space wrap>
-                    <Button type="link" style={{ padding: 0 }} onClick={() => { editHandle(b) }} >编辑</Button>
-                    <Button type="link" style={{ padding: 0 }} onClick={() => { editHandle(b, true) }} >复制</Button>
+                    <Button type="link" style={{ padding: 0, fontSize: 12 }} onClick={() => { editHandle(b) }} >编辑</Button>
+                    <Button type="link" style={{ padding: 0, fontSize: 12 }} onClick={() => { editHandle(b, true) }} >复制</Button>
                 </Space>
             }
         },

+ 33 - 24
src/pages/launchSystemV3/tencentAdPutIn/create/addDynamic.tsx

@@ -22,12 +22,12 @@ const { Text, Title } = Typography;
  * 新增创意
  * @returns 
  */
-const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose, adData: selectData, tactics }) => {
+const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose, adData: selectData, tactics, putInType }) => {
 
     /****************************************/
     const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
     const { adgroups, dynamic } = addelivery
-    const { marketingAssetOuterSpec, marketingCarrierType, marketingGoal, siteSet, automaticSiteEnabled, sceneSpec } = addelivery.adgroups
+    const { marketingAssetOuterSpec, marketingCarrierType, marketingGoal, marketingSubGoal, siteSet, automaticSiteEnabled, sceneSpec } = addelivery.adgroups
     const { deliveryMode, creativeTemplateId } = addelivery.dynamic
     const [wechatVisible, setWechatVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
     const [channelsProfileVisible, setChannelsProfileVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
@@ -45,7 +45,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
 
     const createDynamicTask = useAjax((params) => createDynamicTaskApi(params))
     /****************************************/
-    
+
     useEffect(() => {
         if (creativeTemplateId) {
             let params: any = {
@@ -62,6 +62,10 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
             } else {
                 params.siteSet = siteSet
             }
+            params.taskType = putInType
+            if (putInType === 'GAME') {
+                params.marketingSubGoal = marketingSubGoal
+            }
             getCreativeDetails.run(params).then(res => {
                 if (res?.adcreativeTemplateStructAdpermits?.length > 0) {
                     let adcreativeTemplateStructAdpermits = res?.adcreativeTemplateStructAdpermits[0]
@@ -88,7 +92,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                 }
             })
         }
-    }, [creativeTemplateId, deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, automaticSiteEnabled])
+    }, [creativeTemplateId, deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, automaticSiteEnabled, putInType])
 
     useEffect(() => {
         if (adData && adData.length) {
@@ -120,8 +124,8 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
             setMaterialData(materialData)
             setTextData(textData)
         } else if (selectData?.length > 0) {
-            const { siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled } = selectData[0]
-            setAddelivery({ ...addelivery, adgroups: { marketingGoal, marketingCarrierType, siteSet, automaticSiteEnabled, sceneSpec, marketingAssetOuterSpec: { marketingTargetType } } })
+            const { siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled, marketingSubGoal } = selectData[0]
+            setAddelivery({ ...addelivery, adgroups: { marketingGoal, marketingSubGoal, marketingCarrierType, siteSet, automaticSiteEnabled, sceneSpec, marketingAssetOuterSpec: { marketingTargetType } } })
             let AccountSet = new Set(selectData.map(item => item.accountId))
             setAccountCreateLogs([...AccountSet].map(accountId => ({ accountId })))
         }
@@ -154,7 +158,8 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
             message.error('请先配置创意文案')
             return
         }
-        if (!accountCreateLogs?.some(item => item?.pageList?.length)) {
+
+        if (!accountCreateLogs?.some(item => item?.pageList?.length) && !['PAGE_TYPE_WECHAT_MINI_GAME'].includes(dynamic?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType)) {
             message.error('请先选择落地页')
             return
         }
@@ -579,7 +584,8 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                                     setMaterialData,
                                     textData,
                                     setTextData,
-                                    clearData
+                                    clearData,
+                                    putInType
                                 }}>
                                 <div className={style.settingsBody_content_right}>
                                     <div className={`${style.settingsBody_content_row} ${style.row1}`}>
@@ -603,7 +609,7 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                                         </div>
                                     </div>
                                     {/* 创意 */}
-                                    <Dynamic 
+                                    <Dynamic
                                         creativeTemplateAppellation={creativeTemplateAppellation}
                                         creativeTemplateStyle={creativeTemplateStyle}
                                     />
@@ -621,21 +627,24 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                     </div>
 
                     <Space className={style.bts} wrap>
-                        <TacticsS strategyValue={{
-                            adData: adData.map(item => {
-                                const { accountId, adgroupName, adgroupId, siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled } = item
-                                return {
-                                    siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled,
-                                    accountId,
-                                    adgroupId,
-                                    adgroupName
-                                }
-                            }),
-                            addelivery,
-                            accountCreateLogs,
-                            materialData,
-                            textData
-                        }} />
+                        <TacticsS
+                            strategyValue={{
+                                adData: adData.map(item => {
+                                    const { accountId, adgroupName, adgroupId, siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled, marketingSubGoal } = item
+                                    return {
+                                        siteSet, marketingCarrierType, marketingGoal, marketingSubGoal, marketingTargetType, sceneSpec, automaticSiteEnabled,
+                                        accountId,
+                                        adgroupId,
+                                        adgroupName
+                                    }
+                                }),
+                                addelivery,
+                                accountCreateLogs,
+                                materialData,
+                                textData
+                            }}
+                            putInType={putInType}
+                        />
                         <Button type='primary' onClick={preview}><SearchOutlined />预览广告</Button>
                     </Space>
 

+ 182 - 126
src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx

@@ -1,4 +1,4 @@
-import { Button, Card, Checkbox, Divider, Empty, Modal, Popconfirm, Select, Space, Spin, Table, Tabs, Tag, Tooltip, message, notification } from "antd"
+import { Button, Card, Checkbox, Divider, Empty, Modal, Popconfirm, Radio, Select, Space, Spin, Table, Tabs, Tag, Tooltip, message, notification } from "antd"
 import React, { useEffect, useState } from "react"
 import style from './index.less'
 import '../index.less'
@@ -26,6 +26,7 @@ import { getCreativeDetailsApi } from "@/services/adqV3/global"
 import ConversionSelect from "../../components/ConversionSelect"
 import VideoChannel from "../../components/VideoChannel"
 import Save from "./Save"
+import { useLocalStorageState } from "ahooks"
 
 export const DispatchAddelivery = React.createContext<PULLIN.DispatchAddelivery | null>(null);
 
@@ -36,10 +37,11 @@ export const DispatchAddelivery = React.createContext<PULLIN.DispatchAddelivery
 const Create: React.FC = () => {
 
     /*******************************************/
+    const { initialState } = useModel('@@initialState');
     const { getAllUserAccount } = useModel('useLaunchAdq.useAdAuthorize')
     const { initTargeting } = useModel('useLaunchV3.useTargeting')
     const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
-    const { marketingAssetOuterSpec, marketingCarrierType, marketingGoal, siteSet, automaticSiteEnabled, sceneSpec, isConversion } = addelivery.adgroups
+    const { marketingAssetOuterSpec, marketingCarrierType, marketingGoal, marketingSubGoal, siteSet, automaticSiteEnabled, sceneSpec, isConversion } = addelivery.adgroups
     const { deliveryMode, creativeTemplateId, creativeComponents } = addelivery.dynamic
     const [accSearch, setAccSearch] = useState<string>()
     const [accountCreateLogs, setAccountCreateLogs] = useState<PULLIN.AccountCreateLogsProps[]>([])  // 账户
@@ -57,6 +59,8 @@ const Create: React.FC = () => {
     const [dynamicCount, setDynamicCount] = useState<number>(0)
     const [creativeTemplateAppellation, setCreativeTemplateAppellation] = useState<string>()
     const [creativeTemplateStyle, setCreativeTemplateStyle] = useState<string>()
+    const [putInTypeList, setPutInTypeList] = useState<{ label: string, value: string }[]>([])
+    const [putInType, setPutInType] = useLocalStorageState<'NOVEL' | 'GAME'>('PUTINTYPE');
     const [copyTask, setCopyTask] = useState<{ copyTaskId?: number, uuid?: string }>({})
 
     const getGroupList = useAjax(() => getGroupListApi())
@@ -65,6 +69,26 @@ const Create: React.FC = () => {
     const getCreativeDetails = useAjax((params) => getCreativeDetailsApi(params))
     /*******************************************/
 
+    // 判断游戏还是小说短剧
+    useEffect(() => {
+        if (initialState?.menu?.data?.length) {
+            const menu = initialState?.menu?.data
+            const ADLAUNCH3 = menu.find((item: { path: string }) => item.path === "/launchSystemV3")
+            const ADLAUNCH2 = menu.find((item: { path: string }) => item.path === "/launchSystemNew")
+            const ACCOUNTLIST = ADLAUNCH3?.routes?.find((item: { path: string }) => item.path === "/launchSystemV3/account") || ADLAUNCH2?.routes?.find((item: { path: string }) => item.path === "/launchSystemNew/account")
+            let newputInTypeList: { label: string, value: string }[] = []
+            ACCOUNTLIST?.routes.forEach((item: { path: string }) => {
+                if (item.path.includes('novel')) {
+                    newputInTypeList.push({ label: '小说/短剧投放', value: 'NOVEL' })
+                } else if (item.path.includes('game')) {
+                    newputInTypeList.push({ label: '游戏投放', value: 'GAME' })
+                }
+            })
+            setPutInTypeList(newputInTypeList);
+            newputInTypeList.length > 0 && setPutInType(type => (type && newputInTypeList.some(item => item.value === type) ? type : newputInTypeList[0].value) as 'NOVEL' | 'GAME');
+        }
+    }, [initialState?.menu])
+
     useEffect(() => {
         // 获取账户组
         getGroupList.run()
@@ -89,6 +113,10 @@ const Create: React.FC = () => {
             } else {
                 params.siteSet = siteSet
             }
+            params.taskType = putInType
+            if (putInType === 'GAME') {
+                params.marketingSubGoal = marketingSubGoal
+            }
             getCreativeDetails.run(params).then(res => {
                 if (res?.adcreativeTemplateStructAdpermits?.length > 0) {
                     let adcreativeTemplateStructAdpermits = res?.adcreativeTemplateStructAdpermits[0]
@@ -115,7 +143,7 @@ const Create: React.FC = () => {
                 }
             })
         }
-    }, [creativeTemplateId, deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, automaticSiteEnabled])
+    }, [creativeTemplateId, deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, automaticSiteEnabled, putInType])
 
     /** 获取分组里账号 */
     const getGroupAccountList = (ids: number[]) => {
@@ -132,7 +160,7 @@ const Create: React.FC = () => {
                             }
                         })
                     })
-                    setAccountCreateLogs(userArr?.map((item) => ({ accountId: item?.accountId })))
+                    setAccountCreateLogs(userArr?.filter((item: any) => putInType === 'NOVEL' ? ['NOVEL', 'NOVEL_IAA'].includes(item.adUnitType) : putInType === 'GAME' ? ['GAME', 'GAME_IAA'].includes(item.adUnitType) : false)?.map((item) => ({ accountId: item?.accountId })))
                     clearData()
                     setAddelivery({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
                 } else {
@@ -146,18 +174,23 @@ const Create: React.FC = () => {
 
     /** 存为预设 */
     const severBd = () => {
-        localStorage.setItem('ADQADV3', JSON.stringify({
-            addelivery,
-            accountCreateLogs,
-            materialData,
-            textData
-        }))
-        message.success('存储成功')
+        if (putInType) {
+            localStorage.setItem(putInType === 'GAME' ? 'ADQADV3_GAME' : 'ADQADV3', JSON.stringify({
+                addelivery,
+                accountCreateLogs,
+                materialData,
+                textData,
+                putInType
+            }))
+            message.success('存储成功')
+        }
     }
 
     /** 清除 */
-    const delBdPlan = () => {
-        localStorage.removeItem('ADQADV3')
+    const delBdPlan = ({ isRemoveItem = true }: { isRemoveItem?: boolean }) => {
+        if (isRemoveItem) {
+            localStorage.removeItem(putInType === 'GAME' ? 'ADQADV3_GAME' : 'ADQADV3')
+        }
         setAccountCreateLogs([])
         setMaterialData({})
         setTextData({})
@@ -175,124 +208,131 @@ const Create: React.FC = () => {
 
     /**数据回填 */
     useEffect(() => {
-        let taskId = sessionStorage.getItem('TASKID3.0')
-        let adqAdData = localStorage.getItem('ADQADV3')
-        if (taskId) {
-            getSelectTaskDetail.run(taskId).then(res => {
-                if (res) {
-                    const { adgroupDTO, accountIdParamVOMap, targetings, dynamicCreativesDTO: { mediaType, ...dynamic }, dynamicCreativesTextDTO, dynamicMaterialDTOS, id, uuid } = res
-                    setCopyTask({ copyTaskId: id, uuid })
-                    let beginDate = adgroupDTO.beginDate
-                    let endDate = adgroupDTO.endDate
-                    if (adgroupDTO?.deepConversionSpec) {
-                        adgroupDTO.depthConversionEnabled = true
-                    } else {
-                        adgroupDTO.depthConversionEnabled = false
-                    }
-                    if (beginDate && moment(beginDate) < moment()) {
-                        beginDate = moment().format('YYYY-MM-DD')
-                        endDate = moment().add(7, 'day').format('YYYY-MM-DD')
-                        message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
-                    }
-                    let dynamicGroup: any[] = []
-                    if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
-                        dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
-                            let { type, valueJson } = item[0]
-                            let value = JSON.parse(valueJson).value
-                            if (type === 'image') {
-                                return { image_id: { id: value.imageId, url: value.imageUrl, materialType: value.materialType } }
-                            } else if (type === 'image_list' || type === 'element_story') {
-                                return { [type]: value.list.map((l: { imageUrl: any; imageId: any; materialType: any }) => ({ url: l.imageUrl, id: l.imageId, materialType: l.materialType })) }
-                            } else if (type === 'short_video' || type === 'video') {
-                                let field = type === 'video' ? 'video_id' : 'short_video1'
-                                let videoData: any = {}
-                                videoData[field] = { materialType: value.materialType, url: value.videoUrl, id: value.videoId }
-                                if (value.imageUrl) {
-                                    videoData['cover_id'] = { materialType: value.materialCoverType, url: value.imageUrl, id: value.iamgeId }
-                                }
-                                return videoData
+        if (putInTypeList?.length > 0) {
+            let taskId = sessionStorage.getItem('TASKID3.0')
+            let adqAdData = localStorage.getItem(putInType === 'GAME' ? 'ADQADV3_GAME' : 'ADQADV3')
+            if (taskId) {
+                getSelectTaskDetail.run(taskId).then(res => {
+                    if (res) {
+                        const { adgroupDTO, accountIdParamVOMap, targetings, dynamicCreativesDTO: { mediaType, ...dynamic }, dynamicCreativesTextDTO, dynamicMaterialDTOS, taskType = 'NOVEL', id, uuid } = res
+                        setCopyTask({ copyTaskId: id, uuid })
+                        if (putInTypeList.some(item => item.value === taskType)) {
+                            setPutInType(() => taskType)
+                            let beginDate = adgroupDTO.beginDate
+                            let endDate = adgroupDTO.endDate
+                            if (adgroupDTO?.deepConversionSpec) {
+                                adgroupDTO.depthConversionEnabled = true
                             } else {
-                                return {}
+                                adgroupDTO.depthConversionEnabled = false
                             }
-                        })
-                    } else { // 组件化创意
-                        dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
-                            return {
-                                list: item?.map((i: any) => {
-                                    let { type, valueJson } = i
+                            if (beginDate && moment(beginDate) < moment()) {
+                                beginDate = moment().format('YYYY-MM-DD')
+                                endDate = moment().add(7, 'day').format('YYYY-MM-DD')
+                                message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
+                            }
+                            let dynamicGroup: any[] = []
+                            if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
+                                dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
+                                    let { type, valueJson } = item[0]
                                     let value = JSON.parse(valueJson).value
                                     if (type === 'image') {
-                                        return { id: value.imageId, url: value.imageUrl, materialType: value.materialType }
-                                    } else if (type === 'image_list') {
-                                        return value.list.map((l: { imageUrl: any; imageId: any; materialType: any }) => ({ url: l.imageUrl, id: l.imageId, materialType: l.materialType }))
-                                    } else if (type === 'video') {
-                                        return { materialType: value.materialType, url: value.videoUrl, id: value.videoId }
+                                        return { image_id: { id: value.imageId, url: value.imageUrl, materialType: value.materialType } }
+                                    } else if (type === 'image_list' || type === 'element_story') {
+                                        return { [type]: value.list.map((l: { imageUrl: any; imageId: any; materialType: any }) => ({ url: l.imageUrl, id: l.imageId, materialType: l.materialType })) }
+                                    } else if (type === 'short_video' || type === 'video') {
+                                        let field = type === 'video' ? 'video_id' : 'short_video1'
+                                        let videoData: any = {}
+                                        videoData[field] = { materialType: value.materialType, url: value.videoUrl, id: value.videoId }
+                                        if (value.imageUrl) {
+                                            videoData['cover_id'] = { materialType: value.materialCoverType, url: value.imageUrl, id: value.iamgeId }
+                                        }
+                                        return videoData
                                     } else {
                                         return {}
                                     }
                                 })
+                            } else { // 组件化创意
+                                dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
+                                    return {
+                                        list: item?.map((i: any) => {
+                                            let { type, valueJson } = i
+                                            let value = JSON.parse(valueJson).value
+                                            if (type === 'image') {
+                                                return { id: value.imageId, url: value.imageUrl, materialType: value.materialType }
+                                            } else if (type === 'image_list') {
+                                                return value.list.map((l: { imageUrl: any; imageId: any; materialType: any }) => ({ url: l.imageUrl, id: l.imageId, materialType: l.materialType }))
+                                            } else if (type === 'video') {
+                                                return { materialType: value.materialType, url: value.videoUrl, id: value.videoId }
+                                            } else {
+                                                return {}
+                                            }
+                                        })
+                                    }
+                                })
                             }
-                        })
-                    }
 
-                    let isConversion = false
-                    setAccountCreateLogs(Object.keys(accountIdParamVOMap || {}).map(accountId => {
-                        const { productDTOS, wechatOfficialAccountsVO, pageList, landingPageVOS, userActionSetsList, conversionInfo, wechatChannelVO, wechatAppletList } = accountIdParamVOMap[accountId]
-                        let data: PULLIN.AccountCreateLogsProps = {
-                            accountId: Number(accountId),
-                            productList: productDTOS
-                        }
-                        if (wechatOfficialAccountsVO) {
-                            data.wechatChannelList = [wechatOfficialAccountsVO]
-                        }
-                        if (pageList || landingPageVOS || wechatAppletList) {
-                            data.pageList = pageList || landingPageVOS || wechatAppletList?.map((item: { appletName: any; id: any }) => ({ ...item, pageName: item.appletName, pageId: item.id }))
-                        }
-                        if (userActionSetsList) {
-                            data.userActionSetsList = userActionSetsList
-                        }
-                        if (conversionInfo) {
-                            isConversion = true
-                            data.newConversionList = [conversionInfo]
-                        }
-                        if (wechatChannelVO) {
-                            data.videoChannelList = [wechatChannelVO]
-                        }
-                        return data
-                    }))
+                            let isConversion = false
+                            setAccountCreateLogs(Object.keys(accountIdParamVOMap || {}).map(accountId => {
+                                const { productDTOS, wechatOfficialAccountsVO, pageList, landingPageVOS, userActionSetsList, conversionInfo, wechatChannelVO, wechatAppletList } = accountIdParamVOMap[accountId]
+                                let data: PULLIN.AccountCreateLogsProps = {
+                                    accountId: Number(accountId),
+                                    productList: productDTOS
+                                }
+                                if (wechatOfficialAccountsVO) {
+                                    data.wechatChannelList = [wechatOfficialAccountsVO]
+                                }
+                                if (pageList || landingPageVOS || wechatAppletList) {
+                                    data.pageList = pageList || landingPageVOS || wechatAppletList?.map((item: { appletName: any; id: any }) => ({ ...item, pageName: item.appletName, pageId: item.id }))
+                                }
+                                if (userActionSetsList) {
+                                    data.userActionSetsList = userActionSetsList
+                                }
+                                if (conversionInfo) {
+                                    isConversion = true
+                                    data.newConversionList = [conversionInfo]
+                                }
+                                if (wechatChannelVO) {
+                                    data.videoChannelList = [wechatChannelVO]
+                                }
+                                return data
+                            }))
 
-                    setAddelivery({
-                        adgroups: { ...adgroupDTO, isConversion, adgroupName: adgroupDTO.adgroupName + '_副本' + randomString(true, 3, 5), endDate, beginDate },
-                        targeting: targetings.map((item: any) => {
-                            const { targetingName, ...targeting } = item
-                            return { targetingName, targeting }
-                        }),
-                        dynamic,
-                        dynamicMaterialDTos: dynamicGroup.length > 0 ? { dynamicGroup } : {},
-                        dynamicCreativesTextDTOS: dynamicCreativesTextDTO,
-                        mediaType: mediaType || 0
-                    })
-                    sessionStorage.removeItem('TASKID3.0')
-                }
-            })
-        } else if (adqAdData) {
-            const { addelivery, accountCreateLogs, materialData, textData } = JSON.parse(adqAdData)
-            if (addelivery?.adgroups) {
-                if (addelivery?.adgroups?.beginDate && moment(addelivery?.adgroups?.beginDate) < moment()) {
-                    addelivery.adgroups.beginDate = moment().format('YYYY-MM-DD')
-                    message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
-                }
-                if (addelivery?.adgroups?.endDate && moment(addelivery?.adgroups?.endDate) < moment()) {
-                    addelivery.adgroups.endDate = moment().format('YYYY-MM-DD')
-                    message.warning('请注意,检测投放结束日期小于今天,已自动改成今天,如需修改,请重新设置')
+                            setAddelivery({
+                                adgroups: { ...adgroupDTO, isConversion, adgroupName: adgroupDTO.adgroupName + '_副本' + randomString(true, 3, 5), endDate, beginDate },
+                                targeting: targetings.map((item: any) => {
+                                    const { targetingName, ...targeting } = item
+                                    return { targetingName, targeting }
+                                }),
+                                dynamic,
+                                dynamicMaterialDTos: dynamicGroup.length > 0 ? { dynamicGroup } : {},
+                                dynamicCreativesTextDTOS: dynamicCreativesTextDTO,
+                                mediaType: mediaType || 0
+                            })
+                        } else {
+                            message.error(`没有${taskType === 'NOVEL' ? '小说/短剧' : taskType === 'GAME' ? '游戏' : taskType}投放权限`)
+                        }
+                        sessionStorage.removeItem('TASKID3.0')
+                    }
+                })
+            } else if (adqAdData) {
+                const { addelivery, accountCreateLogs, materialData, textData } = JSON.parse(adqAdData)
+                if (addelivery?.adgroups) {
+                    if (addelivery?.adgroups?.beginDate && moment(addelivery?.adgroups?.beginDate) < moment()) {
+                        addelivery.adgroups.beginDate = moment().format('YYYY-MM-DD')
+                        message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
+                    }
+                    if (addelivery?.adgroups?.endDate && moment(addelivery?.adgroups?.endDate) < moment()) {
+                        addelivery.adgroups.endDate = moment().format('YYYY-MM-DD')
+                        message.warning('请注意,检测投放结束日期小于今天,已自动改成今天,如需修改,请重新设置')
+                    }
                 }
+                setAddelivery({ ...addelivery })
+                setAccountCreateLogs(accountCreateLogs)
+                setMaterialData(materialData)
+                setTextData(textData)
             }
-            setAddelivery({ ...addelivery })
-            setAccountCreateLogs(accountCreateLogs)
-            setMaterialData(materialData)
-            setTextData(textData)
         }
-    }, [])
+    }, [putInType, putInTypeList])
 
     // 预览
     const preview = () => {
@@ -350,7 +390,7 @@ const Create: React.FC = () => {
             return
         }
 
-        if (!accountCreateLogs?.some(item => item?.pageList?.length)) {
+        if (!accountCreateLogs?.some(item => item?.pageList?.length) && !['PAGE_TYPE_WECHAT_MINI_GAME'].includes(dynamic?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType)) {
             message.error('请先选择落地页')
             return
         }
@@ -484,6 +524,7 @@ const Create: React.FC = () => {
 
             newAdCount += data.length
             let newData: any[] = []
+            // 激励
             if ([910].includes(dynamic.creativeTemplateId)) {
                 if (dynamic?.landingPageType === 1) {
                     let averageAdPageList: any[] = distributeArray(item.pageList, productList.length * targeting.length)
@@ -731,7 +772,9 @@ const Create: React.FC = () => {
 
             let map: any = {
                 userActionSetsList: userActionSetsListDto,
-                pageList: pageList?.map((item: { pageId: any }) => item.pageId)
+            }
+            if (!['PAGE_TYPE_WECHAT_MINI_GAME'].includes(dynamic?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType)) {
+                map.pageList = pageList?.map((item: { pageId: any }) => item.pageId)
             }
             if (productList && ['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) {
                 map.productDTOS = productList?.map(item => {
@@ -763,9 +806,9 @@ const Create: React.FC = () => {
             dynamicCreativesTextDTOS,
             dynamicMaterialDTOS,
             accountIdParamDTOMap,
+            taskType: putInType,
             ...copyTask
         }
-        // setSubVisible(false)
         createAdgroupTask.run(params).then(res => {
             if (res) {
                 Modal.success({
@@ -793,8 +836,19 @@ const Create: React.FC = () => {
         <Spin spinning={createAdgroupTask.loading || getSelectTaskDetail.loading || getCreativeDetails.loading}>
             <Card
                 size="small"
-                title={<Space>
+                title={<Space size={18}>
                     <div className={style.cardTitle}>配置区</div>
+                    {putInTypeList.length >= 1 && <Radio.Group
+                        buttonStyle="solid"
+                        value={putInType}
+                        onChange={(e) => {
+                            setPutInType(e.target.value)
+                            delBdPlan({ isRemoveItem: false })
+                        }}
+                        options={putInTypeList}
+                        optionType='button'
+                        size="small"
+                    />}
                 </Space>}
                 className={style.createAd}
             >
@@ -861,7 +915,7 @@ const Create: React.FC = () => {
                                         <Checkbox onChange={(e) => {
                                             let data = []
                                             if (e.target.checked) {
-                                                data = JSON.parse(JSON.stringify(getAllUserAccount?.data?.data))
+                                                data = JSON.parse(JSON.stringify(getAllUserAccount?.data?.data?.filter((item: any) => putInType === 'NOVEL' ? ['NOVEL', 'NOVEL_IAA'].includes(item.adUnitType) : putInType === 'GAME' ? ['GAME', 'GAME_IAA'].includes(item.adUnitType) : false)))
                                                 if (accSearch) {
                                                     let newAccSearch = accSearch?.split(/[,,\n\s]+/ig).filter((item: any) => item)
                                                     data = data?.filter((item: any) => newAccSearch?.some(val => item!.accountId?.toString().toLowerCase()?.includes(val)))
@@ -873,7 +927,7 @@ const Create: React.FC = () => {
                                 </>
                             )}
                         >
-                            {getAllUserAccount?.data?.data?.map((item: any) => <Select.Option value={item.accountId} key={item.id}>{item.remark ? item.accountId + '_' + item.remark : item.accountId}</Select.Option>)}
+                            {getAllUserAccount?.data?.data?.filter((item: any) => putInType === 'NOVEL' ? ['NOVEL', 'NOVEL_IAA'].includes(item.adUnitType) : putInType === 'GAME' ? ['GAME', 'GAME_IAA'].includes(item.adUnitType) : false)?.map((item: any) => <Select.Option value={item.accountId} key={item.id}>{item.remark ? item.accountId + '_' + item.remark : item.accountId}</Select.Option>)}
                         </Select>
                     </Selector>
 
@@ -901,7 +955,8 @@ const Create: React.FC = () => {
                                 setMaterialData,
                                 textData,
                                 setTextData,
-                                clearData
+                                clearData,
+                                putInType
                             }}>
                             <div className={style.settingsBody_content_right}>
                                 {/* 广告信息 */}
@@ -926,11 +981,11 @@ const Create: React.FC = () => {
                     </div>
                 </div>
                 <Space className={style.bts} wrap>
-                    <Save addelivery={addelivery} />
+                    <Save addelivery={addelivery} putInType={putInType} />
                     <Button type='primary' onClick={severBd}>存为预设</Button>
                     <Popconfirm
                         title="确定清空?"
-                        onConfirm={delBdPlan}
+                        onConfirm={() => delBdPlan({})}
                     >
                         <Button>清空配置/预设</Button>
                     </Popconfirm>
@@ -985,6 +1040,7 @@ const Create: React.FC = () => {
                 {/* 转化归因 */}
                 {conversionVisible && <ConversionSelect
                     adgroups={addelivery.adgroups}
+                    putInType={putInType}
                     visible={conversionVisible}
                     data={accountCreateLogs}
                     onClose={() => setConversionVisible(false)}

+ 11 - 0
src/pages/launchSystemV3/tencentAdPutIn/create/tableConfig.tsx

@@ -56,6 +56,11 @@ const columns = (): TableProps<any>['columns'] => {
                                 <Text style={{ fontSize: 12 }}>推广产品:企业微信(产品本地ID:{b?.adgroupsDto?.sysCorpWechatId})</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_WECHAT_MINI_GAME'].includes(b.adgroupsDto?.marketingAssetOuterSpec?.marketingTargetType)) {
+                            return <Space size={0} direction="vertical">
+                                <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>
                         }
                         return 'ERROR,请联系管理员'
                     },
@@ -223,6 +228,9 @@ const columns = (): TableProps<any>['columns'] => {
                     render: (_, b) => {
                         let pageListDto = b?.pageListDto
                         let pageType = b?.dynamicDto?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType
+                        if (pageType === 'PAGE_TYPE_WECHAT_MINI_GAME') {
+                            return <Text style={{ fontSize: 12 }}>无需配置</Text>
+                        }
                         return <Text style={{ fontSize: 12, wordBreak: 'break-all' }}>
                             {pageType === 'PAGE_TYPE_OFFICIAL' ? '灵鹊落地页' : pageType === 'PAGE_TYPE_WECHAT_MINI_PROGRAM' ? '微信小程序' : '原生推广页'}:
                             {pageListDto?.map((item: { pageName: any; }) => item?.pageName)?.join(',')}
@@ -389,6 +397,9 @@ export const columnsAddDynamic = (): TableProps<any>['columns'] => {
                     render: (_, b) => {
                         let pageListDto = b?.pageListDto
                         let pageType = b?.dynamicDto?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType
+                        if (pageType === 'PAGE_TYPE_WECHAT_MINI_GAME') {
+                            return <Text style={{ fontSize: 12 }}>无需配置</Text>
+                        }
                         return <Text style={{ fontSize: 12, wordBreak: 'break-all' }}>
                             {pageType === 'PAGE_TYPE_OFFICIAL' ? '灵鹊落地页' : pageType === 'PAGE_TYPE_WECHAT_MINI_PROGRAM' ? '微信小程序' : '原生推广页'}:
                             {pageListDto?.map((item: { pageName: any; }) => item?.pageName)?.join(',')}

+ 27 - 6
src/pages/launchSystemV3/tencentAdPutIn/rules.ts

@@ -1169,7 +1169,7 @@ export const dynamicRules = {
 
 // 视频规则
 export const videoRules = {
-    "videoL": { // 横板视频 16:9
+	"videoL": { // 横板视频 16:9
 		"name": "video",
 		"elementType": "STRUCT",
 		"fieldType": "STRUCT_ARRAY",
@@ -1218,7 +1218,7 @@ export const videoRules = {
 			}
 		}
 	},
-    "videoP": {  // 竖版视频9:16
+	"videoP": {  // 竖版视频9:16
 		"name": "video",
 		"elementType": "STRUCT",
 		"fieldType": "STRUCT_ARRAY",
@@ -1267,7 +1267,7 @@ export const videoRules = {
 			}
 		}
 	},
-    "short_video": {  // 4:3
+	"short_video": {  // 4:3
 		"name": "short_video",
 		"elementType": "STRUCT",
 		"fieldType": "STRUCT_ARRAY",
@@ -1335,7 +1335,7 @@ export const videoRules = {
 }
 
 export const imageRules = {
-    "imageL": { // 横板大图16:9
+	"imageL": { // 横板大图16:9
 		"name": "image",
 		"elementType": "STRUCT",
 		"fieldType": "STRUCT_ARRAY",
@@ -1364,7 +1364,7 @@ export const imageRules = {
 			}
 		}
 	},
-    "imageP": { // 竖版大图9:16
+	"imageP": { // 竖版大图9:16
 		"name": "image",
 		"elementType": "STRUCT",
 		"fieldType": "STRUCT_ARRAY",
@@ -1395,8 +1395,13 @@ export const imageRules = {
 	},
 }
 
+type AdRules = {
+	[goal: string]: {
+		[subGoal: string]: any; // 根据实际需要可以更改 any 类型  
+	};
+};
 /** 广告配置数据 */
-export const adRules = {
+export const adRules: AdRules = {
 	"MARKETING_GOAL_BRAND_PROMOTION": {
 		"MARKETING_TARGET_TYPE_APP_ANDROID": {
 			"MARKETING_SUB_GOAL_UNKNOWN": {
@@ -1835,6 +1840,22 @@ export const adRules = {
 					"PRODUCT_TYPE": 43
 				}
 			}
+		},
+		"MARKETING_SUB_GOAL_MINI_GAME_NEW_CUSTOMER_GROWTH": {
+			"MARKETING_TARGET_TYPE_WECHAT_MINI_GAME": {
+				"MARKETING_SUB_GOAL_UNKNOWN": {
+					"MARKETING_CARRIER_TYPE_WECHAT_MINI_GAME": {},
+					"MARKETING_CARRIER_TYPE_WECHAT_CHANNELS_LIVE": {}
+				}
+			}
+		},
+		"MARKETING_SUB_GOAL_NEW_GAME_TEST": {
+			"MARKETING_TARGET_TYPE_WECHAT_MINI_GAME": {
+				"MARKETING_SUB_GOAL_UNKNOWN": {
+					"MARKETING_CARRIER_TYPE_WECHAT_MINI_GAME": {},
+					"MARKETING_CARRIER_TYPE_WECHAT_CHANNELS_LIVE": {}
+				}
+			}
 		}
 	}
 }

+ 14 - 9
src/pages/launchSystemV3/tencentAdPutIn/taskList/tableConfig.tsx

@@ -33,7 +33,8 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
             align: 'center',
             fixed: 'left',
             render(value) {
-                return { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }} >执行中</span>} />, 1: <Badge style={{ fontSize: 12 }} status="error" text={<span style={{ fontSize: 12 }} >全部失败</span>} />, 2: <Badge style={{ fontSize: 12 }} status="warning" text={<span style={{ fontSize: 12 }} >部分成功</span>} />, 100: <Badge style={{ fontSize: 12 }} status="success" text={<span style={{ fontSize: 12 }} >全部成功</span>} /> }[value]
+                let a = { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }} >执行中</span>} />, 1: <Badge style={{ fontSize: 12 }} status="error" text={<span style={{ fontSize: 12 }} >全部失败</span>} />, 2: <Badge style={{ fontSize: 12 }} status="warning" text={<span style={{ fontSize: 12 }} >部分成功</span>} />, 100: <Badge style={{ fontSize: 12 }} status="success" text={<span style={{ fontSize: 12 }} >全部成功</span>} /> }
+                return a[value as keyof typeof a]
             },
         },
         {
@@ -67,7 +68,7 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
             render: (_, b) => {
                 let configuredStatus = b?.adgroupDTO?.configuredStatus
                 if (configuredStatus) {
-                    return <span style={{ fontSize: "12px" }}>{AD_STATUS_ENUM[configuredStatus]}</span>
+                    return <span style={{ fontSize: "12px" }}>{AD_STATUS_ENUM[configuredStatus as keyof typeof AD_STATUS_ENUM]}</span>
                 } else {
                     return <span>--</span>
                 }
@@ -79,7 +80,7 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
             key: 'adgroupDTO',
             width: 200,
             ellipsis: true,
-            render(value) {
+            render(value, records) {
                 return <div style={{ width: '100%', display: 'flex', alignItems: 'center' }}>
                     <div style={{ width: 'calc(100% - 20px)' }}><Typography.Text style={{ fontSize: 12 }} ellipsis={{ tooltip: true }}>{value?.adgroupName}</Typography.Text></div>
                     <Popover
@@ -87,6 +88,7 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
                         overlayInnerStyle={{ maxWidth: 350, maxHeight: 350, overflow: 'hidden', overflowY: 'auto' }}
                         mouseEnterDelay={0.5}
                         content={<AdgroupTooltip
+                            taskType={records?.taskType}
                             data={value}
                         />}
                     >
@@ -100,7 +102,7 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
             dataIndex: 'targetings',
             key: 'targetings',
             width: 300,
-            render(value) {
+            render(value, records) {
                 return <div style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, overflow: 'hidden', overflowX: 'auto' }}>
                     {value?.map((item: any, index: number) => <Popover
                         key={index}
@@ -111,6 +113,7 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
                             data={item}
                             geoLocationList={geoLocationList}
                             modelList={modelList}
+                            taskType={records?.taskType}
                         />}
                     >
                         <div style={{ width: 80, cursor: 'help' }}><Typography.Text style={{ fontSize: 12 }} ellipsis>{index + 1}、{item?.targetingName || '--'}</Typography.Text></div>
@@ -210,7 +213,7 @@ export const columnsLog = (sync: (value: any) => void): TableProps<any>['columns
             ellipsis: true,
             align: 'center',
             render(value) {
-                return <span style={{ fontSize: 12 }}>{MARKETING_GOAL_ENUM[value]}</span>
+                return <span style={{ fontSize: 12 }}>{MARKETING_GOAL_ENUM[value  as keyof typeof MARKETING_GOAL_ENUM]}</span>
             },
         },
         {
@@ -230,7 +233,8 @@ export const columnsLog = (sync: (value: any) => void): TableProps<any>['columns
             width: 90,
             align: 'center',
             render(value) {
-                return { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }}>执行中</span>} />, 1: <Badge status="error" style={{ fontSize: 12 }} text={<span>失败</span>} />, 101: <Badge status="warning" style={{ fontSize: 12 }} text={<span>同步异常</span>} />, 100: <Badge style={{ fontSize: 12 }} status="success" text={<span>成功</span>} /> }[value]
+                const a = { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }}>执行中</span>} />, 1: <Badge status="error" style={{ fontSize: 12 }} text={<span>失败</span>} />, 101: <Badge status="warning" style={{ fontSize: 12 }} text={<span>同步异常</span>} />, 100: <Badge style={{ fontSize: 12 }} status="success" text={<span>成功</span>} /> }
+                return a[value as keyof typeof a]
             },
         },
         {
@@ -299,7 +303,7 @@ export const columnsDynamicLog = (): TableProps<any>['columns'] => {
             ellipsis: true,
             align: 'center',
             render(value) {
-                return <span style={{ fontSize: 12 }}>{DELIVERY_MODE_ENUM[value]}</span>
+                return <span style={{ fontSize: 12 }}>{DELIVERY_MODE_ENUM[value as keyof typeof DELIVERY_MODE_ENUM]}</span>
             }
         },
         {
@@ -310,7 +314,7 @@ export const columnsDynamicLog = (): TableProps<any>['columns'] => {
             ellipsis: true,
             align: 'center',
             render(value) {
-                return <span style={{ fontSize: 12 }}>{DYNAMIC_CREATIVE_TYPE_ENUM[value]}</span>
+                return <span style={{ fontSize: 12 }}>{DYNAMIC_CREATIVE_TYPE_ENUM[value as keyof typeof DYNAMIC_CREATIVE_TYPE_ENUM]}</span>
             }
         },
         {
@@ -353,7 +357,8 @@ export const columnsDynamicLog = (): TableProps<any>['columns'] => {
             width: 80,
             align: 'center',
             render(value) {
-                return { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }}>执行中</span>} />, 1: <Badge status="error" style={{ fontSize: 12 }} text={<span>失败</span>} />, 100: <Badge style={{ fontSize: 12 }} status="success" text={<span>成功</span>} /> }[value]
+                const a = { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }}>执行中</span>} />, 1: <Badge status="error" style={{ fontSize: 12 }} text={<span>失败</span>} />, 100: <Badge style={{ fontSize: 12 }} status="success" text={<span>成功</span>} /> }
+                return a[value as keyof typeof a]
             },
         },
         {

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

@@ -6,11 +6,13 @@ declare namespace PULLIN {
         automaticSiteEnabled: boolean,
         marketingTargetType?: string,
         marketingCarrierType?: string,
+        marketingSubGoal?: string
     }
     interface AdReactContent {
         form: FormInstance<any>
         OGPParams: OGPParamsProps,
         setOGPparams: React.Dispatch<React.SetStateAction<OGPParamsProps>>
+        putInType?: 'NOVEL' | 'GAME'
     }
     interface AddeliveryProps {
         adgroups: any,
@@ -29,10 +31,12 @@ declare namespace PULLIN {
         setMaterialData: React.Dispatch<any>
         textData: any,
         setTextData: React.Dispatch<any>,
-        clearData: () => void
+        clearData: () => void,
+        putInType?: 'NOVEL' | 'GAME'
     }
     type DataType = { label: string | number, value: any, disabled?: boolean }
     interface FormItemDataProps {
+        data: { label: string, value: string, icon: string, iconSelected: string }[]
         id?: any
         value?: string,
         onChange?: (value?: string) => void
@@ -59,6 +63,7 @@ declare namespace PULLIN {
         pageSize: number,
         accountId?: number,
         targetingName?: string,
+        taskType?: 'NOVEL' | 'GAME',
         min?: string,
         max?: string,
     }
@@ -134,6 +139,7 @@ declare namespace PULLIN {
      */
     type NewAddDynamic = {
         adData: any[],
+        putInType?: 'NOVEL' | 'GAME'
         visible?: boolean,
         tactics?: any,
         onClose?: () => void,
@@ -145,11 +151,13 @@ declare namespace PULLIN {
         strategyKey: string,
         strategyValue: string,
         remark?: string
+        putInType?: 'NOVEL' | 'GAME'
     }
     type GetV3StrategyListProps = {
         pageNum: number,
         pageSize: number,
         strategyKey?: string,
-        type?: string
+        type?: string,
+        putInType?: 'NOVEL' | 'GAME'
     }
 }

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

@@ -601,4 +601,86 @@ export async function getAdLabelApi(data: { accountId?: number }) {
         method: 'POST',
         data
     })
+}
+
+/**
+ * 获取游戏列表
+ * @param data 
+ * @returns 
+ */
+export async function getGameLibraryListApi(data: { pageNum: number, pageSize: number, gameName?: string, idList?: number[], gameType?: string }) {
+    return request(api + `/adq/v3/gameLibrary/pageList`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 所有游戏列表
+ * @param data 
+ * @returns 
+ */
+export async function getGameLibraryApi(data: { gameName?: string, idList?: number[], gameType?: string }) {
+    return request(api + `/adq/v3/gameLibrary/listAll`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 新增游戏
+ * @param data 
+ * @returns 
+ */
+export async function addGameApi(data: { appId: string, gameName: string, gameType: string, id?: number }) {
+    return request(api + `/adq/v3/gameLibrary/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 删除游戏
+ * @param id 
+ * @returns 
+ */
+export async function delGameApi(id: number) {
+    return request(api + `/adq/v3/gameLibrary/delById/${id}`, {
+        method: 'DELETE'
+    })
+}
+
+/**
+ * 获取游戏详情
+ * @param id 
+ * @returns 
+ */
+export async function getGameLibraryDetailApi(id: number) {
+    return request(api + `/adq/v3/gameLibrary/getById/${id}`, {
+        method: 'GET'
+    })
+}
+
+/**
+ * 获取游戏详情
+ * @param wxGameAppId 
+ * @returns 
+ */
+export async function getGameLibraryAppIdDetailApi(wxGameAppId: number) {
+    return request(api + `/adq/v3/gameLibrary/getByAppId/${wxGameAppId}`, {
+        method: 'GET'
+    })
+}
+
+
+/**
+ * 获取弹幕
+ * @param data 
+ * @returns 
+ */
+export async function getBarrageRecommendListApi(params: { adAccountId?: number, taskType?: 'GAME' | 'NOVEL' }) {
+    return request(api + `/adq/v3/launch/tools//creative/tools/barrage`, {
+        method: 'GET',
+        params
+    })
 }

部分文件因为文件数量过多而无法显示