wjx 2 роки тому
батько
коміт
efbda90405

+ 9 - 1
src/pages/launchSystemNew/components/adModal/index.tsx

@@ -13,16 +13,24 @@ interface Props {
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: any) => void,
+    sysAdgroupsId?: number
 }
 const AdModal: React.FC<Props> = (props) => {
 
     /**********************/
-    const { visible, onClose, onChange, promotedObjectType } = props
+    const { visible, onClose, onChange, promotedObjectType, sysAdgroupsId } = props
     const [oldsearchData, setOldsearchData] = useState<any>(null)
     const sysAdgroupsList: FnAjax<ListData<SysAdgroupsDTO>> = useAjax((params) => getSysAdgroupsList(params))
     const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
     /**********************/
 
+    // 回填
+    useEffect(() => {
+        if (sysAdgroupsId) {
+            setSelectedRowKeys([sysAdgroupsId])
+        }
+    }, [sysAdgroupsId])
+    
     // 初始获取列表
     useEffect(() => {
         getList({ pageSize: 20, pageNum: 1, promotedObjectType })

+ 9 - 1
src/pages/launchSystemNew/components/targetingModal/index.tsx

@@ -12,17 +12,25 @@ interface Props {
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: any) => void,
+    sysTargetingId?: number
 }
 const TargetingModal: React.FC<Props> = (props) => {
 
     /*****************************/
-    const { visible, onClose, onChange } = props
+    const { visible, onClose, onChange, sysTargetingId } = props
     const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
     const [oldsearchData, setOldsearchData] = useState<any>(null)
 
     const list: FnAjax<ListData<any>> = useAjax((params) => getsysTargetingList(params))
     /*****************************/
 
+    // 回填数据
+    useEffect(() => {
+        if (sysTargetingId) {
+            setSelectedRowKeys([sysTargetingId])
+        }
+    }, [sysTargetingId])
+
     // 初始获取列表
     useEffect(() => {
         getList({ pageSize: 20, pageNum: 1 })

+ 8 - 0
src/pages/launchSystemNew/components/targetingTooltip/index.less

@@ -0,0 +1,8 @@
+.targetingTooltip {
+    >div {
+        font-size: 12px;
+        >span {
+            color: #3085ff;
+        }
+    }
+}

+ 181 - 0
src/pages/launchSystemNew/components/targetingTooltip/index.tsx

@@ -0,0 +1,181 @@
+import { DevicePriceEnum, EducationEnum, ExcludedDimensionEnum, MaritalStatusEnum, NetworkEnum, OptimizationGoalEnum, UserOsEnum, WechatAdBehaviorEnum } from "@/services/launchAdq/enum"
+import React, { useEffect, useState } from "react"
+import './index.less'
+
+const targetingData = [
+    { key: 'geoLocation', name: '地域' },
+    { key: 'age', name: '年龄' },
+    { key: 'gender', name: '性别' },
+    { key: 'education', name: '学历' },
+    { key: 'maritalStatus', name: '婚恋' },
+    { key: 'customAudience', name: '定向人群' },
+    { key: 'deviceBrandModel', name: '品牌型号' },
+    { key: 'wechatAdBehavior', name: '微信再营销' },
+    { key: 'networkType', name: '联网方式' },
+    { key: 'devicePrice', name: '设备价格' },
+    { key: 'userOs', name: '手机系统' },
+    { key: 'excludedConvertedAudience', name: '排除已转化用户' }
+]
+interface Props {
+    data: any,
+    geoLocationList: any // 所有地域
+    modelList: any  // 所有品牌手机
+}
+interface ContentProps {
+    unlimited?: string,         // 不限
+    geoLocation?: any[],        // 地域
+    age?: string,               // 年龄
+    gender?: string,            // 性别
+    education?: string[]        // 学历
+    maritalStatus?: string[],   // 婚恋
+    deviceBrandModel?: {
+        excludedList?: string[], // 品牌型号
+        includedList?: string[]  // 排除品牌型号
+    },
+    wechatAdBehavior?: {
+        actions?: string[],      // 再营销
+        excludedActions?: string[]// 排除营销
+    },
+    networkType?: string[]      // 联网方式
+    devicePrice?: string[]      // 手机价格
+    userOs?: string[]           // 手机系统
+    excludedConvertedAudience?: {
+        conversionBehaviorList: string[],
+        excludedDimension: string
+    }
+}
+/**
+ * 定向处理展示
+ * @returns 
+ */
+const TargetingTooltip: React.FC<Props> = (props) => {
+
+    /**********************/
+    const { data, geoLocationList, modelList } = props
+    const [content, setContent] = useState<ContentProps>({})
+    /**********************/
+
+    useEffect(() => {
+        if (data && geoLocationList) {
+            targetingData.forEach(item => {
+                if (Object.keys(data?.targeting)?.includes(item.key)) {
+
+                }
+            })
+            let newConten: ContentProps = {}
+            newConten.unlimited = targetingData.filter((item: any) => !Object.keys(data?.targeting).includes(item.key))?.map((item: any) => item?.name)?.toString()
+            Object.keys(data?.targeting)?.forEach((item: any) => {
+                switch (item) {
+                    case 'geoLocation': // 地域
+                        newConten.geoLocation = data?.targeting[item]?.regions?.map((item: any) => geoLocationList[item]?.name)
+                        break
+                    case 'age':
+                        let newAge = data?.targeting[item][0]
+                        newConten.age = `${newAge.min}至${newAge.max > 65 ? '66岁及以上' : newAge.max + '岁'}`
+                        break
+                    case 'gender':
+                        newConten.gender = data?.targeting[item][0]
+                        break
+                    case 'education':
+                        newConten.education = data?.targeting[item]
+                        break
+                    case 'maritalStatus':
+                        newConten.maritalStatus = data?.targeting[item]
+                        break
+                    case 'deviceBrandModel': // 品牌型号
+                        let newData = data?.targeting[item]
+                        let deviceBrandModel: { excludedList?: string[], includedList?: string[] } = {}
+                        if (newData?.excludedList) {
+                            deviceBrandModel.excludedList = newData?.excludedList?.map((key: any) => modelList[key]?.name)
+                        }
+                        if (newData?.includedList) {  // 排除
+                            deviceBrandModel.includedList = newData?.includedList?.map((key: any) => modelList[key]?.name)
+                        }
+                        newConten.deviceBrandModel = deviceBrandModel
+                        break
+                    case 'wechatAdBehavior': // 微信再营销
+                        let wechatAdBehaviorData = data?.targeting[item]
+                        let wechatAdBehavior: {
+                            actions?: string[],      // 再营销
+                            excludedActions?: string[]// 排除营销
+                        } = {}
+                        if (wechatAdBehaviorData?.actions) {
+                            wechatAdBehavior.actions = wechatAdBehaviorData?.actions
+                        }
+                        if (wechatAdBehaviorData?.excludedActions) {  // 排除
+                            wechatAdBehavior.excludedActions = wechatAdBehaviorData?.excludedActions
+                        }
+                        newConten.wechatAdBehavior = wechatAdBehavior
+                        break
+                    case 'networkType':
+                        newConten.networkType = data?.targeting[item]
+                        break
+                    case 'devicePrice':
+                        newConten.devicePrice = data?.targeting[item]
+                        break
+                    case 'userOs':
+                        newConten.userOs = data?.targeting[item]
+                        break
+                    case 'excludedConvertedAudience':
+                        newConten.excludedConvertedAudience = data?.targeting[item]
+                        break
+                }
+                setContent(newConten)
+            })
+            
+        }
+    }, [data, geoLocationList])
+
+
+
+    return <div className='targetingTooltip'>
+        {content?.geoLocation && <div>
+            地域:<span>{content?.geoLocation?.toString()}</span>
+        </div>}
+        {content?.age && <div>
+            年龄:<span>{content?.age}</span>
+        </div>}
+        {content?.gender && <div>
+            性别:<span>{content?.gender === 'MALE' ? '男' : '女'}</span>
+        </div>}
+        {(content?.education && content?.education?.length > 0) && <div>
+            学历:<span>{content?.education?.map((key: string) => EducationEnum[key]).toString()}</span>
+        </div>}
+        {(content?.networkType && content?.networkType?.length > 0) && <div>
+            联网方式:<span>{content?.networkType?.map((key: string) => NetworkEnum[key]).toString()}</span>
+        </div>}
+        {(content?.devicePrice && content?.devicePrice?.length > 0) && <div>
+            手机价格:<span>{content?.devicePrice?.map((key: string) => DevicePriceEnum[key]).toString()}</span>
+        </div>}
+        {(content?.userOs && content?.userOs?.length > 0) && <div>
+            手机系统:<span>{content?.userOs?.map((key: string) => UserOsEnum[key]).toString()}</span>
+        </div>}
+        {(content?.maritalStatus && content?.maritalStatus?.length > 0) && <div>
+            婚恋:<span>{content?.maritalStatus?.map((key: string) => MaritalStatusEnum[key]).toString()}</span>
+        </div>}
+        {content?.deviceBrandModel && <>
+            {(content?.deviceBrandModel?.includedList && content?.deviceBrandModel?.includedList?.length > 0) && <div>
+                排除品牌型号:<span>{content?.deviceBrandModel?.includedList?.toString()}</span>
+            </div>}
+            {(content?.deviceBrandModel?.excludedList && content?.deviceBrandModel?.excludedList?.length > 0) && <div>
+                品牌型号:<span>{content?.deviceBrandModel?.excludedList?.toString()}</span>
+            </div>}
+        </>}
+        {content?.wechatAdBehavior && <>
+            {(content?.wechatAdBehavior?.actions && content?.wechatAdBehavior?.actions?.length > 0) && <div>
+                再营销:<span>{content?.wechatAdBehavior?.actions?.map((key: string) => WechatAdBehaviorEnum[key]).toString()}</span>
+            </div>}
+            {(content?.wechatAdBehavior?.excludedActions && content?.wechatAdBehavior?.excludedActions?.length > 0) && <div>
+                排除营销:<span>{content?.wechatAdBehavior?.excludedActions?.map((key: string) => WechatAdBehaviorEnum[key]).toString()}</span>
+            </div>}
+        </>}
+        {content?.excludedConvertedAudience && <div>
+            排除已转化用户:<span>{ExcludedDimensionEnum[content?.excludedConvertedAudience?.excludedDimension]}{`(自定义转化行为:${OptimizationGoalEnum[content?.excludedConvertedAudience?.conversionBehaviorList[0]]})`}</span>
+        </div>}
+        {content?.unlimited && <div>
+            不限:<span>{content?.unlimited}</span>
+        </div>}
+    </div>
+}
+
+export default React.memo(TargetingTooltip)

+ 5 - 0
src/pages/launchSystemNew/launchManage/createAd/index.less

@@ -93,6 +93,11 @@
           .centerContent {
             width: 100%;
             min-height: 200px;
+            font-size: 12px;
+
+            // span {
+            //   color: #3085ff;
+            // }
           }
 
           .acc {

+ 59 - 30
src/pages/launchSystemNew/launchManage/createAd/index.tsx

@@ -2,6 +2,7 @@ import Tables from "@/components/Tables"
 import { useAjax } from "@/Hook/useAjax"
 import { CreateAdProps } from "@/services/launchAdq/createAd"
 import { BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType, SiteSetEnum } from "@/services/launchAdq/enum"
+import { getTagsList } from "@/services/launchAdq/global"
 import { getSysAdgroupsInfo } from "@/services/launchAdq/localAd"
 import { getsysTargetingInfo } from "@/services/launchAdq/targeting"
 import { CloseOutlined, SearchOutlined } from "@ant-design/icons"
@@ -15,6 +16,7 @@ import IdModal from "../../components/idModal"
 import LookLanding from "../../components/lookLanding"
 import SelectCloud from "../../components/selectCloud"
 import TargetingModal from "../../components/targetingModal"
+import TargetingTooltip from "../../components/targetingTooltip"
 import { WxAutoButton } from "../../req"
 import style from './index.less'
 import Selector from "./selector"
@@ -47,14 +49,40 @@ const CreateAd: React.FC = () => {
     const [selectImgVisible, setSelectImgVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
     const [lookVisible, setLookVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
     const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
-    const getSysAdgroups = useAjax((params) => getSysAdgroupsInfo(params))
-    const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
     const [wxButtonList, setWxButtonList] = useState<WxAutoButton[]>([])
     const [tableData, setTableData] = useState<any[]>([])   // 预览表格
     const [tableSelect, setTableSelect] = useState<any[]>([])
+    const [geoLocationList, setGeoLocationList] = useState<any>({}) // 所有地域列表
+    const [modelList, setModelList] = useState<any>({})  // 所有品牌手机
     const { init, get } = useModel('useLaunchAdq.useBdMediaPup')
+
+
+    const tagsList_REGION = useAjax((params) => getTagsList(params))
+    const tagsList_MODEL = useAjax((params) => getTagsList(params))
+    const getSysAdgroups = useAjax((params) => getSysAdgroupsInfo(params))
+    const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
     /*************************/
 
+    // 设置地域
+    useEffect(() => {
+        tagsList_REGION.run({ type: 'REGION' }).then(res => {
+            if (res) {
+                setGeoLocationList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
+                    prev[cur.id] = cur
+                    return prev
+                }, {}))
+            }
+        })
+        tagsList_MODEL.run({ type: 'DEVICE_BRAND_MODEL' }).then(res => {
+            if (res) {
+                setModelList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
+                    prev[cur.id] = cur
+                    return prev
+                }, {}))
+            }
+        })
+    }, [])
+
     // 获取账户列表
     useEffect(() => {
         getAdAccount.run()
@@ -192,16 +220,16 @@ const CreateAd: React.FC = () => {
                                     <Spin spinning={getSysAdgroups.loading}>
                                         <div className={style.centerContent}>
                                             {getSysAdgroups?.data ? <>
-                                                <div>广告名称: {getSysAdgroups?.data?.adgroupName}</div>
-                                                <div>推广目标: {PromotedObjectType[getSysAdgroups?.data?.promotedObjectType]}</div>
-                                                <div>广告版位: {getSysAdgroups?.data?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</div>
-                                                <div>投放日期: {getSysAdgroups?.data?.endDate ? getSysAdgroups?.data?.beginDate + '~' + getSysAdgroups?.data?.endDate : getSysAdgroups?.data?.beginDate + '~' + '长期投放'}</div>
-                                                <div>出价方式: {BidModeEnum[getSysAdgroups?.data?.bidMode]}</div>
-                                                <div>优化目标: {OptimizationGoalEnum[getSysAdgroups?.data?.optimizationGoal]}</div>
-                                                <div>出价类型: {getSysAdgroups?.data?.smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</div>
-                                                <div>出价策略: {BidStrategyEnum[getSysAdgroups?.data?.bidStrategy]}</div>
-                                                <div>广告出价: {getSysAdgroups?.data?.bidAmount}</div>
-                                                <div>广告日预算: {getSysAdgroups?.data?.dailyBudget || '不限'}</div>
+                                                <div>广告名称: <span>{getSysAdgroups?.data?.adgroupName}</span></div>
+                                                <div>推广目标: <span>{PromotedObjectType[getSysAdgroups?.data?.promotedObjectType]}</span></div>
+                                                <div>广告版位: <span>{getSysAdgroups?.data?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span></div>
+                                                <div>投放日期: <span>{getSysAdgroups?.data?.endDate ? getSysAdgroups?.data?.beginDate + '~' + getSysAdgroups?.data?.endDate : getSysAdgroups?.data?.beginDate + '~' + '长期投放'}</span></div>
+                                                <div>出价方式: <span>{BidModeEnum[getSysAdgroups?.data?.bidMode]}</span></div>
+                                                <div>优化目标: <span>{OptimizationGoalEnum[getSysAdgroups?.data?.optimizationGoal]}</span></div>
+                                                <div>出价类型: <span>{getSysAdgroups?.data?.smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</span></div>
+                                                <div>出价策略: <span>{BidStrategyEnum[getSysAdgroups?.data?.bidStrategy]}</span></div>
+                                                <div>广告出价: <span>{getSysAdgroups?.data?.bidAmount}</span></div>
+                                                <div>广告日预算: <span>{getSysAdgroups?.data?.dailyBudget || '不限'}</span></div>
                                             </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                                         </div>
                                     </Spin>
@@ -221,8 +249,9 @@ const CreateAd: React.FC = () => {
                                     <Spin spinning={getsysTargeting.loading}>
                                         <div className={style.centerContent}>
                                             {getsysTargeting?.data ? <>
-                                                <div>定向名称: {getsysTargeting?.data?.targetingName}</div>
-                                                <div>定向描述: {getsysTargeting?.data?.description}</div>
+                                                <div>定向名称: <span>{getsysTargeting?.data?.targetingName}</span></div>
+                                                <div>定向描述: <span>{getsysTargeting?.data?.description}</span></div>
+                                                <TargetingTooltip data={getsysTargeting?.data} geoLocationList={geoLocationList} modelList={modelList}/>
                                             </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                                         </div>
                                     </Spin>
@@ -323,8 +352,8 @@ const CreateAd: React.FC = () => {
                                                             case 'TOP_IMAGE':
                                                                 return <Image width={80} src={item.topImageSpec.imageUrl} style={{ borderRadius: 8, overflow: 'hidden' }} key={index} />
                                                             case 'TOP_SLIDER':
-                                                                return <Space>
-                                                                    {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={80} src={url} style={{ borderRadius: 8 }} key={index} />)}
+                                                                return <Space wrap>
+                                                                    {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={70} src={url} style={{ borderRadius: 8 }} key={index} />)}
                                                                 </Space>
                                                             case 'TOP_VIDEO':
                                                                 return <video src={item.topVideoSpec.videoUrl} width={150} controls key={index}></video>
@@ -360,17 +389,17 @@ const CreateAd: React.FC = () => {
             hoverable
             extra={tableData?.length > 0 ? <Space>
                 <span>推广计划总数:{accountCreateLogs?.length}</span>
-                    <span>广告总数:{accountCreateLogs?.length}</span>
-                    {tableSelect?.length > 0 && <span> 已选:<span style={{ color: '#1890FF' }}>{tableSelect?.length}</span> 条</span>}
-                    {
-                        <Button type='primary' onClick={() => {
-                            if (tableSelect.length === 0) {
-                                message.error('请选择要提交的计划!')
-                                return
-                            };
-                            setSubVisible(true)
-                        }}>批量提交审核</Button>
-                    }
+                <span>广告总数:{accountCreateLogs?.length}</span>
+                {tableSelect?.length > 0 && <span> 已选:<span style={{ color: '#1890FF' }}>{tableSelect?.length}</span> 条</span>}
+                {
+                    <Button type='primary' onClick={() => {
+                        if (tableSelect.length === 0) {
+                            message.error('请选择要提交的计划!')
+                            return
+                        };
+                        setSubVisible(true)
+                    }}>批量提交审核</Button>
+                }
             </Space> : false
             }
         >
@@ -398,9 +427,9 @@ const CreateAd: React.FC = () => {
 
 
         {/* 选择广告 */}
-        {adVisible && <AdModal visible={adVisible} onClose={() => setAdVisible(false)} promotedObjectType={queryForm.promotedObjectType as string} onChange={(e) => { setQueryForm({ ...queryForm, sysAdgroupsId: e }); setAdVisible(false) }} />}
+        {adVisible && <AdModal visible={adVisible} onClose={() => setAdVisible(false)} promotedObjectType={queryForm.promotedObjectType as string} onChange={(e) => { setQueryForm({ ...queryForm, sysAdgroupsId: e }); setAdVisible(false) }} sysAdgroupsId={queryForm?.sysAdgroupsId}/>}
         {/* 选择定向 */}
-        {dxVisible && <TargetingModal visible={dxVisible} onClose={() => setDxVisible(false)} onChange={(e) => { setQueryForm({ ...queryForm, sysTargetingId: e }); setDxVisible(false) }} />}
+        {dxVisible && <TargetingModal visible={dxVisible} onClose={() => setDxVisible(false)} onChange={(e) => { setQueryForm({ ...queryForm, sysTargetingId: e }); setDxVisible(false) }} sysTargetingId={queryForm?.sysTargetingId}/>}
         {/* 选择商品 */}
         {goodsVisible && <GoodsModal visible={goodsVisible} data={accountCreateLogs} onClose={() => setGoodsVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setGoodsVisible(false) }} />}
         {/* 选择数据源 */}
@@ -412,7 +441,7 @@ const CreateAd: React.FC = () => {
         {/* 查看落地页 */}
         {lookVisible && <LookLanding visible={lookVisible} onClose={() => setLookVisible(false)} id={queryForm?.sysPageId as any} />}
         {/* 设置名称 */}
-        {subVisible && <SubmitModal visible={subVisible} onClose={() => setSubVisible(false)} onChange={submit}/>}
+        {subVisible && <SubmitModal visible={subVisible} onClose={() => setSubVisible(false)} onChange={submit} />}
     </Space>
 }