adgroupsPrice.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import { Card, Form, Input, InputNumber, Space, Switch, Tooltip } from "antd"
  2. import React, { useContext } from "react"
  3. import { DispatchAd } from "./newCreateAd";
  4. import New1Radio from "@/pages/launchSystemV3/components/New1Radio";
  5. import { BID_ALL_OCATION_MODE, BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, OPTIMIZATIONGOAL_ENUM, ROI_ALL_OCATION_MODE, SMART_BID_TYPE_ENUM } from "../../const";
  6. import { QuestionCircleFilled } from "@ant-design/icons";
  7. /**
  8. * 出价与预算
  9. * @returns
  10. */
  11. const AdgroupsPrice: React.FC = () => {
  12. /****************************************/
  13. const { form, setOGPparams, OGPParams, putInType } = useContext(DispatchAd)!;
  14. const siteSet = Form.useWatch('siteSet', form)
  15. const bidMode = Form.useWatch('bidMode', form)
  16. const bidAllocationMode = Form.useWatch('bidAllocationMode', form)
  17. const roiAllocationMode = Form.useWatch(['deepConversionSpec', 'deepConversionWorthSpec', 'roiAllocationMode'], form)
  18. const optimizationGoal = Form.useWatch('optimizationGoal', form)
  19. const smartBidType = Form.useWatch('smartBidType', form)
  20. const bidScene = Form.useWatch('bidScene', form)
  21. const autoAcquisitionEnabled = Form.useWatch('autoAcquisitionEnabled', form)
  22. const automaticSiteEnabled = Form.useWatch('automaticSiteEnabled', form)
  23. const deepConversionType = Form.useWatch(['deepConversionSpec', 'deepConversionType'], form);
  24. const goal = Form.useWatch(['deepConversionSpec', deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? 'deepConversionBehaviorSpec' : 'deepConversionWorthSpec', 'goal'], form);
  25. /****************************************/
  26. return <Card
  27. title={<strong style={{ fontSize: 18 }}>出价与预算</strong>}
  28. className="cardResetCss"
  29. >
  30. <Form.Item
  31. label={<Space>
  32. <strong>计费方式</strong>
  33. <Tooltip title={`设置了优化目标CPM、CPC、CPA不可选`}>
  34. <QuestionCircleFilled />
  35. </Tooltip>
  36. </Space>}
  37. name='bidMode'
  38. rules={[{ required: true, message: '请选择计费方式' }]}
  39. >
  40. <New1Radio
  41. data={Object.keys(BID_MODE_ENUM).filter(key => {
  42. if (siteSet?.some((name: string) => ['SITE_SET_CHANNELS', 'SITE_SET_MOMENTS'].includes(name)) || automaticSiteEnabled) {
  43. return key === 'BID_MODE_OCPM' || key === 'BID_MODE_CPM'
  44. } else {
  45. return true
  46. }
  47. })?.map(key => ({ label: BID_MODE_ENUM[key as keyof typeof BID_MODE_ENUM], value: key, disabled: optimizationGoal && ['BID_MODE_CPM', 'BID_MODE_CPC', 'BID_MODE_CPA'].includes(key) ? true : false }))}
  48. onChange={(e) => {
  49. // form.setFieldsValue({ siteSet: defaultSiteSet })
  50. setOGPparams({ ...OGPParams, automaticSiteEnabled: e, bidMode: e as string })
  51. if (e === "BID_MODE_CPM" || e === "BID_MODE_CPC") {
  52. form.setFieldsValue({
  53. optimizationGoal: null,
  54. smartBidType: null,
  55. bidScene: null,
  56. // bidAmount:null,
  57. bidStrategy: null,
  58. autoAcquisitionEnabled: false,
  59. autoAcquisitionBudget: null,
  60. dailyBudget: null,
  61. })
  62. } else {
  63. form.setFieldsValue({
  64. optimizationGoal: "OPTIMIZATIONGOAL_ECOMMERCE_ORDER",
  65. smartBidType: "SMART_BID_TYPE_CUSTOM",
  66. bidScene: "BID_SCENE_NORMAL_AVERAGE",
  67. bidAmount: '1000',
  68. bidStrategy: "BID_STRATEGY_TARGET_COST",
  69. autoAcquisitionEnabled: false,
  70. autoAcquisitionBudget: null,
  71. dailyBudget: null,
  72. })
  73. }
  74. }}
  75. />
  76. </Form.Item>
  77. {(bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') && <>
  78. {putInType === 'GAME' ? <Form.Item label={<strong>出价场景</strong>} name='bidScene' rules={[{ required: true, message: '请选择出价场景' }]}>
  79. <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 }))} />
  80. </Form.Item> : <Form.Item label={<strong>出价类型</strong>} name='smartBidType' rules={[{ required: true, message: '请选择出价类型' }]}>
  81. <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 }))} />
  82. </Form.Item>}
  83. </>}
  84. {(putInType === 'GAME' ? bidScene !== 'BID_SCENE_NORMAL_MAX' : smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC') && <>
  85. <Form.Item label={<strong>出价分配方式</strong>} name='bidAllocationMode' rules={[{ required: true, message: '请选择出价分配方式' }]}>
  86. <New1Radio data={BID_ALL_OCATION_MODE} />
  87. </Form.Item>
  88. {bidAllocationMode === 1 ? <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
  89. <Input
  90. placeholder={`请输入价格`}
  91. style={{ width: 480 }}
  92. suffix={`元/${optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}`}
  93. />
  94. </Form.Item> : <Form.Item label={<strong>{bidAllocationMode === 2 ? '随机出价' : '阶梯出价'}</strong>} required>
  95. <Space>
  96. <Form.Item name='bidAmountMin' rules={[{ required: true, message: '请输入价格最小值' }]} noStyle>
  97. <Input
  98. placeholder={`请输入价格最小值`}
  99. style={{ width: 200 }}
  100. />
  101. </Form.Item>
  102. <span>-</span>
  103. <Form.Item name='bidAmountMax' rules={[{ required: true, message: '请输入价格最大值' }]} noStyle>
  104. <Input
  105. placeholder={`请输入价格最大值`}
  106. style={{ width: 200 }}
  107. />
  108. </Form.Item>
  109. <span>元/{optimizationGoal ? OPTIMIZATIONGOAL_ENUM[optimizationGoal as keyof typeof OPTIMIZATIONGOAL_ENUM] : ['BID_MODE_OCPM', 'BID_MODE_OCPC'].includes(bidMode) ? '千次曝光' : '点击'}</span>
  110. </Space>
  111. </Form.Item>}
  112. {deepConversionType === 'DEEP_CONVERSION_BEHAVIOR' ? <>
  113. <Form.Item label={<strong>深度目标出价</strong>} name={['deepConversionSpec', 'deepConversionBehaviorSpec', 'bidAmount']} rules={[{ required: true, message: '请输入深度目标出价' }]}>
  114. <Input style={{ width: 480 }} suffix={`元/${OPTIMIZATIONGOAL_ENUM[goal as keyof typeof OPTIMIZATIONGOAL_ENUM] || '优化目标'}`} placeholder={`请输入深度目标出价,范围0.1~10000`} />
  115. </Form.Item>
  116. </> :
  117. deepConversionType === 'DEEP_CONVERSION_WORTH' ? <>
  118. <Form.Item label={<strong>ROI分配方式</strong>} name={['deepConversionSpec', 'deepConversionWorthSpec', 'roiAllocationMode']} rules={[{ required: true, message: '请选择ROI分配方式' }]}>
  119. <New1Radio data={ROI_ALL_OCATION_MODE} />
  120. </Form.Item>
  121. {roiAllocationMode === 1 ? <Form.Item
  122. label={<strong>期望ROI</strong>}
  123. name={['deepConversionSpec', 'deepConversionWorthSpec', 'expectedRoi']}
  124. rules={[
  125. { required: true, message: '请输入期望ROI' },
  126. { type: 'number', ...(goal === 'GOAL_1DAY_MONETIZATION_ROAS' ? { min: 0.001, max: 50, message: '范围0.001~50' } : { min: 0.001, max: 1000, message: '范围0.001~1000' }) },
  127. {
  128. validator: (_: any, value: string) => {
  129. if (!value || /^\d+(\.\d{0,3})?$/.test(value)) {
  130. return Promise.resolve();
  131. }
  132. return Promise.reject(new Error('请输入最多三位小数'));
  133. }
  134. }
  135. ]}
  136. >
  137. <InputNumber style={{ width: 480 }} placeholder={`期望ROI目标范围${goal === 'GOAL_1DAY_MONETIZATION_ROAS' ? '0.001~50' : '0.001~1000'},输入0.05,表示ROI目标为5%`} />
  138. </Form.Item> : <Form.Item label={<strong>{roiAllocationMode === 2 ? '随机ROI' : '阶梯ROI'}</strong>} required>
  139. <Space>
  140. <Form.Item
  141. name={['deepConversionSpec', 'deepConversionWorthSpec', 'expectedRoiMin']}
  142. rules={[
  143. { required: true, message: '请输入期望ROI最小值' },
  144. { type: 'number', ...(goal === 'GOAL_1DAY_MONETIZATION_ROAS' ? { min: 0.001, max: 50, message: '范围0.001~50' } : { min: 0.001, max: 1000, message: '范围0.001~1000' }) },
  145. {
  146. validator: (_: any, value: string) => {
  147. if (!value || /^\d+(\.\d{0,3})?$/.test(value)) {
  148. return Promise.resolve();
  149. }
  150. return Promise.reject(new Error('请输入最多三位小数'));
  151. }
  152. }
  153. ]}
  154. noStyle
  155. >
  156. <InputNumber
  157. placeholder={`请输入期望ROI最小值`}
  158. style={{ width: 228 }}
  159. />
  160. </Form.Item>
  161. <span>-</span>
  162. <Form.Item
  163. name={['deepConversionSpec', 'deepConversionWorthSpec', 'expectedRoiMax']}
  164. rules={[
  165. { required: true, message: '请输入期望ROI最大值' },
  166. { type: 'number', ...(goal === 'GOAL_1DAY_MONETIZATION_ROAS' ? { min: 0.001, max: 50, message: '范围0.001~50' } : { min: 0.001, max: 1000, message: '范围0.001~1000' }) },
  167. {
  168. validator: (_: any, value: string) => {
  169. if (!value || /^\d+(\.\d{0,3})?$/.test(value)) {
  170. return Promise.resolve();
  171. }
  172. return Promise.reject(new Error('请输入最多三位小数'));
  173. }
  174. }
  175. ]}
  176. noStyle
  177. >
  178. <InputNumber
  179. placeholder={`请输入期望ROI最大值`}
  180. style={{ width: 228 }}
  181. />
  182. </Form.Item>
  183. </Space>
  184. </Form.Item>}
  185. </> : null}
  186. {((bidMode === 'BID_MODE_OCPM' || bidMode === 'BID_MODE_OCPC') && (putInType === 'GAME' ? bidScene !== 'BID_SCENE_NORMAL_MAX' : smartBidType !== 'SMART_BID_TYPE_SYSTEMATIC')) && <>
  187. <Form.Item
  188. style={{ marginBottom: 10 }}
  189. label={<Space>
  190. <strong>一键起量</strong>
  191. <Tooltip title={<div>
  192. <p>1. 一键起量原理:给该广告提供一笔起量预算,系统会在 6 小时内快速花完预算,帮助广告激进探索,获取更多曝光,期间转化成本可能高于预期;</p>
  193. <p>
  194. <span>2. 一键起量注意事项:</span><br />
  195. 探索中任何原因导致广告暂停播放,都会导致起量中止,且恢复播放后也不会再继续探索; 一键起量期间产生的消耗不赔付,但转化计入赔付门槛判断;你可以在该广告的一键起量状态中止或结束时,重新设置起量预算,开始一次新的起量周期
  196. </p>
  197. <p>
  198. <span>点击查看</span><a href="https://e.qq.com/ads/helpcenter/detail?cid=3532&pid=2004" target="__blank">赔付规则</a><br />
  199. <span>点击了解</span><a href="https://e.qq.com/ads/helpcenter/detail?cid=3532&pid=2005" target="__blank">一键起量</a>
  200. </p>
  201. </div>}>
  202. <QuestionCircleFilled />
  203. </Tooltip>
  204. </Space>}
  205. name='autoAcquisitionEnabled'
  206. valuePropName="checked"
  207. >
  208. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  209. </Form.Item>
  210. {/* 一键起量开启时才出现 */}
  211. {autoAcquisitionEnabled && <Form.Item
  212. name='autoAcquisitionBudget'
  213. rules={[{ required: true, message: '请输入起量预算' }]}
  214. help={<div>
  215. <span>1. 一键起量期间产生的消耗不赔付,但转化计入赔付门槛判断</span><br />
  216. <span>2. 一键起量可能导致转化成本高于预期,且起量结束后不一定能持续消耗</span>
  217. </div>}
  218. >
  219. <Input placeholder='请输入起量预算,建议设置为出价的10倍,范围 200~100000 元,不能低于出价' style={{ width: 560 }} suffix="元" />
  220. </Form.Item>}
  221. </>}
  222. </>}
  223. <Form.Item label={<strong>广告日预算</strong>} name='dailyBudget' rules={[{ required: (smartBidType === 'SMART_BID_TYPE_SYSTEMATIC' || bidScene === 'BID_SCENE_NORMAL_MAX'), message: '请输入广告日预算' }]}>
  224. <Input placeholder={`广告日预算${(smartBidType === 'SMART_BID_TYPE_SYSTEMATIC' || bidScene === 'BID_SCENE_NORMAL_MAX') ? '' : ', 不填默认为不限'}`} style={{ width: 480 }} suffix="元/天" />
  225. </Form.Item>
  226. </Card>
  227. }
  228. export default React.memo(AdgroupsPrice)