updateAd.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import { Checkbox, DatePicker, Form, Input, message, Modal, notification, Radio, Select, Space, TimePicker, Tooltip } from "antd"
  2. import React, { useEffect, useState } from "react"
  3. import TimeInSelect from "../../components/timeInSelect"
  4. import moment from "moment"
  5. import { getTimeSeriesList } from "./const";
  6. import { editAdqAdgroupsDataApi } from "@/services/launchAdq/adq";
  7. import { useAjax } from "@/Hook/useAjax";
  8. import { BidStrategyEnum, GoalRoasEnum } from "@/services/launchAdq/enum";
  9. import { QuestionCircleOutlined } from "@ant-design/icons";
  10. import { RangePickerProps } from "antd/lib/date-picker";
  11. const { RangePicker } = DatePicker;
  12. let DatePickers: any = DatePicker
  13. interface Props {
  14. title: string,
  15. visible?: boolean,
  16. onChange?: () => void,
  17. onClose?: () => void,
  18. selectedRows: any[]
  19. }
  20. /**
  21. * 修改广告
  22. * @returns
  23. */
  24. const UpdateAd: React.FC<Props> = ({ title = '修改广告', visible, onChange, onClose, selectedRows }) => {
  25. /***************************/
  26. const [form] = Form.useForm();
  27. let dateType = Form.useWatch('dateType', form)
  28. let field = Form.useWatch('field', form)
  29. const [state, setState] = useState<any>({ isShowTime: [] })
  30. const [isBidSType, setIsBidSType] = useState<boolean>(true)
  31. const [timeSeriesType, setTimeSeriesType] = useState<'allDayLong' | 'timeInterValS'>('allDayLong')
  32. const editAdqAdgroupsData = useAjax((params) => editAdqAdgroupsDataApi(params))
  33. /***************************/
  34. useEffect(() => {
  35. setIsBidSType(selectedRows.every((item: { smartBidType: string }) => item.smartBidType === 'SMART_BID_TYPE_CUSTOM'))
  36. }, [selectedRows])
  37. const handleOk = () => {
  38. form.validateFields().then(values => {
  39. console.log(values);
  40. let newValues = JSON.parse(JSON.stringify(values))
  41. let field = newValues.field
  42. let adgroupsUpdateDatetimeDTO = {} // 排期
  43. let adgroupsUpdateBidAmountDTO = {} // 出价
  44. let deepConversionSpec = {} // ROI
  45. let data = {}
  46. if (timeSeriesType === 'allDayLong') {
  47. newValues.timeSeries = getTimeSeriesList()
  48. }
  49. if (title === '批量修改深度优化') {
  50. deepConversionSpec['deepConversionType'] = newValues.deepConversionType
  51. deepConversionSpec['deepConversionWorthSpec'] = {
  52. expectedRoi: newValues.deepBidAmount,
  53. goal: newValues.goal
  54. }
  55. } else {
  56. field?.forEach((key: string) => {
  57. switch (key) {
  58. case 'dateType':
  59. if (newValues.dateType === '2') {
  60. adgroupsUpdateDatetimeDTO['beginDate'] = moment(newValues.date).format('YYYY-MM-DD')
  61. adgroupsUpdateDatetimeDTO['endDate'] = ''
  62. } else {
  63. adgroupsUpdateDatetimeDTO['beginDate'] = moment(newValues.date[0]).format('YYYY-MM-DD')
  64. adgroupsUpdateDatetimeDTO['endDate'] = moment(newValues.date[1]).format('YYYY-MM-DD')
  65. }
  66. break
  67. case 'timeType':
  68. // if (timeSeriesType === 'timeInterValS') {
  69. // if (newValues.firstDayBeginTime && moment(newValues.firstDayBeginTime).format('HH:mm:ss') !== '00:00:00') {
  70. // message.error('1111')
  71. // return
  72. // }
  73. // }
  74. if (newValues.timeSeries) {
  75. adgroupsUpdateDatetimeDTO['timeSeries'] = newValues.timeSeries.join('')
  76. }
  77. if (newValues.firstDayBeginTime) {
  78. adgroupsUpdateDatetimeDTO['firstDayBeginTime'] = moment(newValues.firstDayBeginTime).format('HH:mm:ss')
  79. }
  80. break
  81. case 'bidSType':
  82. adgroupsUpdateBidAmountDTO['bidStrategy'] = newValues.bidStrategy
  83. adgroupsUpdateBidAmountDTO['bidAmount'] = newValues.bidAmount
  84. break
  85. case 'dailyBudget':
  86. data['dailyBudget'] = newValues.dailyBudget
  87. break
  88. case 'adgroupName':
  89. data['adgroupName'] = newValues.adgroupName
  90. break
  91. }
  92. })
  93. }
  94. editAdqAdgroupsData.run({ adgroupIds: selectedRows.map((item: { adgroupId: number }) => item.adgroupId), adgroupsUpdateDatetimeDTO, adgroupsUpdateBidAmountDTO, deepConversionSpec, ...data }).then(res => {
  95. if (res) {
  96. message.success(`修改操作完成.结果请在操作记录查询!`)//成功: ${res.success},失败: ${res.fail}
  97. if (res?.fail) {
  98. notification.error({
  99. message: `修改失败`,
  100. description: `成功: ${res.success},修改失败${res.fail}条,失败的请到任务列表查看`,
  101. duration: 0
  102. });
  103. }
  104. onChange?.()
  105. }
  106. })
  107. })
  108. }
  109. /** 禁止选择以前时间 */
  110. const disabledDate: RangePickerProps['disabledDate'] = current => {
  111. // Can not select days before today and today
  112. return current && current < moment().startOf('day');
  113. };
  114. return <Modal
  115. title={title}
  116. visible={visible}
  117. onOk={handleOk}
  118. width={800}
  119. onCancel={() => onClose && onClose()}
  120. confirmLoading={editAdqAdgroupsData.loading}
  121. >
  122. <Form
  123. form={form}
  124. labelCol={{ span: 4 }}
  125. className='ad_form_style'
  126. colon={false}
  127. initialValues={{
  128. dateType: '2',
  129. date: moment().startOf('day'),
  130. timeSeries: getTimeSeriesList(),
  131. firstDayBeginTime: moment('2023-02-24 00:00:00'),
  132. bidStrategy: 'BID_STRATEGY_TARGET_COST',
  133. optimizationMode: 'DEEP_CONVERSION_TARGET',
  134. deepConversionType: 'DEEP_CONVERSION_WORTH',
  135. goal: 'GOAL_1DAY_PURCHASE_ROAS'
  136. }}
  137. >
  138. {title === '批量修改' ? <>
  139. <Form.Item label={<strong>选择修改字段</strong>} name='field' rules={[{ required: true, message: '选择修改字段' }]}>
  140. <Checkbox.Group>
  141. <Checkbox value="dateType">投放日期</Checkbox>
  142. <Checkbox value="timeType">投放时段</Checkbox>
  143. <Checkbox value="bidSType" disabled={!isBidSType}>
  144. <Space size={5}>
  145. 出价
  146. {!isBidSType && <Tooltip title="选择的广告包含自动出价类型,不支持修改出价">
  147. <QuestionCircleOutlined />
  148. </Tooltip>}
  149. </Space>
  150. </Checkbox>
  151. <Checkbox value="dailyBudget">广告预算</Checkbox>
  152. <Checkbox value="adgroupName">广告名称</Checkbox>
  153. </Checkbox.Group>
  154. </Form.Item>
  155. {field?.includes('dateType') && <>
  156. <Form.Item label={<strong>投放日期</strong>} name='dateType'>
  157. <Radio.Group onChange={(e) => {
  158. if (e.target.value === "1") {
  159. form.setFieldsValue({ date: [moment().startOf('day'), moment().startOf('day').add(1, 'M')] })
  160. }
  161. if (e.target.value === "2") {
  162. form.setFieldsValue({ date: moment().startOf('day') })
  163. }
  164. }}>
  165. <Radio.Button value="1">选择开始与结束日期</Radio.Button>
  166. <Radio.Button value="2">长期投放</Radio.Button>
  167. </Radio.Group>
  168. </Form.Item>
  169. {/* 投放日期的不同展示不同的日期选择 */}
  170. {dateType === '1' ? <Form.Item name='date' rules={[{ required: true, message: '请选择日期' }]}>
  171. <RangePicker style={{ marginLeft: 125 }} disabledDate={disabledDate}></RangePicker>
  172. </Form.Item> : <Form.Item name='date' style={{ marginLeft: 125 }} rules={[{ required: true, message: '请选择日期' }]}>
  173. <DatePickers disabledDate={disabledDate} />
  174. </Form.Item>}
  175. </>}
  176. {field?.includes('timeType') && <>
  177. <Form.Item label={<strong>投放时段</strong>} style={{ marginBottom: 0 }}>
  178. <Space direction='vertical' style={{ width: '100%' }}>
  179. <Radio.Group
  180. name='timeSeriesType'
  181. value={timeSeriesType}
  182. onChange={(value) => {
  183. setTimeSeriesType(value.target.value)
  184. if (value.target.value === 'timeInterValS') {
  185. setState({ isShowTime: ['1'] })
  186. }
  187. }}>
  188. <Radio.Button value={'allDayLong'}>全天投放</Radio.Button>
  189. <Radio.Button value={'timeInterValS'}>指定多个时段</Radio.Button>
  190. </Radio.Group>
  191. {timeSeriesType === 'timeInterValS' && <Form.Item name='timeSeries' noStyle rules={[{ required: true, message: '请选择时段' }]}>
  192. <TimeInSelect />
  193. </Form.Item>}
  194. </Space>
  195. </Form.Item>
  196. <Form.Item label={<strong></strong>}>
  197. <Space>
  198. <Checkbox.Group options={[{ label: '指定首日开始投放时间', value: '1' }]} disabled={timeSeriesType === 'timeInterValS'} value={state.isShowTime} onChange={(checkedValue) => { setState({ ...state, isShowTime: checkedValue }) }} />
  199. {state?.isShowTime?.length > 0 && <Form.Item name='firstDayBeginTime' noStyle rules={[{ required: true, message: '请选择时间' }]}>
  200. <TimePicker />
  201. </Form.Item>}
  202. </Space>
  203. </Form.Item>
  204. </>}
  205. {field?.includes('bidSType') && <>
  206. <Form.Item label={<strong>出价策略</strong>} name='bidStrategy' rules={[{ required: true, message: '请选择出价策略' }]}>
  207. <Radio.Group>
  208. {Object.keys(BidStrategyEnum).map(key => {
  209. return <Radio.Button value={key} key={key} disabled={key === 'BID_STRATEGY_PRIORITY_CAP_COST'}>{BidStrategyEnum[key]}</Radio.Button>
  210. })}
  211. </Radio.Group>
  212. </Form.Item>
  213. <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
  214. <Input placeholder={`输入价格 元`} style={{ width: 300 }} />
  215. </Form.Item>
  216. </>}
  217. {field?.includes('dailyBudget') && <>
  218. <Form.Item label={<strong>广告预算</strong>} name='dailyBudget' rules={[
  219. { required: true, message: '请输入广告预算' },
  220. {
  221. required: true, message: `大于0,并且填写数字`, validator(rule, value, callback) {
  222. let regPos = /^[0-9]+.?[0-9]*/; //判断是否是数字。
  223. if (!regPos.test(value)) {
  224. return Promise.reject()
  225. }
  226. if (value >= 0) {
  227. return Promise.resolve()
  228. } else {
  229. return Promise.reject()
  230. }
  231. },
  232. },
  233. ]}>
  234. <Input placeholder={`输入价格 元`} style={{ width: 300 }} />
  235. </Form.Item>
  236. </>}
  237. {field?.includes('adgroupName') && <>
  238. <Form.Item label={<strong>广告名称</strong>} name='adgroupName' rules={[{ required: true, message: '请输入广告名称' }]}>
  239. <Input placeholder={`请输入广告名称`} style={{ width: 300 }} />
  240. </Form.Item>
  241. </>}
  242. </> : <>
  243. <Form.Item label={<strong>深度优化方式</strong>} name='optimizationMode' rules={[{ required: true, message: '请选择深度优化方式' }]}>
  244. <Radio.Group disabled>
  245. <Radio.Button value="DEEP_CONVERSION_TARGET">深度目标优化</Radio.Button>
  246. </Radio.Group>
  247. </Form.Item>
  248. <Form.Item label={<strong>深度优化类型</strong>} name='deepConversionType' rules={[{ required: true, message: '请选择深度优化类型' }]}>
  249. <Radio.Group disabled onChange={() => {
  250. form.setFieldsValue({
  251. goal: undefined,
  252. deepBidAmount: undefined
  253. })
  254. }}>
  255. <Radio.Button value="DEEP_CONVERSION_WORTH">优化 ROI</Radio.Button>
  256. </Radio.Group>
  257. </Form.Item>
  258. <Form.Item label={<strong>深度优化目标</strong>} name='goal' rules={[{ required: true, message: '请选择深度优化目标' }]}>
  259. <Select style={{ width: 380 }} placeholder='请选择'>
  260. {Object.keys(GoalRoasEnum).filter(key => ['GOAL_1DAY_PURCHASE_ROAS']?.includes(key)).map(key => <Select.Option value={key} key={key}>{GoalRoasEnum[key]}</Select.Option>)}
  261. </Select>
  262. </Form.Item>
  263. <Form.Item label={<strong>期望ROI</strong>} name='deepBidAmount' rules={[{ required: true, message: '请输入期望ROI' }]}>
  264. <Input style={{ width: 380 }} placeholder={`期望ROI目标范围0.001~1000,输入0.05,表示ROI目标为5%`} />
  265. </Form.Item>
  266. </>}
  267. </Form>
  268. </Modal>
  269. }
  270. export default React.memo(UpdateAd)