generateTarget.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import { Button, Card, Form, InputNumber, Modal, Select, Space, message } from "antd"
  2. import React, { useEffect, useState } from "react"
  3. import '../../index.less'
  4. import { getRandomElements } from "@/utils/utils"
  5. import { REGION_DATA } from "./const"
  6. interface Props {
  7. target: any
  8. visible?: boolean,
  9. onClose?: () => void
  10. onChange?: (value: any) => void
  11. }
  12. interface Region {
  13. title: string;
  14. value: number;
  15. key: number;
  16. parentId?: number;
  17. disabled?: boolean;
  18. children?: Region[];
  19. }
  20. // 年龄前驱
  21. const START_AGE = Array(66 - 13).fill('').map((_, i) => i + 14).filter(i => i !== 15 && i !== 16 && i !== 17)
  22. // 年龄后驱
  23. const END_AGE = Array(66 - 17).fill('')
  24. /**
  25. * 一键生成
  26. * @param param0
  27. * @returns
  28. */
  29. const GenerateTarget: React.FC<Props> = ({ target, visible, onChange, onClose }) => {
  30. /*********************************/
  31. const [form] = Form.useForm();
  32. const startAgeMax = Form.useWatch('startAgeMax', form)
  33. const startAgeMin = Form.useWatch('startAgeMin', form)
  34. const defaultStartAge = Form.useWatch('defaultStartAge', form)
  35. const endAgeMax = Form.useWatch('endAgeMax', form)
  36. const endAgeMin = Form.useWatch('endAgeMin', form)
  37. const defaultEndAge = Form.useWatch('defaultEndAge', form)
  38. const [startAgeList, setStartAgeList] = useState<number[]>([])
  39. const [endAgeList, setEndAgeList] = useState<number[]>([])
  40. /*********************************/
  41. useEffect(() => {
  42. if (typeof startAgeMin === 'number' && typeof startAgeMax === 'number') {
  43. let startAgeList = Array(startAgeMin === 14 ? startAgeMax - startAgeMin + 1 : startAgeMax - startAgeMin + 1).fill('').map((_, index) => startAgeMin + index).filter(i => i !== 15 && i !== 16 && i !== 17 && i !== defaultStartAge)
  44. setStartAgeList(startAgeList)
  45. } else {
  46. setStartAgeList([])
  47. }
  48. }, [startAgeMin, startAgeMax, defaultStartAge])
  49. useEffect(() => {
  50. if (typeof endAgeMin === 'number' && typeof endAgeMax === 'number') {
  51. let endAgeList = Array(endAgeMax - endAgeMin + 1).fill('').map((_, index) => endAgeMin + index).filter(item => item !== defaultEndAge)
  52. setEndAgeList(endAgeList)
  53. } else {
  54. setEndAgeList([])
  55. }
  56. }, [endAgeMax, endAgeMin, defaultEndAge])
  57. const handleOk = (values: any) => {
  58. const zhongguo = [156, 540000, 630000, 510000, 450000, 320000, 220000, 370000, 340000, 150000, 140000, 420000, 130000, 360000, 310000, 330000, 650000, 350000, 120000, 110000, 640000, 530000, 210000, 610000, 520000, 230000, 460000, 440000, 500000, 410000, 620000, 430000]
  59. const locationTypes = ["LIVE_IN"]
  60. const { regions, count, defaultStartAge, defaultEndAge } = values
  61. let regionsData = REGION_DATA.find(item => item.value === regions)
  62. let children = regionsData?.children || []
  63. function getRegionPaths(region: Region, parentPath: string = ''): string[] {
  64. const currentPath = parentPath ? `${parentPath},${region.value}` : `${region.value}`;
  65. const paths = [];
  66. if (region?.children?.length) {
  67. for (const child of region.children) {
  68. paths.push(...getRegionPaths(child, currentPath));
  69. }
  70. } else {
  71. paths.push(currentPath)
  72. }
  73. return paths;
  74. }
  75. function getAllPaths(regions: Region[]): string[] {
  76. let allPaths: string[] = [];
  77. for (const region of regions) {
  78. allPaths = allPaths.concat(getRegionPaths(region));
  79. }
  80. return allPaths;
  81. }
  82. let regionsList: string[] = getAllPaths(children);
  83. if (regionsList.length === 0) {
  84. message.error('请联系管理员')
  85. return
  86. }
  87. const getTarget = (count: number, regionsList: string[]): any => {
  88. let dataRandom = getRandomElements(regionsList, count)
  89. return dataRandom.map(item => {
  90. let r = item.split(',')
  91. let rData = children.find(c => c.value.toString() === r[0])
  92. let rName = rData?.title
  93. let regionsL = children.map(item => item.value)
  94. if (r.length > 1) {
  95. let lChildren = rData?.children || []
  96. let lData = lChildren.find(item => item.value.toString() === r[1])
  97. let lName = lData?.title
  98. let lRegionsL = lChildren.map(item => item.value)
  99. console.log('target?.targeting?.regions', target?.targeting)
  100. return {
  101. ...target,
  102. targetingName: `${regionsData?.title}无${rName}${lName}+` + target.targetingName,
  103. targeting: {
  104. ...(target?.targeting || {}),
  105. geoLocation: {
  106. locationTypes: (target?.targeting?.geoLocation?.locationTypes || locationTypes),
  107. regions: [...((target?.targeting?.geoLocation?.regions || zhongguo) as number[]).filter(item => item !== regions), ...regionsL.filter(item => item.toString() !== r[0]), ...lRegionsL.filter(item => item.toString() !== r[1])]
  108. }
  109. }
  110. }
  111. } else {
  112. return {
  113. ...target,
  114. targetingName: `${regionsData?.title}无${rName}+` + target.targetingName,
  115. targeting: {
  116. ...(target?.targeting || {}),
  117. geoLocation: {
  118. locationTypes: (target?.targeting?.geoLocation?.locationTypes || locationTypes),
  119. regions: [...((target?.targeting?.geoLocation?.regions || zhongguo) as number[]).filter(item => item !== regions), ...regionsL.filter(item => item.toString() !== r[0])]
  120. }
  121. }
  122. }
  123. }
  124. })
  125. }
  126. let data: any[] = []
  127. if (target?.targeting?.geoLocation) {
  128. let oldregions: number[] = target.targeting.geoLocation.regions
  129. if (oldregions.includes(regions)) {
  130. data = data.concat(getTarget(count, regionsList))
  131. } else { // 不包含 regionsList
  132. message.error('当前地域已经排除了该省下的某个区或者县')
  133. return
  134. }
  135. } else {
  136. data = data.concat(getTarget(count, regionsList))
  137. }
  138. data = data.map(item => {
  139. const { targeting, id, createTime, updateTime, createBy, ...data } = item
  140. let age: { min: number, max: number }
  141. if (startAgeList.length || endAgeList.length) {
  142. if (Math.random() > 0.5 && startAgeList.length > 0) {
  143. age = { min: startAgeList.pop() as number, max: defaultEndAge }
  144. } else if (endAgeList.length > 0) {
  145. age = { min: defaultStartAge, max: endAgeList.pop() as number }
  146. } else {
  147. if (startAgeList.length > 0) {
  148. age = { min: startAgeList.pop() as number, max: defaultEndAge }
  149. } else {
  150. age = { min: defaultStartAge, max: endAgeList.pop() as number }
  151. }
  152. }
  153. data.targetingName = data.targetingName.replace('年龄', '')
  154. data.targetingName += `+年龄${age.min}岁至${age.max === 66 ? '66岁及以上' : age.max + '岁'}`
  155. let newTargeting = {
  156. ...targeting,
  157. age: [age]
  158. }
  159. return { ...data, targeting: newTargeting }
  160. }
  161. data.targetingName = '年龄不够数量分配,请删除'
  162. return { ...data, targeting }
  163. })
  164. onChange?.(data)
  165. }
  166. return <Modal
  167. title={<strong>一键生成定向配置</strong>}
  168. open={visible}
  169. className='modalResetCss'
  170. onCancel={onClose}
  171. bodyStyle={{ padding: '0 0 40px', position: 'relative', borderRadius: '0 0 8px 8px' }}
  172. footer={null}
  173. width={700}
  174. >
  175. <Form
  176. form={form}
  177. name="generateTarget"
  178. labelAlign='left'
  179. labelCol={{ span: 5 }}
  180. colon={false}
  181. style={{ backgroundColor: '#f1f4fc', maxHeight: 600, overflow: 'hidden', overflowY: 'auto', padding: '10px 10px 10px', borderRadius: '0 0 8px 8px' }}
  182. scrollToFirstError
  183. onFinishFailed={({ errorFields }) => {
  184. message.error(errorFields?.[0]?.errors?.[0])
  185. }}
  186. onFinish={handleOk}
  187. initialValues={{
  188. regions: 540000,
  189. defaultStartAge: 35,
  190. startAgeMin: 30,
  191. startAgeMax: 40,
  192. defaultEndAge: 66,
  193. endAgeMin: 50,
  194. endAgeMax: 66,
  195. count: 3
  196. }}
  197. >
  198. <Card
  199. title={<strong style={{ fontSize: 14 }}>生成设置</strong>}
  200. className="cardResetCss"
  201. >
  202. <Form.Item
  203. label={<strong>地域差异</strong>}
  204. name='regions'
  205. rules={[
  206. { required: true, message: '请选择' }
  207. ]}
  208. >
  209. <Select
  210. placeholder="请选择"
  211. showSearch
  212. filterOption={(input, option) =>
  213. ((option?.children ?? '') as any).toLowerCase().includes(input.toLowerCase())
  214. }
  215. >
  216. {REGION_DATA.map(item => <Select.Option value={item.value} key={item.key}>{item.title}</Select.Option>)}
  217. </Select>
  218. </Form.Item>
  219. <Form.Item
  220. label={<strong>年龄前段扩展区间</strong>}
  221. required
  222. help={<span style={{ color: 'red', fontSize: 12 }}>{`前段扩展的时候,后段固定年龄“${defaultEndAge === 66 ? '66岁及以上' : defaultEndAge + '岁'}”,可扩展${startAgeMin === 66 ? '66岁及以上' : `${startAgeMin}岁~${startAgeMax === 66 ? '66岁及以上' : startAgeMax + '岁'}`},可扩展数量${startAgeList.length}个`}</span>}
  223. >
  224. <Space>
  225. <Form.Item name={'defaultStartAge'} noStyle>
  226. <Select style={{ width: 138 }} placeholder="前段默认年龄">
  227. {START_AGE.map(i => {
  228. return <Select.Option disabled={i > defaultEndAge} value={i} key={i}>{i === 66 ? '66岁及以上' : i + ' 岁'}</Select.Option>
  229. })}
  230. </Select>
  231. </Form.Item>
  232. <Form.Item name={'startAgeMin'} noStyle>
  233. <Select style={{ width: 170 }} placeholder="请选择">
  234. {START_AGE.map(i => {
  235. return <Select.Option disabled={i > startAgeMax} value={i} key={i}>{i === 66 ? '66 岁及以上' : i + ' 岁'}</Select.Option>
  236. })}
  237. </Select>
  238. </Form.Item>
  239. <span>-</span>
  240. <Form.Item name={'startAgeMax'} noStyle>
  241. <Select style={{ width: 170 }} placeholder="请选择">
  242. {END_AGE.map((_, i) => {
  243. return <Select.Option disabled={(i + 18 < startAgeMin) || (i + 18 > defaultEndAge)} value={i + 18} key={i + 18}>{i + 18 === 66 ? '66 岁及以上' : i + 18 + ' 岁'}</Select.Option>
  244. })}
  245. </Select>
  246. </Form.Item>
  247. </Space>
  248. </Form.Item>
  249. <Form.Item
  250. label={<strong>年龄后段扩展区间</strong>}
  251. required
  252. help={<span style={{ color: 'red', fontSize: 12 }}>{`后段扩展的时候,前段固定年龄“${defaultStartAge === 66 ? '66岁及以上' : defaultStartAge + '岁'}”,可扩展${endAgeMin === 66 ? '66岁及以上' : `${endAgeMin}岁~${endAgeMax === 66 ? '66岁及以上' : endAgeMax + '岁'}`}, 可扩展数量${endAgeList.length}个`}</span>}
  253. >
  254. <Space>
  255. <Form.Item name={'defaultEndAge'} noStyle>
  256. <Select style={{ width: 138 }} placeholder="请选择">
  257. {END_AGE.map((_, i) => {
  258. return <Select.Option disabled={i + 18 < defaultStartAge} value={i + 18} key={i + 18}>{i + 18 === 66 ? '66 岁及以上' : i + 18 + ' 岁'}</Select.Option>
  259. })}
  260. </Select>
  261. </Form.Item>
  262. <Form.Item name={'endAgeMin'} noStyle>
  263. <Select style={{ width: 170 }} placeholder="请选择">
  264. {END_AGE.map((_, i) => {
  265. return <Select.Option disabled={(i > endAgeMax) || (i + 18 < defaultStartAge)} value={i + 18} key={i + 18}>{i + 18 === 66 ? '66 岁及以上' : i + 18 + ' 岁'}</Select.Option>
  266. })}
  267. </Select>
  268. </Form.Item>
  269. <span>-</span>
  270. <Form.Item name={'endAgeMax'} noStyle>
  271. <Select style={{ width: 170 }} placeholder="请选择">
  272. {END_AGE.map((_, i) => {
  273. return <Select.Option disabled={i + 18 < endAgeMin} value={i + 18} key={i + 18}>{i + 18 === 66 ? '66 岁及以上' : i + 18 + ' 岁'}</Select.Option>
  274. })}
  275. </Select>
  276. </Form.Item>
  277. </Space>
  278. </Form.Item>
  279. <Form.Item
  280. label={<strong>生成数量</strong>}
  281. name='count'
  282. rules={[
  283. { required: true, message: '请输入生成数量' }
  284. ]}
  285. >
  286. <InputNumber max={20} style={{ width: '100%' }} placeholder="请输入生成数量" />
  287. </Form.Item>
  288. </Card>
  289. <Form.Item className="submit_pull">
  290. <Space>
  291. <Button onClick={onClose}>取消</Button>
  292. <Button type="primary" htmlType="submit" className="modalResetCss">
  293. 确定
  294. </Button>
  295. </Space>
  296. </Form.Item>
  297. </Form>
  298. </Modal>
  299. }
  300. export default React.memo(GenerateTarget)