monitor1.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. import { Card, Input, Radio, Select, Space, Spin, Tag, TimePicker, Tooltip } from "antd";
  2. import React, { useCallback, useEffect, useState } from "react";
  3. import { ColumnHeightOutlined, ColumnWidthOutlined, EyeInvisibleOutlined, EyeOutlined, RedoOutlined } from "@ant-design/icons";
  4. import useEcharts from '@/Hook/useEcharts'
  5. import { columnsMonitor } from './tableMonitorConfig1'
  6. import { useModel } from 'umi'
  7. import { ListHourProps, ListType } from '@/services/adMonitor/adMonitor'
  8. import { qiliangpaihanghour } from './config1'
  9. import Details from './components/Details'
  10. import './table.less'
  11. import moment from "moment";
  12. import TableData from "@/pages/launchSystemNew/components/TableData";
  13. import FilterQuery from "./components/FilterQuery";
  14. import RuleAccountLog from "@/components/EarlyWarning/ruleAccountLog";
  15. interface newListType extends ListType {
  16. totalTimeUnit: 'total' | 'minute' | 'hour' | 'day',
  17. planTimeUnit: 'minute' | 'hour' | 'day'
  18. }
  19. /**
  20. * 今日起量监控
  21. * @param props
  22. * @returns
  23. */
  24. function Monitor(props: { onChange: () => void }) {
  25. const { onChange } = props
  26. const { getCostTrendList, getCostTopList, getListForHour, getMinuteList, getAdqAccountList } = useModel('useAdMonitor.useMonitor')
  27. const { getPicherList } = useModel('useOperating.useWxGroupList')
  28. // 变量开始
  29. const [queryForm, setQueryForm] = useState<newListType>({ totalTimeUnit: 'day', planTimeUnit: 'hour', sysUserId: JSON.parse(sessionStorage.getItem('SYSUSERID') || '[]'), pageNum: 1, pageSize: 20 }) // 搜索变量//startTime: moment().format('YYYY-MM-DD'), endTime: moment().format('YYYY-MM-DD'),
  30. const { BarMonitor, LineMonitor } = useEcharts()
  31. const [barDis, setBarDis] = useState<any[]>([])
  32. const [lineDis, setLineDis] = useState<any[]>([])
  33. const [lineTitle, setLineTitle] = useState<string>('广告总消耗趋势')
  34. const [px, setPx] = useState<boolean>(false)//设置顶部图形的排列方式
  35. const [visible, setVisible] = useState<boolean>(false) // 详情弹窗控制
  36. const [aId, setAId] = useState<any>()
  37. const [showEacharts, setShowEacharts] = useState<boolean>(true)
  38. const [queryForHour, setQueryForHour] = useState<ListHourProps>({ pageNum: 1, pageSize: 20 })
  39. const [filterQuery, setFilterQuery] = useState<any>({})
  40. const [logVisible, setLogVisible] = useState<boolean>(false)
  41. const [adgroupId, setAdgroupId] = useState<string>('')
  42. const [adgroupName, setAdgroupName] = useState<string>('')
  43. const [accountIdRule, setAccountIdRule] = useState<string>('')
  44. const { totalTimeUnit, planTimeUnit, adgroup, accountId, sysUserId } = queryForm
  45. const configName = '起量广告排行明细New'
  46. useEffect(() => {
  47. getList()
  48. }, [queryForHour, filterQuery, queryForm?.sysUserId, queryForm?.accountId, queryForm?.adgroup])
  49. const getList = () => {
  50. let message = localStorage.getItem(`myAdMonitorConfig1.0.0_${configName}`)
  51. if (message) {
  52. message = JSON.parse(message)
  53. }
  54. let isAccountId = true
  55. let isAdgroupId = true
  56. let isAdgroupName = true
  57. let columns: string[] = []
  58. if (message && Array.isArray(message)) {
  59. message.forEach((item: { serverIndex: any; dataIndex: string; }) => {
  60. if (!['event'].includes(item.dataIndex)) {
  61. if (item.dataIndex === 'account_id') {
  62. isAccountId = false
  63. } else if (item.dataIndex === 'adgroup_id') {
  64. isAdgroupId = false
  65. } else if (item.dataIndex === 'adgroup_name') {
  66. isAdgroupName = false
  67. }
  68. columns.push(item?.serverIndex || 'adgroup_data.' + item.dataIndex)
  69. }
  70. })
  71. } else {
  72. qiliangpaihanghour.forEach((item: any) => {
  73. item?.data?.forEach((d: { default: any, serverIndex: string, dataIndex: string }) => {
  74. if (d.default && !['event'].includes(d.dataIndex)) {
  75. if (d.dataIndex === 'account_id') {
  76. isAccountId = false
  77. } else if (d.dataIndex === 'adgroup_id') {
  78. isAdgroupId = false
  79. } else if (item.dataIndex === 'adgroup_name') {
  80. isAdgroupName = false
  81. }
  82. columns.push(d?.serverIndex || 'adgroup_data.' + d.dataIndex)
  83. }
  84. })
  85. })
  86. }
  87. if (isAccountId) {
  88. columns.push('adgroup_data.account_id')
  89. }
  90. if (isAdgroupId) {
  91. columns.push('adgroup_data.adgroup_id')
  92. }
  93. if (isAdgroupName) {
  94. columns.push('adgroup_data.adgroup_name')
  95. }
  96. let params = { ...queryForHour, ...filterQuery }
  97. if (queryForm?.sysUserId) {
  98. params.sysUserIds = queryForm?.sysUserId
  99. }
  100. if (queryForm?.accountId && queryForm?.accountId?.length > 0) {
  101. params.accountIdStr = queryForm?.accountId.toString()
  102. }
  103. if (queryForm?.adgroup) {
  104. params.adgroupIdStr = queryForm?.adgroup
  105. }
  106. params.columns = columns
  107. getListForHour.run(params)
  108. }
  109. // 获取投手
  110. useEffect(() => {
  111. !getPicherList.data && getPicherList.run()
  112. }, [])
  113. // 获取广告账号
  114. useEffect(() => {
  115. !getAdqAccountList.data && getAdqAccountList.run()
  116. }, [])
  117. // // 获取排行数据,柱图
  118. useEffect(() => {
  119. getPlanCostList()
  120. }, [totalTimeUnit, accountId, sysUserId])
  121. // 获取今日计划总消耗图谱,折线
  122. useEffect(() => {
  123. getTootalCostList()
  124. }, [planTimeUnit, adgroup, accountId, sysUserId])
  125. /** 获取折线图 */
  126. const getTootalCostList = useCallback(async () => {
  127. let { totalTimeUnit, planTimeUnit, pageNum, pageSize, adgroup, sysUserId, accountId, ...newQueryForm } = queryForm
  128. let params = adgroup ? { ...newQueryForm, adgroupId: adgroup } : newQueryForm
  129. let res = await getCostTrendList.run({ ...params, timeUnit: planTimeUnit, sysUserIds: sysUserId, accountId: accountId?.join() })
  130. let data: any[] = [{ legendName: '消耗' }]
  131. res?.data?.forEach((item: any) => {
  132. data[0][item.trendUnit] = item.costOfUnit
  133. })
  134. setLineDis(() => data)
  135. setLineTitle(() => res?.data?.adName ? res?.data?.adName + '_消耗趋势' : '广告总消耗趋势')
  136. }, [queryForm, lineDis])
  137. /** 获取柱状图 */
  138. const getPlanCostList = useCallback(async () => {
  139. let { totalTimeUnit, planTimeUnit, pageNum, pageSize, sysUserId, accountId, ...newQueryForm } = queryForm
  140. let { adgroup, ...planQueryFrom } = newQueryForm
  141. let res = await getCostTopList.run({ ...planQueryFrom, timeUnit: totalTimeUnit, sysUserIds: sysUserId, accountId: accountId?.join(), topN: 10 })
  142. let data = res?.data?.map((item: { adgroupId: number, cost: number, adgroupName: string, accountId: number }) => {
  143. return { name: item.adgroupId.toString(), value: item.cost, adName: item.adgroupName, accountId: item.accountId }
  144. })
  145. data = data?.sort((a: any, b: any) => {
  146. var value1 = a['value'];
  147. var value2 = b['value'];
  148. return value2 - value1;
  149. })
  150. setBarDis(() => data)
  151. }, [queryForm, barDis])
  152. // 详情
  153. const details = (data: any) => {
  154. setAId(data)
  155. setVisible(true)
  156. }
  157. // 计划详情
  158. const planDetail = (data: any) => {
  159. sessionStorage.setItem('ADIDORNAME', JSON.stringify(data))
  160. onChange && onChange()
  161. }
  162. //全部接口刷新
  163. const refresh = () => {
  164. getCostTrendList.refresh()
  165. getCostTopList.refresh()
  166. getListForHour.refresh()
  167. getMinuteList.refresh()
  168. }
  169. // 接口自动刷新10分钟一次
  170. useEffect(() => {
  171. let time = setInterval(() => {
  172. refresh()
  173. }, 1000 * 60 * 10)
  174. return () => {
  175. clearInterval(time)
  176. }
  177. }, [])
  178. //图形排列样式改变重新获取数据刷新图形
  179. const set = useCallback((b) => {
  180. setPx(b)
  181. getCostTrendList.refresh()
  182. getCostTopList.refresh()
  183. }, [getCostTopList, getCostTrendList])
  184. // 处理折线图数据
  185. const timePickerHandle = async (values: any, formatString: [string, string]) => {
  186. let res = await getCostTrendList.data
  187. let data: any[] = [{ legendName: '消耗' }]
  188. res?.data?.forEach((item: any) => {
  189. data[0][item.trendUnit] = item.costOfUnit
  190. })
  191. if (values) {
  192. let date = moment().format('YYYY-MM-DD')
  193. let strTime = formatString[0]
  194. let endTime = formatString[1]
  195. let dateTimeStr = `${date} ${strTime}:00`
  196. let dateTimeEnd = `${date} ${endTime}:00`
  197. let { legendName, ...otherData } = data[0]
  198. let newData: any = { legendName }
  199. for (const key in otherData) {
  200. if (Object.prototype.hasOwnProperty.call(otherData, key)) {
  201. const value = otherData[key];
  202. if (moment(dateTimeStr) <= moment(key) && moment(key) <= moment(dateTimeEnd)) {
  203. newData[key] = value
  204. }
  205. }
  206. }
  207. setLineDis(() => [newData])
  208. } else {
  209. setLineDis(() => data)
  210. }
  211. }
  212. const log = (value: any) => {
  213. setAccountIdRule(value.account_id)
  214. setAdgroupId(value.adgroup_id)
  215. setAdgroupName(value.adgroup_name)
  216. setLogVisible(true)
  217. }
  218. return <Space direction='vertical' style={{ width: '100%' }} className="monitor">
  219. <Card hoverable bodyStyle={{ padding: '12px 16px' }}>
  220. <div style={{ display: 'flex', justifyContent: 'space-between' }}>
  221. <Space>
  222. <Select
  223. showSearch
  224. value={queryForm.sysUserId}
  225. style={{ minWidth: 180, maxWidth: 250 }}
  226. mode='multiple'
  227. maxTagCount={1}
  228. allowClear
  229. placeholder="请选择投手"
  230. disabled={queryForm?.adgroup || queryForm?.accountId?.length > 0}
  231. onChange={(value: number[]) => {
  232. setQueryForm({ ...queryForm, sysUserId: value, pageNum: 1 })
  233. sessionStorage.setItem('SYSUSERID', value ? JSON.stringify(value) : '')
  234. }}
  235. filterOption={(input, option) =>
  236. (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
  237. }
  238. >
  239. {getPicherList?.data?.map((item: { nickname: string, userId: number }, index: number) =>
  240. <Select.Option
  241. value={item.userId}
  242. key={item.userId + '' + index}
  243. >
  244. {item.nickname}
  245. </Select.Option>
  246. )}
  247. </Select>
  248. <Select
  249. showSearch
  250. mode='multiple'
  251. maxTagCount={1}
  252. value={queryForm.accountId}
  253. style={{ minWidth: 220, maxWidth: 250 }}
  254. allowClear
  255. placeholder="请选择广告账号"
  256. onChange={(value: number[]) => {
  257. setQueryForm({ ...queryForm, accountId: value, pageNum: 1 })
  258. }}
  259. >
  260. {getAdqAccountList?.data?.data?.map((item: { id: number, accountId: number }) => <Select.Option
  261. value={item.accountId}
  262. key={item.id}
  263. >
  264. {item.accountId}
  265. </Select.Option>)}
  266. </Select>
  267. <Input
  268. value={queryForm.campaign}
  269. placeholder="计划ID"
  270. onChange={(e) => {
  271. let value = e.target.value
  272. setQueryForm({ ...queryForm, campaign: value })
  273. }}
  274. allowClear
  275. />
  276. <Input
  277. value={queryForm.adgroup}
  278. placeholder="广告ID"
  279. onChange={(e) => {
  280. setQueryForm({ ...queryForm, adgroup: e.target.value })
  281. }}
  282. allowClear
  283. />
  284. </Space>
  285. <Space>
  286. <Tag onClick={() => setShowEacharts(!showEacharts)}>{showEacharts ? <><EyeInvisibleOutlined /> 隐藏</> : <><EyeOutlined /> 显示</>}</Tag>
  287. <Tag color="#2db7f5" onClick={refresh}><RedoOutlined /> 刷新</Tag>
  288. </Space>
  289. </div>
  290. </Card>
  291. {showEacharts && <Card hoverable bodyStyle={{ padding: '12px 16px' }}>
  292. <span style={{ position: 'absolute', top: 10, zIndex: 10 }}>
  293. {px ?
  294. <Tooltip title='左右排列'><Tag color='#f50' onClick={() => { set(false) }}><ColumnWidthOutlined /><span style={{ fontSize: 10 }}>左右排列</span></Tag></Tooltip>
  295. :
  296. <Tooltip title='上下排列'><Tag color='#f50' onClick={() => { set(true) }} ><ColumnHeightOutlined /><span style={{ fontSize: 10 }}>上下排列</span></Tag></Tooltip>
  297. }
  298. </span>
  299. <div className={!px ? 'charts' : 'charts charts100'}>
  300. <div>
  301. <div className="selectTime">
  302. <Space>
  303. <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getCostTopList?.data?.reqTime}</span>
  304. <Radio.Group value={queryForm.totalTimeUnit} buttonStyle="solid" size='small' onChange={(e) => { setQueryForm({ ...queryForm, totalTimeUnit: e.target.value }) }}>
  305. <Radio.Button value="total">总</Radio.Button>
  306. <Radio.Button value="day">天</Radio.Button>
  307. <Radio.Button value="hour">小时</Radio.Button>
  308. <Radio.Button value="minute">5min</Radio.Button>
  309. </Radio.Group>
  310. </Space>
  311. </div>
  312. {getCostTopList?.loading ? <Spin /> : <BarMonitor style={{ width: '100%', height: '100%' }} data={barDis} xName="今日消耗" yName="广告名称" onChange={(value: string, accountId) => { console.log(value, accountId); setQueryForm({ ...queryForm, adgroup: value }) }} planID={queryForm?.adgroup} />}
  313. </div>
  314. <div>
  315. <div className="selectTime">
  316. <Space>
  317. <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getCostTrendList?.data?.reqTime}</span>
  318. <Radio.Group value={queryForm.planTimeUnit} buttonStyle="solid" size='small' onChange={(e) => { setQueryForm({ ...queryForm, planTimeUnit: e.target.value }) }}>
  319. <Radio.Button value="hour">小时</Radio.Button>
  320. <Radio.Button value="minute">5min</Radio.Button>
  321. </Radio.Group>
  322. {queryForm.planTimeUnit === 'minute' && <TimePicker.RangePicker
  323. size="small"
  324. format='HH:mm'
  325. minuteStep={5}
  326. onChange={timePickerHandle}
  327. />}
  328. </Space>
  329. </div>
  330. {getCostTrendList?.loading ? <Spin /> : <LineMonitor style={{ width: '100%', height: '100%' }} series smooth data={lineDis} title={lineTitle} />}
  331. </div>
  332. </div>
  333. </Card>}
  334. <div className={'MYtable'}>
  335. <TableData
  336. refreshData={getList}
  337. bodyStyle={{ padding: '12px 16px' }}
  338. gutter={[0, 12]}
  339. columns={columnsMonitor(planDetail, details, (value) => { setQueryForm({ ...queryForm, adgroup: value }); }, log)}
  340. dataSource={getListForHour?.data?.data?.records}
  341. loading={getListForHour?.loading}
  342. ajax={getListForHour}
  343. leftChild={
  344. <Space>
  345. <FilterQuery onChange={(data) => {
  346. setFilterQuery(data)
  347. }} />
  348. </Space>
  349. }
  350. myKey={'adgroup_id'}
  351. fixed={{ left: 0, right: 2 }}
  352. total={getListForHour?.data?.data?.total}
  353. onChange={(props: any) => {
  354. let { sortData, pagination } = props
  355. let { current, pageSize } = pagination
  356. let newQueryForm = JSON.parse(JSON.stringify(queryForHour))
  357. newQueryForm.pageNum = current
  358. newQueryForm.pageSize = pageSize
  359. if (sortData && JSON.stringify('sortData') !== '{}') {
  360. let { field, order } = sortData // descend 降序 大到小 ascend 升序 小到大
  361. if (order) {
  362. newQueryForm.sortColumn = field
  363. newQueryForm.sortAsc = order === 'ascend'
  364. } else {
  365. Object.keys(newQueryForm).forEach(key => {
  366. if (key === 'sortColumn' || key === 'sortAsc') {
  367. delete newQueryForm[key]
  368. }
  369. })
  370. }
  371. } else {
  372. Object.keys(newQueryForm).forEach(key => {
  373. if (key === 'sortField' || key === 'sort') {
  374. delete newQueryForm[key]
  375. }
  376. })
  377. }
  378. setQueryForHour({ ...newQueryForm })
  379. }}
  380. page={queryForHour.pageNum}
  381. pageSize={queryForHour.pageSize}
  382. scroll={{ y: 750 }}
  383. config={qiliangpaihanghour}
  384. configName={configName}
  385. />
  386. </div>
  387. {visible && <Details visible={visible} onClose={() => { setVisible(false) }} data={aId} />}
  388. {logVisible && <RuleAccountLog accountId={accountIdRule} adgroupName={adgroupName} adgroupId={adgroupId} visible={logVisible} onClose={() => setLogVisible(false)}/>}
  389. </Space>
  390. }
  391. export default React.memo(Monitor)