123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847 |
- 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 { QuestionCircleFilled } from "@ant-design/icons"
- import { getTargetingGagsApi } from "@/services/adqV3/global"
- import { useAjax } from "@/Hook/useAjax"
- import { DefaultOptionType } from "antd/lib/select"
- import InputName from "@/components/InputName"
- import moment from "moment"
- import { txtLength } from "@/utils/utils"
- import { addTargetingApi, checkTargetingApi, updateTargetingApi } from "@/services/adqV3"
- import { useModel } from "umi"
- const { Title, Paragraph } = Typography;
- interface Props {
- isBackVal?: boolean
- value?: any,
- visible?: boolean
- onClose?: () => void
- onChange?: (targeting?: any) => void
- }
- const AddTarget: React.FC<Props> = ({ isBackVal, value, visible, onChange, onClose }) => {
- /******************************/
- const [form] = Form.useForm();
- const geoLocationType = Form.useWatch('geoLocationType', form);
- const maritalStatusType = Form.useWatch('maritalStatusType', form);
- const deviceBrandModelType = Form.useWatch('deviceBrandModelType', form);
- const wechatAdBehaviorType = Form.useWatch('wechatAdBehaviorType', form);
- const userOsType = Form.useWatch('userOsType', form);
- const ageType = Form.useWatch('ageType', form);
- const min = Form.useWatch(['age', 'min'], form);
- const max = Form.useWatch(['age', 'max'], form);
- const education = Form.useWatch('education', form);
- const networkType = Form.useWatch('networkType', form);
- const devicePrice = Form.useWatch('devicePrice', 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 [regionsList, setRegionsList] = useState<any[]>([])
- const [modelList, setModelList] = useState([])
- const [osList, setOsList] = useState<DefaultOptionType[]>([])
- const [isAdd, setIsAdd] = useState<boolean>(false)
- const getTargetingGags = useAjax((params) => getTargetingGagsApi(params))
- const checkTargeting = useAjax((params) => checkTargetingApi(params))
- const updateTargeting = useAjax((params) => updateTargetingApi(params))
- const addTargeting = useAjax((params) => addTargetingApi(params))
- const { getAllUserAccount } = useModel('useLaunchAdq.useAdAuthorize')
- /******************************/
- // 地址 手机
- useEffect(() => {
- getTargetingGags.run({ type: 'REGION' }).then(res => {
- let arr: any = Object.values(res).filter(v => typeof v !== 'string')
- let parentList = arr.filter((item: { parentName: any }) => !item.parentName)
- let childrenList = arr.filter((item: { parentName: any }) => item.parentName)
- parentList = parentList.map((item: { name: any; id: any, parentId: any }) => {
- let children = childrenList?.filter((c: { parentId: any }) => {
- return item.id === c.parentId
- })
- let obj = {
- title: item.name,
- value: item.id,
- key: item.id,
- parentId: item.parentId,
- disabled: item.id === 710000 || item.id === 810000 || item.id === 820000,
- children: children.map((item: { name: any; id: any, parentId: any }) => ({
- title: item.name,
- value: item.id,
- key: item.id,
- parentId: item.parentId,
- disabled: item.parentId === 710000 || item.parentId === 810000 || item.parentId === 820000
- }))
- }
- return obj
- })
- parentList = parentList.map((item: any) => {
- let itemArr = item?.children?.map((c: any) => {
- let arr = childrenList.filter((d: { parentId: any }) => {
- return d.parentId === c.value
- })
- arr = arr.map((i: { name: any; id: any }) => ({
- title: i.name,
- value: i.id,
- key: i.id,
- }))
- return { ...c, children: arr }
- })
- return { ...item, children: itemArr }
- })
- let zg = parentList.filter((item: { title: string }) => item.title === '中国')
- let zg_children = parentList.filter((item: { title: string }) => (item.title !== '中国' && item.title !== '国外'))
- let gw = parentList.filter((item: { title: string }) => item.title === '国外')
- let earth = [
- { ...zg[0], children: zg_children },
- // { ...gw[0], disabled: true }
- ]
- setRegionsList(earth)
- })
- // 获取手机
- getTargetingGags.run({ type: 'DEVICE_BRAND_MODEL' }).then(res => {
- let arr: any = Object.values(res).filter(v => typeof v !== 'string')
- let parentList = arr.filter((item: { parentName: any }) => !item.parentName)
- let childrenList = arr.filter((item: { parentName: any }) => item.parentName)
- parentList = parentList.map((item: { name: any; id: any }) => {
- let children = childrenList?.filter((c: { parentId: any }) => {
- return item.id === c.parentId
- })
- let obj = {
- title: item.name,
- value: item.id,
- key: item.id,
- children: children.map((item: { name: any; id: any }) => ({
- title: item.name,
- value: item.id,
- key: item.id,
- }))
- }
- return obj
- })
- setModelList(parentList)
- })
- // 获取账户列表
- getAllUserAccount.run()
- }, [])
- // 系统处理
- useEffect(() => {
- let iosChildren: DefaultOptionType[] = []
- let androidChildren: DefaultOptionType[] = []
- Object.keys(USER_OS_ENUM).forEach(key => {
- if (key !== 'ANDROID' && key !== 'IOS') {
- if (key.includes('ANDROID')) {
- androidChildren.push({
- label: USER_OS_ENUM[key],
- value: key
- })
- }
- if (key.includes('IOS')) {
- iosChildren.push({
- label: USER_OS_ENUM[key],
- value: key
- })
- }
- }
- })
- setOsList([{ label: 'iOS系统', value: 'IOS', children: iosChildren }, { label: 'Android系统', value: 'ANDROID', children: androidChildren }])
- }, [USER_OS_ENUM])
- const handleOk = async (values: any) => {
- console.log(values)
- const {
- targetingName,
- description,
- accountId,
- geoLocationType,
- ageType,
- geoLocation,
- age,
- gender,
- education,
- networkType,
- maritalStatusType,
- excludedConvertedAudience,
- deviceBrandModelType,
- deviceBrandModelList,
- isExcludedDeviceBrandModel,
- userOsType,
- os,
- isExcludedOs,
- devicePrice,
- wechatAdBehaviorType,
- ...surplusValues
- } = values
- let targetValues: any = {
- ...surplusValues,
- }
- if (geoLocationType === '1') {
- targetValues.geoLocation = {
- ...geoLocation,
- regions: geoLocation.regions[0] === 1156 ? regionsList[0]?.children?.filter((item: any) => !item.disabled)?.map((item: { value: any }) => item.value) : geoLocation.regions
- }
- }
- // 性别
- if (gender !== '0') {
- targetValues.gender = [gender]
- }
- // 学历
- if (!education?.includes('0')) {
- targetValues.education = education
- }
- // 联网方式
- if (!networkType?.includes('0')) {
- targetValues.networkType = networkType
- }
- // 年龄
- if (ageType !== '0' && ageType !== '1') {
- let [min, max] = ageType.split('_')
- targetValues.age = [{ min: Number(min), max: Number(max) }]
- } else if (ageType === '1') {
- targetValues.age = [age]
- }
- // 排除已转化用户
- if (excludedConvertedAudience?.excludedDimension !== '0') {
- targetValues.excludedConvertedAudience = excludedConvertedAudience
- }
- // 设备品牌型号 deviceBrandModelList, isExcludedDeviceBrandModel,
- if (deviceBrandModelType === '1') {
- if (isExcludedDeviceBrandModel) { // 排除
- targetValues.deviceBrandModel = {
- excludedList: deviceBrandModelList
- }
- } else {
- targetValues.deviceBrandModel = {
- includedList: deviceBrandModelList
- }
- }
- }
- // 操作系统 userOsType, os, isExcludedOs,
- if (userOsType === '1') {
- if (isExcludedOs) {
- targetValues.excludedOs = os
- } else {
- targetValues.userOs = os
- }
- }
- // 设备价格
- if (!devicePrice?.includes('0')) {
- targetValues.devicePrice = devicePrice
- }
- let targetingDTO = {
- targetingName,
- description,
- accountId,
- targeting: targetValues
- }
- if (isBackVal && !isAdd) {
- delete targetingDTO.accountId
- delete targetingDTO.description
- onChange?.(targetingDTO)
- return
- }
- let checkData = await checkTargeting.run(value?.id ? { ...targetingDTO, id: value?.id } : targetingDTO)
- if (checkData?.[0]?.isSame) {
- message.error('存在相同模板名称,请修改')
- return
- }
- if (value?.id) {
- updateTargeting.run({ ...targetingDTO, id: value?.id }).then(res => {
- message.success('修改成功')
- onChange?.()
- })
- } else {
- addTargeting.run(targetingDTO).then(res => {
- message.success('新增成功')
- if (isBackVal && isAdd) {
- delete targetingDTO.accountId
- delete targetingDTO.description
- onChange?.(targetingDTO)
- } else {
- onChange?.()
- }
- })
- }
- }
- // 回填数据
- useEffect(() => {
- if (value && Object.keys(value).length > 0 && regionsList.length) {
- let regionsAll = regionsList[0]?.children?.filter((item: any) => !item.disabled)?.map((item: { value: any }) => item.value)//全选省列表
- const {
- geoLocation,
- age,
- gender,
- education,
- networkType,
- maritalStatus,
- excludedConvertedAudience,
- deviceBrandModel,
- excludedOs,
- userOs,
- devicePrice,
- wechatAdBehavior,
- ...surplusValues
- } = JSON.parse(JSON.stringify(value))
- let targetValues: any = {
- ...surplusValues,
- age: age?.[0] || undefined,
- gender: gender?.[0] || '0',
- education: education || '0',
- networkType: networkType || '0',
- excludedConvertedAudience: excludedConvertedAudience || { excludedDimension: '0' },
- devicePrice: devicePrice || '0',
- wechatAdBehavior,
- maritalStatus
- }
- if (geoLocation && Object.keys(geoLocation).length > 0) {
- targetValues.geoLocationType = '1'
- targetValues.geoLocation = {
- ...geoLocation,
- regions: JSON.stringify(geoLocation.regions) === JSON.stringify(regionsAll) ? [1156] : geoLocation.regions
- }
- } else {
- targetValues.geoLocationType = '0'
- targetValues.geoLocation = {
- locationTypes: ['LIVE_IN']
- }
- }
- if (age?.length > 0) {
- let g = age?.[0]?.min + '_' + age?.[0]?.max
- if (['14_18', '19_24', '25_29', '30_39', '40_49', '50_66'].includes(g)) {
- targetValues.ageType = g
- } else {
- targetValues.ageType = '1'
- }
- } else {
- targetValues.ageType = '0'
- }
- if (maritalStatus?.length > 0) {
- targetValues.maritalStatusType = '1'
- } else {
- targetValues.maritalStatusType = '0'
- }
- if (deviceBrandModel?.includedList) {
- targetValues.deviceBrandModelType = '1'
- targetValues.deviceBrandModelList = deviceBrandModel?.includedList
- } else if (deviceBrandModel?.excludedList) {
- targetValues.deviceBrandModelType = '1'
- targetValues.deviceBrandModelList = deviceBrandModel?.excludedList
- targetValues.isExcludedDeviceBrandModel = true
- } else {
- targetValues.deviceBrandModelType = '0'
- }
- if (excludedOs?.length > 0) {
- targetValues.isExcludedOs = true
- targetValues.os = excludedOs
- targetValues.userOsType = '1'
- } else if (userOs?.length > 0) {
- targetValues.os = userOs
- targetValues.userOsType = '1'
- } else {
- targetValues.userOsType = '0'
- }
- let wechatAdBehaviorType = []
- if (wechatAdBehavior?.actions) {
- wechatAdBehaviorType.push('actions')
- }
- if (wechatAdBehavior?.excludedActions) {
- wechatAdBehaviorType.push('excludedActions')
- }
- targetValues.wechatAdBehaviorType = wechatAdBehaviorType.length > 0 ? wechatAdBehaviorType : ['0']
- form.setFieldsValue({ ...targetValues })
- }
- }, [value, regionsList])
- return <Modal
- title={isBackVal ? <strong style={{ fontSize: 20 }}>
- {(value && Object.keys(value).length > 0) ? `修改定向` : `新增定向`}
- </strong> : <strong style={{ fontSize: 20 }}>{value?.id ? `修改定向模板` : value?.isCopy ? `复制定向模板` : `新增定向模板`}</strong>}
- visible={visible}
- onCancel={onClose}
- footer={null}
- width={920}
- className={`modalResetCss`}
- bodyStyle={{ padding: '0 0 40px', position: 'relative', borderRadius: '0 0 8px 8px' }}
- maskClosable={false}
- >
- {getTargetingGags.loading && <div style={{ position: 'absolute', width: '100%', top: 0, left: 0, zIndex: 20 }}>
- <Spin spinning={getTargetingGags.loading}>
- <div style={{ width: '100%', height: 600, backgroundColor: 'rgba(255,255,255,0.95)' }}></div>
- </Spin>
- </div>}
- <Form
- form={form}
- name="newAdTarget"
- labelAlign='left'
- labelCol={{ span: 4 }}
- colon={false}
- style={{ backgroundColor: '#f1f4fc', maxHeight: 600, overflow: 'hidden', overflowY: 'auto', padding: '10px 10px 10px', borderRadius: '0 0 8px 8px' }}
- scrollToFirstError
- onFinishFailed={({ errorFields }) => {
- message.error(errorFields?.[0]?.errors?.[0])
- }}
- onFinish={handleOk}
- initialValues={{
- geoLocationType: '0',
- maritalStatusType: '0',
- deviceBrandModelType: '0',
- userOsType: '0',
- ageType: '0',
- wechatAdBehaviorType: ['0'],
- age: {
- min: 14,
- max: 66
- },
- gender: '0',
- education: ['0'],
- networkType: ['0'],
- devicePrice: ['0'],
- excludedConvertedAudience: {
- excludedDimension: '0'
- },
- geoLocation: {
- locationTypes: ['LIVE_IN']
- },
- targetingName: (isBackVal ? '定向' : '定向模板') + '_' + localStorage.getItem('userId') + '_' + moment().format('MM_DD_HH:mm:ss')
- }}
- >
- <Card
- title={<strong style={{ fontSize: 18 }}>定向选择</strong>}
- className="cardResetCss newCss"
- bodyStyle={{ padding: '4px 6px' }}
- style={{ marginBottom: 8 }}
- >
- <div className={style.newSpace}>
- <Form.Item name="geoLocationType" label={<strong>地理位置</strong>} style={{ marginBottom: 0 }}>
- <Radio.Group>
- <Radio value="0">不限</Radio>
- <Radio value="1">按区域</Radio>
- </Radio.Group>
- </Form.Item>
- {geoLocationType === '1' && <div className={style.newSpace_bottom}>
- {/* <Title level={5} style={{ fontSize: 14 }}>微信流量(除视频号/搜一搜)暂时仅支持 “常住地”或“旅行到访”。按法务合规要求,“常住地”暂不支持国内港澳台及国外地区,“旅行到访”仅支持部分国外地区。</Title> */}
- <Form.Item
- name={['geoLocation', 'regions']}
- rules={[
- { required: true, message: '请选择区域' },
- { type: 'array', max: 1000, message: '地理位置最多选择1000' },
- ]}
- >
- <TreeSelect
- placeholder="请选择"
- showSearch={true}
- maxTagCount={50}
- treeCheckable={true}
- showCheckedStrategy={TreeSelect.SHOW_PARENT}
- treeData={regionsList}
- loading={getTargetingGags.loading}
- style={{ width: '100%' }}
- allowClear
- filterTreeNode={(inputValue: string, treeNode: any) => {
- if (treeNode.title.includes(inputValue)) {
- return true
- } else {
- return false
- }
- }}
- />
- </Form.Item>
- <Form.Item
- name={['geoLocation', 'locationTypes']}
- rules={[{ required: true, message: '请选择地点类型' }]}
- >
- <Checkbox.Group
- disabled
- options={Object.keys(LOCATION_TYPES_ENUM)?.map(key => ({ label: LOCATION_TYPES_ENUM[key], value: key }))}
- />
- </Form.Item>
- </div>}
- </div>
- <div className={style.newSpace}>
- <Form.Item name="ageType" label={<strong>年龄</strong>} style={{ marginBottom: 0 }}>
- <Radio.Group>
- <Radio value="0">不限</Radio>
- <Radio value="14_18">14~18岁</Radio>
- <Radio value="19_24">19~24岁</Radio>
- <Radio value="25_29">25~29岁</Radio>
- <Radio value="30_39">30~39岁</Radio>
- <Radio value="40_49">40~49岁</Radio>
- <Radio value="50_66">50岁及以上</Radio>
- <Radio value="1">自定义</Radio>
- </Radio.Group>
- </Form.Item>
- {ageType === '1' && <div className={`${style.newSpace_bottom} flexStart`} style={{ '--g': '5px' } as React.CSSProperties}>
- <Form.Item name={['age', 'min']}>
- <Select style={{ width: 185 }} placeholder="请选择">
- {Array(66 - 13).fill('').map((_, i) => i + 14).filter(i => i !== 15 && i !== 16 && i !== 17).map(i => {
- return <Select.Option disabled={i > max} value={i} key={i}>{i === 66 ? '66 岁及以上' : i + ' 岁'}</Select.Option>
- })}
- </Select>
- </Form.Item>
- <span>-</span>
- <Form.Item name={['age', 'max']}>
- <Select style={{ width: 185 }} placeholder="请选择">
- {Array(66 - 17).fill('').map((_, i) => {
- return <Select.Option disabled={i + 18 < min} value={i + 18} key={i + 18}>{i + 18 === 66 ? '66 岁及以上' : i + 18 + ' 岁'}</Select.Option>
- })}
- </Select>
- </Form.Item>
- </div>}
- </div>
- <div className={style.newSpace}>
- <Form.Item name="gender" label={<strong>性别</strong>} style={{ marginBottom: 0 }}>
- <Radio.Group>
- <Radio value="0">不限</Radio>
- {Object.keys(GENDER_ENUM).map(key => {
- return <Radio value={key} key={key}>{GENDER_ENUM[key]}</Radio>
- })}
- </Radio.Group>
- </Form.Item>
- </div>
- <div className={style.newSpace}>
- <Form.Item
- name="education"
- label={<Space>
- <strong>学历</strong>
- <Tooltip title="用户的最高学历">
- <QuestionCircleFilled />
- </Tooltip>
- </Space>}
- style={{ marginBottom: 0 }}
- getValueFromEvent={(e: string[]) => {
- if (e.length > 1 && !education.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(EDUCATION_ENUM)?.map(key => ({ label: EDUCATION_ENUM[key], value: key }))
- ]}
- />
- </Form.Item>
- </div>
- <div className={style.newSpace}>
- <Form.Item
- name="networkType"
- label={<strong>联网方式</strong>}
- style={{ marginBottom: 0 }}
- getValueFromEvent={(e: string[]) => {
- if (e.length > 1 && !networkType.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(NETWORK_ENUM)?.map(key => ({ label: NETWORK_ENUM[key], value: key }))
- ]}
- />
- </Form.Item>
- </div>
- <div className={style.newSpace}>
- <Form.Item
- label={<Space>
- <strong>自定义人群</strong>
- <Tooltip title={<span>
- 自定义人群是指客户通过腾讯广告知数(原DMP)创建和管理自己定义类人群,包括您自行上传的号码包人群等。仅当出价方式选择CPC、 CPM或oCPM(且优化目标为“点击”)时,你可以“二方人群”进行投放。
- <a href="https://e.qq.com/ads/helpcenter/detail/?cid=3161&pid=8995" target="__blank">了解更多</a>
- </span>}>
- <QuestionCircleFilled />
- </Tooltip>
- </Space>}
- style={{ marginBottom: 0 }}
- >
- <Space>
- <Checkbox.Group
- defaultValue={['0']}
- options={[
- { label: '不限', value: '0' }
- ]}
- />
- <span style={{ color: '#FAAD14' }}>自定义人群必须关联指定账户,当前关联账户为不限</span>
- </Space>
- </Form.Item>
- </div>
- <div className={style.newSpace}>
- <Form.Item name="maritalStatusType" label={<strong>婚恋育儿状态</strong>} style={{ marginBottom: maritalStatusType === '1' ? 4 : 12 }}>
- <Radio.Group>
- <Radio value="0">不限</Radio>
- <Radio value="1">自定义</Radio>
- </Radio.Group>
- </Form.Item>
- {maritalStatusType === '1' && <div className={`${style.newSpace_bottom}`} style={{ marginBottom: 6 }}>
- <Form.Item name={'maritalStatus'} rules={[{ required: true, message: '请选择婚恋育儿状态' }]}>
- <Checkbox.Group options={Object.keys(MARITAL_STATUS_ENUM).map(key => ({ label: MARITAL_STATUS_ENUM[key], value: key }))} />
- </Form.Item>
- </div>}
- <Form.Item
- name={['excludedConvertedAudience', 'excludedDimension']}
- label={<Space>
- <strong>排除已转化用户</strong>
- <Tooltip title={<>
- <Paragraph>
- 设置排除已转化定向,广告不会曝光给所选范围内已转化的用户;
- 系统将自动以当前广告选择的优化目标作为此定向的转化行为(不支持“点击”、“次留率”优化目标);
- 该定向仅可选择oCPC、oCPM、oCPA出价方式,若勾选自定义转化行为则不限制出价方式 。
- </Paragraph>
- <a href="https://e.qq.com/ads/helpcenter/detail/?cid=3531&pid=2612" target="__blank">了解更多</a>
- </>}>
- <QuestionCircleFilled />
- </Tooltip>
- </Space>}
- style={{ marginBottom: excludedDimension === '0' ? 12 : 4 }}
- >
- <Radio.Group>
- <Radio value="0">不限</Radio>
- {Object.keys(EXCLUDED_DIMENSION_ENUM).map(key => {
- return <Radio value={key} key={key}>{EXCLUDED_DIMENSION_ENUM[key]}</Radio>
- })}
- </Radio.Group>
- </Form.Item>
- {excludedDimension !== '0' && <div className={`${style.newSpace_bottom}`} style={{ marginBottom: 6 }}>
- <Title level={5} style={{ fontSize: 14 }}>系统自动依照当前广告选择的优化目标作为此定向的转化行为</Title>
- <Form.Item label="自定义转化行为" name={['excludedConvertedAudience', 'conversionBehaviorList']} rules={[{ required: true, message: '请选择自定义转化行为' }]}>
- <Select
- showSearch
- filterOption={(input, option) =>
- (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
- }
- allowClear
- placeholder='请选择自定义转化行为'
- mode="multiple"
- style={{ width: 480 }}
- >
- {Object.keys(OPTIMIZATIONGOAL_ENUM).filter(key => key !== 'OPTIMIZATIONGOAL_NONE').map(key => {
- return <Select.Option value={key} key={key} disabled={conversionBehaviorList?.length >= 2 && !conversionBehaviorList.includes(key)}>{OPTIMIZATIONGOAL_ENUM[key]}</Select.Option>
- })}
- </Select>
- </Form.Item>
- </div>}
- <Form.Item name="deviceBrandModelType" label={<strong>设备品牌型号</strong>} style={{ marginBottom: deviceBrandModelType === '0' ? 0 : 4 }}>
- <Radio.Group>
- <Radio value="0">不限</Radio>
- <Radio value="1">自定义</Radio>
- </Radio.Group>
- </Form.Item>
- {deviceBrandModelType === '1' && <div className={`${style.newSpace_bottom}`} style={{ marginBottom: 6 }}>
- <Form.Item
- name={'deviceBrandModelList'}
- rules={[
- { required: true, message: '请选择设备品牌' },
- { type: 'array', max: 400, message: '设备品牌最多选择400个设备型号' }
- ]}
- style={{ marginBottom: 10 }}
- >
- <TreeSelect
- placeholder="请选择"
- showSearch={true}
- maxTagCount={20}
- treeCheckable={true}
- showCheckedStrategy={TreeSelect.SHOW_CHILD}
- treeData={modelList}
- style={{ width: '100%' }}
- allowClear
- filterTreeNode={(inputValue: string, treeNode: any) => {
- if (treeNode.title.includes(inputValue)) {
- return true
- } else {
- return false
- }
- }}
- />
- </Form.Item>
- <Form.Item name='isExcludedDeviceBrandModel' valuePropName="checked">
- <Checkbox>排除所选设备的用户</Checkbox>
- </Form.Item>
- </div>}
- </div>
- <div className={style.newSpace}>
- <Form.Item name="userOsType" label={<strong>操作系统版本</strong>} style={{ marginBottom: 0 }}>
- <Radio.Group>
- <Radio value="0">不限</Radio>
- <Radio value="1">自定义</Radio>
- </Radio.Group>
- </Form.Item>
- {userOsType === '1' && <div className={`${style.newSpace_bottom}`}>
- <Form.Item
- name={'os'}
- style={{ marginBottom: 10 }}
- rules={[
- { required: true, message: '请选择操作系统版本' },
- { type: 'array', max: 100, message: '操作系统版本最多选择100个设备型号' }
- ]}
- >
- <TreeSelect
- placeholder="请选择"
- showSearch={true}
- maxTagCount={10}
- treeCheckable={true}
- showCheckedStrategy={TreeSelect.SHOW_PARENT}
- treeData={osList}
- style={{ width: '100%' }}
- allowClear
- filterTreeNode={(inputValue: string, treeNode: any) => {
- if (treeNode.title.includes(inputValue)) {
- return true
- } else {
- return false
- }
- }}
- />
- </Form.Item>
- <Form.Item name='isExcludedOs' valuePropName="checked">
- <Checkbox>排除所选操作系统版本的用户</Checkbox>
- </Form.Item>
- </div>}
- </div>
- <div className={style.newSpace}>
- <Form.Item
- name="devicePrice"
- label={<strong>设备价格</strong>}
- style={{ marginBottom: 0 }}
- getValueFromEvent={(e: string[]) => {
- if (e.length > 1 && !devicePrice.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(DEVICE_PRICE_ENUM)?.map(key => ({ label: DEVICE_PRICE_ENUM[key], value: key }))
- ]}
- />
- </Form.Item>
- </div>
- <div className={style.newSpace}>
- <Form.Item
- name="wechatAdBehaviorType"
- label={<strong>微信再营销</strong>}
- style={{ marginBottom: 0 }}
- getValueFromEvent={(e: string[]) => {
- if (e.length > 1 && !wechatAdBehaviorType.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' },
- { label: '再营销', value: 'actions' },
- { label: '排除营销', value: 'excludedActions' }
- ]}
- />
- </Form.Item>
- {(wechatAdBehaviorType && !wechatAdBehaviorType?.includes('0')) && <div className={`${style.newSpace_bottom}`}>
- {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 => item !== 'WECHAT_WORK_CONTACTS_ADDED').map(key => ({ label: WECHAT_AD_NEHAVIOR_ENUM[key], 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], value: key, disabled: actions?.some((k: string) => k === key) }))} />
- </Form.Item>
- </>}
- </div>}
- </div>
- </Card>
- <Card
- title={<strong style={{ fontSize: 18 }}>定向设置</strong>}
- className="cardResetCss"
- >
- <Form.Item
- label={<strong>定向模板名称</strong>}
- name='targetingName'
- // tooltip="下标、日期时分秒、广告账户创建时默认自带"
- rules={[
- { required: true, message: '请输入定向模板名称!' },
- {
- required: true, message: '定向模板名称不能包含特殊字符:< > & ‘ ” / 以及TAB、换行、回车键,请修改', validator(_, value) {
- let reg = /[&‘’“”/\n\t\f]/ig
- if (value && reg.test(value)) {
- return Promise.reject()
- }
- return Promise.resolve()
- }
- },
- {
- required: true, message: '请确保定向模板名称长度不超过30个字(1个汉字等于2个字符)', validator(_, value) {
- if (value && txtLength(value) > 30) {
- return Promise.reject()
- }
- return Promise.resolve()
- }
- }
- ]}
- >
- <InputName placeholder='定向模板名称' style={{ width: 480 }} length={30} />
- </Form.Item>
- {!isBackVal && <>
- <Form.Item
- label={<strong>定向模板描述</strong>}
- name='description'
- >
- <Input.TextArea style={{ width: 480 }} placeholder="定向模板描述" />
- </Form.Item>
- <Form.Item
- label={<strong>关联账户</strong>}
- name='accountId'
- >
- <Select
- style={{ width: 480 }}
- showSearch
- filterOption={(input, option) =>
- (option!.children as unknown as string)?.toLowerCase()?.includes(input?.toLowerCase())
- }
- allowClear
- placeholder='请选择媒体账户'
- >
- {getAllUserAccount?.data?.data?.map((item: any) => <Select.Option value={item.accountId} key={item.id}>{item.remark ? item.accountId + '_' + item.remark : item.accountId}</Select.Option>)}
- </Select>
- </Form.Item>
- </>}
- </Card>
- <Form.Item className="submit_pull">
- <Space>
- {isBackVal && !(value && Object.keys(value).length > 0) && <Checkbox checked={isAdd} onChange={(e) => setIsAdd(e.target.checked)}>是否保存到定向模板</Checkbox>}
- <Button onClick={onClose}>取消</Button>
- <Button type="primary" htmlType="submit" className="modalResetCss" loading={checkTargeting.loading || updateTargeting.loading || addTargeting.loading}>
- 确定
- </Button>
- </Space>
- </Form.Item>
- </Form>
- </Modal>
- }
- export default React.memo(AddTarget)
|