strategy.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import NewSteps, { NewStepsItem } from '@/pages/weComTask/components/newSteps';
  2. import { App, Button, Card, Form, Input, Radio } from 'antd';
  3. import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
  4. import '../../global.less';
  5. import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'
  6. import SendTimeSet from '@/pages/weComTask/components/sendTimeSet';
  7. import style from './index.less';
  8. import dayjs from 'dayjs';
  9. import FilterUser from '@/pages/weComTask/components/filterUser';
  10. /**
  11. * 群发策略
  12. * @param param0
  13. * @returns
  14. */
  15. const Strategy = forwardRef(({ value, onChange }: TASK_CREATE.StrategyProps, ref: React.ForwardedRef<{ handleOk: (type: string) => void }>) => {
  16. /****************************************/
  17. const { message } = App.useApp()
  18. const ref1 = useRef<HTMLDivElement>(null)
  19. const [form] = Form.useForm();
  20. const strategySettings = Form.useWatch('strategySettings', form);
  21. const [stepsList, setStepsList] = useState<NewStepsItem[]>([
  22. { title: '群发配置', description: '群发类型、群发标题、适用产品', id: 'basicInfo' },
  23. {
  24. title: '群发策略', children: [{ title: `策略1`, id: 'strategy_0', children: [{ title: `发送时间`, id: `strategy_0_strategyName` }, { title: `发送对象1`, id: `strategy_0_sendData` }] }, { title: `完成` }]
  25. },
  26. { title: '完成' }
  27. ])
  28. /****************************************/
  29. useImperativeHandle(ref, () => ({
  30. handleOk(type) {
  31. handleOk(type)
  32. }
  33. }));
  34. // 回填
  35. useEffect(() => {
  36. if (value && Object.keys(value).length) {
  37. const data = {
  38. ...value, strategySettings: value?.strategySettings?.map(item => {
  39. const { sendTime, startTime, endTime, sendDay, timeRepeatType } = item
  40. if (timeRepeatType === 'TIME_TYPE_SINGLE_PLACE') {
  41. const data = {
  42. ...item,
  43. timeRepeatType,
  44. sendDay: sendDay ? dayjs(sendDay + ' ' + sendTime) : undefined
  45. }
  46. delete data?.sendTime
  47. return data
  48. }
  49. return {
  50. ...item,
  51. timeRepeatType,
  52. sendTime: sendTime ? dayjs('2025-04-25 ' + sendTime) : undefined,
  53. startTime: startTime ? dayjs(startTime) : undefined,
  54. endTime: endTime ? dayjs(endTime) : undefined,
  55. sendDay: sendDay ? dayjs(sendDay) : undefined
  56. }
  57. })
  58. }
  59. filedUpdateChange(data)
  60. form.setFieldsValue(data)
  61. }
  62. }, [value])
  63. const handleOk = (type: string) => {
  64. form.validateFields().then((values) => {
  65. const data = {
  66. ...values,
  67. strategySettings: values?.strategySettings?.map(item => {
  68. const { startTime, endTime, sendDay, sendTime, timeRepeatType, repeatArray, ...rest } = item
  69. const data = { ...rest, timeRepeatType }
  70. if (timeRepeatType === 'TIME_TYPE_SINGLE_PLACE') {
  71. // 定时发送
  72. data.sendDay = dayjs(sendDay).format('YYYY-MM-DD')
  73. data.sendTime = dayjs(sendDay).format('HH:mm:ss')
  74. } else if (timeRepeatType === 'TIME_TYPE_REPEAT_DAY') {
  75. // 每日循环
  76. data.startTime = dayjs(startTime).format('YYYY-MM-DD')
  77. if (endTime) {
  78. data.endTime = dayjs(endTime).format('YYYY-MM-DD')
  79. }
  80. data.sendTime = dayjs(sendTime).format('HH:mm:ss')
  81. } else if (timeRepeatType === 'TIME_TYPE_REPEAT_WEEK' || timeRepeatType === 'TIME_TYPE_REPEAT_MONTH') {
  82. // 每周循环、每月循环
  83. data.startTime = dayjs(startTime).format('YYYY-MM-DD')
  84. data.sendTime = dayjs(sendTime).format('HH:mm:ss')
  85. if (endTime) {
  86. data.endTime = dayjs(endTime).format('YYYY-MM-DD')
  87. }
  88. data.repeatArray = repeatArray
  89. }
  90. return data
  91. })
  92. }
  93. onChange?.(data, type)
  94. }).catch((err) => {
  95. console.log('err', err)
  96. form.submit()
  97. });
  98. };
  99. const filedUpdateChange = ({ groupSendName, strategySettings }: any) => {
  100. const isChecked = (content: NewStepsItem[]) => {
  101. return content.every(item => {
  102. if (item.children) {
  103. return isChecked(item.children)
  104. }
  105. return item.checked
  106. })
  107. }
  108. const content = strategySettings?.map((item, index) => {
  109. const { timeRepeatType, sendDay, startTime, sendTime, repeatArray } = item
  110. const sendTimeChecked =
  111. timeRepeatType === "TIME_TYPE_SINGLE_TIMELY" ||
  112. (timeRepeatType === "TIME_TYPE_SINGLE_PLACE" && sendDay) ||
  113. (timeRepeatType === "TIME_TYPE_REPEAT_DAY" && startTime && sendTime) ||
  114. ((timeRepeatType === "TIME_TYPE_REPEAT_WEEK" || timeRepeatType === "TIME_TYPE_REPEAT_MONTH") && startTime && sendTime && repeatArray)
  115. const children = [
  116. {
  117. title: `发送时间`, checked: sendTimeChecked, id: `strategy_${index}_strategyName`
  118. },
  119. ...item.sendData.map((i, n) => {
  120. return { title: `发送对象${n + 1}`, description: '对象筛选,人群包', id: `strategy_${index}_${n}_sendData`, checked: i?.externalUserType ? i?.externalUserType === 'all' ? true : i?.externalUserFilter && Object.values(i.externalUserFilter).some(item => item) : false }
  121. })
  122. ]
  123. return {
  124. title: `策略${index + 1}`,
  125. id: `strategy_${index}`,
  126. children: children,
  127. checked: isChecked(children)
  128. }
  129. })
  130. const isChecked1 = !!groupSendName
  131. const isChecked2 = isChecked(content)
  132. const stepsData = [
  133. { title: '群发配置', description: '群发标题', checked: isChecked1, id: 'basicInfo' },
  134. {
  135. title: '群发策略', checked: isChecked2, children: [...content, { title: `完成`, checked: isChecked2 }]
  136. },
  137. { title: '完成', checked: isChecked1 && isChecked2 }
  138. ]
  139. setStepsList(stepsData)
  140. }
  141. return <>
  142. <div className={`body_steps`}>
  143. <NewSteps
  144. items={stepsList}
  145. onChange={(e) => {
  146. if (e?.id)
  147. ref1.current?.querySelector('#' + e?.id)?.scrollIntoView({ behavior: 'smooth' })
  148. }}
  149. />
  150. </div>
  151. <div className={`body_content`} ref={ref1}>
  152. <Form
  153. form={form}
  154. name="gjnewMassSending"
  155. labelAlign='left'
  156. labelCol={{ span: 5 }}
  157. colon={false}
  158. scrollToFirstError={{
  159. behavior: 'smooth',
  160. block: 'center'
  161. }}
  162. onFinishFailed={({ errorFields }) => {
  163. message.error(errorFields?.[0]?.errors?.[0])
  164. }}
  165. initialValues={{
  166. taskType: 'novel',
  167. strategySettings: [{ sendData: [{ externalUserType: 'specify' }] }],
  168. }}
  169. onFieldsChange={() => {
  170. filedUpdateChange(form.getFieldsValue())
  171. }}
  172. preserve={true}
  173. >
  174. <Card title={<strong>基础信息配置</strong>} style={{ background: '#fff', marginBottom: 10 }} hoverable id='basicInfo'>
  175. <Form.Item label={<strong>高级群发标题</strong>} name="groupSendName" rules={[{ required: true, message: '请输入标题!' }]}>
  176. <Input placeholder="请输入标题" style={{ width: 358 }} allowClear />
  177. </Form.Item>
  178. </Card>
  179. <Form.List name="strategySettings">
  180. {(fields, { add, remove }) => (
  181. <>
  182. {fields.map(({ key, name, ...restField }, index) => {
  183. const timeRepeatType = strategySettings?.[index]?.timeRepeatType
  184. const sendData = strategySettings?.[index]?.sendData
  185. return <Card
  186. key={key}
  187. title={<strong>策略{index + 1}配置</strong>}
  188. style={{ background: '#fff', marginBottom: 10 }}
  189. hoverable
  190. id={`strategy_${index}`}
  191. extra={strategySettings?.length > 1 ? <Button icon={<DeleteOutlined />} type='link' style={{ color: 'red' }} onClick={() => remove(name)}></Button> : null}
  192. >
  193. <div className={style.strategy_item} id={`strategy_${index}_strategyName`}>
  194. <Form.Item
  195. {...restField}
  196. label={<strong>策略名称</strong>}
  197. name={[name, 'strategyName']}
  198. rules={[{ required: false, message: '请输入策略名称!' }]}
  199. >
  200. <Input placeholder="请输入标题" style={{ width: 358 }} allowClear />
  201. </Form.Item>
  202. <SendTimeSet active='all' form={form} restField={restField} name={name} timeRepeatType={timeRepeatType} />
  203. <Form.List name={[name, 'sendData']}>
  204. {(fields, { add, remove }) => (
  205. <>
  206. {fields.map(({ key, name, ...restField }, i) => {
  207. return <Card
  208. key={i}
  209. title={<strong>策略{index + 1} 发送对象{i + 1}</strong>}
  210. style={{ background: '#fff', marginBottom: 10 }}
  211. extra={sendData?.length > 1 ? <Button icon={<DeleteOutlined />} type='link' style={{ color: 'red' }} onClick={() => remove(name)}></Button> : null}
  212. id={`strategy_${index}_${i}_sendData`}
  213. >
  214. <Form.Item
  215. label={<strong>发送对象配置</strong>}
  216. required
  217. >
  218. <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
  219. <Form.Item
  220. {...restField}
  221. name={[name, 'externalUserType']}
  222. rules={[{ required: true, message: '请选择转移对象!' }]}
  223. noStyle
  224. >
  225. <Radio.Group options={[{ label: '全部', value: 'all' }, { label: '指定', value: 'specify' }]} />
  226. </Form.Item>
  227. {sendData?.[i]?.externalUserType === 'specify' && <div style={{ marginTop: 8, width: '100%' }}>
  228. <Form.Item
  229. {...restField}
  230. name={[name, 'externalUserFilter']}
  231. rules={[{ required: true, message: '请选择人群包!' }]}
  232. noStyle
  233. >
  234. <FilterUser configType={'GROUP_GROUP'} />
  235. </Form.Item>
  236. </div>}
  237. </div>
  238. </Form.Item>
  239. </Card>
  240. })}
  241. <Form.Item>
  242. <Button type="dashed" onClick={() => add({ externalUserType: 'specify' })} block icon={<PlusOutlined />}>
  243. 新增发送对象
  244. </Button>
  245. </Form.Item>
  246. </>
  247. )}
  248. </Form.List>
  249. </div>
  250. </Card>
  251. })}
  252. <Form.Item>
  253. <Button type="primary" onClick={() => add({ sendData: [{ externalUserType: 'specify' }] })} block icon={<PlusOutlined />}>
  254. 新增策略组
  255. </Button>
  256. </Form.Item>
  257. </>
  258. )}
  259. </Form.List>
  260. </Form>
  261. </div>
  262. </>
  263. });
  264. export default React.memo(Strategy);