wjx 1 năm trước cách đây
mục cha
commit
cb3bf3abf4
43 tập tin đã thay đổi với 5563 bổ sung245 xóa
  1. 6 0
      config/routerConfig.ts
  2. 1 1
      src/assets/rocket.svg
  3. 49 10
      src/components/EarlyWarning/addEdit.tsx
  4. 46 6
      src/components/EarlyWarning/expandedColumns.tsx
  5. 91 24
      src/components/EarlyWarning/expandedRowTable.tsx
  6. 53 39
      src/components/EarlyWarning/index.tsx
  7. 6 3
      src/components/EarlyWarning/look.tsx
  8. 80 0
      src/components/EarlyWarning/ruleAccountLog.tsx
  9. 81 0
      src/components/EarlyWarning/ruleLog.tsx
  10. 2 2
      src/components/EarlyWarning/setEarlyWarning.tsx
  11. 77 0
      src/components/EarlyWarning/setEarlyWarnings.tsx
  12. 223 2
      src/components/EarlyWarning/tableConfig.tsx
  13. 17 0
      src/components/StatisticNull/index.tsx
  14. 6 4
      src/components/Tables/index.tsx
  15. 8 0
      src/global.less
  16. 17 2
      src/models/useAdMonitor/useMonitor.ts
  17. 190 0
      src/pages/adMonitor/adMonitorList/components/Details.tsx
  18. 245 0
      src/pages/adMonitor/adMonitorList/components/FilterQuery.tsx
  19. 175 0
      src/pages/adMonitor/adMonitorList/components/TabAd.tsx
  20. 1 1
      src/pages/adMonitor/adMonitorList/components/box.tsx
  21. 64 0
      src/pages/adMonitor/adMonitorList/components/filterQuery.less
  22. 26 0
      src/pages/adMonitor/adMonitorList/components/index.less
  23. 95 0
      src/pages/adMonitor/adMonitorList/components/tableConfigEw.tsx
  24. 153 0
      src/pages/adMonitor/adMonitorList/config1.ts
  25. 5 33
      src/pages/adMonitor/adMonitorList/index.tsx
  26. 414 0
      src/pages/adMonitor/adMonitorList/monitor1.tsx
  27. 1 1
      src/pages/adMonitor/adMonitorList/planList.tsx
  28. 1 1
      src/pages/adMonitor/adMonitorList/tableMonitorConfig.tsx
  29. 1067 0
      src/pages/adMonitor/adMonitorList/tableMonitorConfig1.tsx
  30. 489 0
      src/pages/launchSystemNew/adq/ad/FilterQuery.tsx
  31. 128 0
      src/pages/launchSystemNew/adq/ad/adExpandedRowRender.tsx
  32. 436 0
      src/pages/launchSystemNew/adq/ad/adPlanList.tsx
  33. 90 0
      src/pages/launchSystemNew/adq/ad/index.less
  34. 0 4
      src/pages/launchSystemNew/adq/ad/index.tsx
  35. 10 43
      src/pages/launchSystemNew/adq/ad/tableConfig.tsx
  36. 716 0
      src/pages/launchSystemNew/adq/ad/tablePlanListConfig.tsx
  37. 110 25
      src/pages/launchSystemNew/adq/config.ts
  38. 2 2
      src/pages/launchSystemNew/adq/log/index.tsx
  39. 1 10
      src/pages/launchSystemNew/adq/log/tableConfig.tsx
  40. 87 23
      src/pages/launchSystemNew/components/TableData/index.tsx
  41. 13 0
      src/pages/launchSystemNew/sysWarningRule/index.tsx
  42. 211 0
      src/services/adMonitor/adMonitor.ts
  43. 70 9
      src/services/adMonitor/earlyWarning.ts

+ 6 - 0
config/routerConfig.ts

@@ -106,6 +106,12 @@ const launchSystem = {
             component: './launchSystemNew/adq',
             access: 'adq',
         },
+        {
+            path: '/launchSystemNew/sysWarningRule',
+            name: '监控告警',
+            component: './launchSystemNew/sysWarningRule',
+            access: 'sysWarningRule',
+        },
         {
             path: '/launchSystemNew/launchManage',
             name: '广告投放',

+ 1 - 1
src/assets/rocket.svg

@@ -1 +1 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1640337291163" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24697" xmlns:xlink="http://www.w3.org/1999/xlink" width="26" height="26"><defs><style type="text/css"></style></defs><path d="M175.9 686z" p-id="24698"></path><path d="M694.9 412.6L483 535.7l66.4 72.7 148.5-192.9c0.6-1.1 0.2-2.4-0.8-3-0.7-0.3-1.6-0.3-2.2 0.1z" fill="#4887C7" p-id="24699"></path><path d="M463.9 574.6c0 27.3 22.1 49.4 49.4 49.4s49.4-22.1 49.4-49.4-22.1-49.4-49.4-49.4c-27.3-0.1-49.4 22.1-49.4 49.4z" fill="#4887C7" p-id="24700"></path><path d="M264.8 327.4l-69.9-69.9C369 83.1 651.3 82.3 826.3 255.7L756.8 326c-136.5-134.9-356.3-134.3-492 1.4z" fill="#EDB019" p-id="24701"></path><path d="M194.9 890.7C20 716.1 19.8 432.8 194.4 257.9l0.5-0.5 69.9 69.9c-136.2 136.2-136.2 357.1 0 493.3l-69.9 70.1z" fill="#81BE00" p-id="24702"></path><path d="M828.1 890.7l-69.9-69.9c136.2-136.2 136.2-357.1 0-493.3l-1.4-1.4 69.5-70.3c175.8 173.6 177.6 456.9 4 632.7-0.8 0.7-1.5 1.4-2.2 2.2z" fill="#D22422" p-id="24703"></path><path d="M500.5 258.7h22v37.2h-22v-37.2z m-156.2 47.8l19-11 18.6 32.2-19 11-18.6-32.2zM232.9 425.9l11-19 32.2 18.6-11 19-32.2-18.6z m-36.7 137.2h37.2v22h-37.2v-22z m36.7 159.1l32.2-18.6 11 19-32.2 18.6-11-19zM641 327.7l18.6-32.2 19 11-18.6 32.2-19-11z m105.9 97.8l32.2-18.6 11 19-32.2 18.6-11-19z m42.7 137.6h37.2v22h-37.2v-22z m-42.7 159.5l11-19 32.2 18.6-11 19-32.2-18.6z" fill="#4887C7" p-id="24704"></path></svg>
+<svg t="1640337291163" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24697" xmlns:xlink="http://www.w3.org/1999/xlink" width="1em" height="1em"><defs><style type="text/css"></style></defs><path d="M175.9 686z" p-id="24698"></path><path d="M694.9 412.6L483 535.7l66.4 72.7 148.5-192.9c0.6-1.1 0.2-2.4-0.8-3-0.7-0.3-1.6-0.3-2.2 0.1z" fill="#4887C7" p-id="24699"></path><path d="M463.9 574.6c0 27.3 22.1 49.4 49.4 49.4s49.4-22.1 49.4-49.4-22.1-49.4-49.4-49.4c-27.3-0.1-49.4 22.1-49.4 49.4z" fill="#4887C7" p-id="24700"></path><path d="M264.8 327.4l-69.9-69.9C369 83.1 651.3 82.3 826.3 255.7L756.8 326c-136.5-134.9-356.3-134.3-492 1.4z" fill="#EDB019" p-id="24701"></path><path d="M194.9 890.7C20 716.1 19.8 432.8 194.4 257.9l0.5-0.5 69.9 69.9c-136.2 136.2-136.2 357.1 0 493.3l-69.9 70.1z" fill="#81BE00" p-id="24702"></path><path d="M828.1 890.7l-69.9-69.9c136.2-136.2 136.2-357.1 0-493.3l-1.4-1.4 69.5-70.3c175.8 173.6 177.6 456.9 4 632.7-0.8 0.7-1.5 1.4-2.2 2.2z" fill="#D22422" p-id="24703"></path><path d="M500.5 258.7h22v37.2h-22v-37.2z m-156.2 47.8l19-11 18.6 32.2-19 11-18.6-32.2zM232.9 425.9l11-19 32.2 18.6-11 19-32.2-18.6z m-36.7 137.2h37.2v22h-37.2v-22z m36.7 159.1l32.2-18.6 11 19-32.2 18.6-11-19zM641 327.7l18.6-32.2 19 11-18.6 32.2-19-11z m105.9 97.8l32.2-18.6 11 19-32.2 18.6-11-19z m42.7 137.6h37.2v22h-37.2v-22z m-42.7 159.5l11-19 32.2 18.6-11 19-32.2-18.6z" fill="#4887C7" p-id="24704"></path></svg>

+ 49 - 10
src/components/EarlyWarning/addEdit.tsx

@@ -6,6 +6,7 @@ import React, { useEffect } from "react"
 import { MonitorFieldEnum, WarningTypeEnum } from "./config"
 import './index.less'
 import PriceInput from "./priceInput"
+import { OperationType } from "./ruleLog"
 
 
 interface Props {
@@ -35,25 +36,31 @@ const AddEdit: React.FC<Props> = (props) => {
         form.validateFields().then(values => {
             if (values?.rules) {
                 let newValues = JSON.parse(JSON.stringify(values))
-                let { rules, defaultRule, enable, notifyFrequency, ruleName, warningType } = newValues
+                let { rules, defaultRule, enable, notifyFrequency, ruleName, warningType, unTipWhenStop } = newValues
                 let params = rules?.map((item: any) => {
-                    const { field, ...newItem } = item
+                    const { field, operationType, operationValue, ...newItem } = item
+                    let ov = {}
+                    if (operationValue) {
+                        ov = { operationValue }
+                    }
                     return {
                         rule: Object.keys(newItem).map((item: any) => {
                             return {
                                 ...newItem[item],
                                 field: item
                             }
-                        })
+                        }),
+                        operationType,
+                        ...ov
                     }
                 })
                 if (initialValues?.id) {
-                    editSysWarningRule.run({ ruleId: initialValues.id, rules: params, notifyFrequency, ruleName, warningType, defaultRule: defaultRule ? 1 : 0, enable: enable ? 1 : 0 }).then(res => {
+                    editSysWarningRule.run({ ruleId: initialValues.id, rules: params, notifyFrequency, ruleName, warningType, defaultRule: defaultRule ? 1 : 0, enable: enable ? 1 : 0, unTipWhenStop }).then(res => {
                         message.success('修改成功')
                         onChange?.()
                     })
                 } else {
-                    addSysWarningRule.run({ rules: params, notifyFrequency, ruleName, warningType, defaultRule: defaultRule ? 1 : 0, enable: enable ? 1 : 0 }).then(res => {
+                    addSysWarningRule.run({ rules: params, notifyFrequency, ruleName, warningType, defaultRule: defaultRule ? 1 : 0, enable: enable ? 1 : 0, unTipWhenStop }).then(res => {
                         message.success('新增成功')
                         onChange?.()
                     })
@@ -78,16 +85,18 @@ const AddEdit: React.FC<Props> = (props) => {
             name="dynamic_form_nest_item"
             form={form}
             className="addEdit_form"
-            labelCol={{ span: 5 }}
+            labelCol={{ span: 6 }}
             colon={false}
             initialValues={initialValues || {
                 enable: true,
                 notifyFrequency: 5,
                 defaultRule: false,
                 rules: [{
-                    field: [],
-                }]
+                    field: []
+                }],
+                unTipWhenStop: true
             }}
+            labelAlign="left"
         >
             <div style={{ padding: '0 4px' }}>
                 <Form.Item name="ruleName" label={<strong>规则名称</strong>} rules={[{ required: true, message: '请输入规则名称' }]}>
@@ -115,9 +124,12 @@ const AddEdit: React.FC<Props> = (props) => {
                 <Form.Item name="defaultRule" label={<strong>是否默认规则</strong>} valuePropName="checked" rules={[{ required: true }]}>
                     <Switch />
                 </Form.Item>
+                <Form.Item name="unTipWhenStop" label={<strong>暂停/删除广告不通知</strong>} valuePropName="checked" rules={[{ required: true }]}>
+                    <Switch />
+                </Form.Item>
             </div>
             <Form.List name="rules">
-                {(fields, { add, remove }, { errors }) => <>
+                {(fields, { add, remove }) => <>
                     <>{fields.map(({ key, name, ...restField }) => {
                         return <div key={key} style={{ width: '100%' }} className="field">
                             <Space className="field_name">
@@ -165,11 +177,38 @@ const AddEdit: React.FC<Props> = (props) => {
                             >
                                 <PriceInput disabled={field === 'cost_trend_last_three_hour'} />
                             </Form.Item>)}
+                            <Form.Item
+                                {...restField}
+                                label={<strong>操作类型</strong>}
+                                name={[name, 'operationType']}
+                                rules={[{ required: true }]}
+                            >
+                                <Select
+                                    showSearch
+                                    allowClear
+                                    placeholder="选择操作类型"
+                                    filterOption={(input, option) =>
+                                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                                    }
+                                >
+                                    {OperationType.map((item, index) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                                </Select>
+                            </Form.Item>
+                            {[3, 4].includes(rules?.[key]?.['operationType']) && <Form.Item
+                                {...restField}
+                                label={<strong>操作值</strong>}
+                                name={[name, 'operationValue']}
+                                rules={[{ required: true }]}
+                            >
+                                <InputNumber style={{ width: '100%' }} min={0} placeholder="请输入操作值" />
+                            </Form.Item>}
                         </div>
                     })}</>
 
                     <Form.Item>
-                        <Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>新增条件</Button>
+                        <Button type="dashed" onClick={() => add({
+                            field: []
+                        })} block icon={<PlusOutlined />}>新增条件</Button>
                     </Form.Item>
                 </>}
             </Form.List>

+ 46 - 6
src/components/EarlyWarning/expandedColumns.tsx

@@ -4,28 +4,68 @@ import React from "react"
 
 function columns(del: (data: DelAdSysWarningRuleProps) => void) {
     const data: any = [
-        { title: '广告账号', dataIndex: 'accountId', key: 'accountId' },
-        { title: '广告名称', dataIndex: 'adgroupName', key: 'adgroupName', width: 350, ellipsis: true },
-        { title: '广告ID', dataIndex: 'adgroupId', key: 'adgroupId' },
-        { title: '创意ID', dataIndex: 'campaignId', key: 'campaignId' },
+        { title: '广告账号', dataIndex: 'accountId', key: 'accountId', width: 120 },
+        { title: '广告名称', dataIndex: 'adgroupName', key: 'adgroupName', ellipsis: true },
+        { title: '广告ID', dataIndex: 'adgroupId', key: 'adgroupId', width: 120 },
         {
             title: '操作',
             dataIndex: 'cz',
             key: 'cz',
+            width: 120,
             render: (a: string[], b: any) => {
                 return <Popconfirm
                     title="确定删除?"
                     onConfirm={() => del(b)}
-                    okText="是"
-                    cancelText="否"
                 >
                     <a style={{ color: 'red' }}>删除</a>
                 </Popconfirm>
             }
         },
     ]
+    return data
+}
 
+export function columnsAccount(del: (value: any) => void) {
+    const data: any = [
+        { title: '广告账号', dataIndex: 'accountId', key: 'accountId', width: 120 },
+        { title: '腾讯备注', dataIndex: 'memo', key: 'memo', ellipsis: true },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 120,
+            render: (a: string[], b: any) => {
+                return <Popconfirm
+                    title="确定删除?"
+                    onConfirm={() => del(b)}
+                >
+                    <a style={{ color: 'red' }}>删除</a>
+                </Popconfirm>
+            }
+        },
+    ]
+    return data
+}
 
+export function columnsBlack(del: (value: any) => void) {
+    const data: any = [
+        { title: '广告账号', dataIndex: 'accountId', key: 'accountId' },
+        { title: '广告ID', dataIndex: 'adgroupId', key: 'adgroupId' },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 120,
+            render: (a: string[], b: any) => {
+                return <Popconfirm
+                    title="确定删除?"
+                    onConfirm={() => del(b)}
+                >
+                    <a style={{ color: 'red' }}>删除</a>
+                </Popconfirm>
+            }
+        },
+    ]
     return data
 }
 

+ 91 - 24
src/components/EarlyWarning/expandedRowTable.tsx

@@ -1,46 +1,113 @@
 import { useAjax } from "@/Hook/useAjax"
-import { delAdSysWarningRuleApi, DelAdSysWarningRuleProps, getSysWarningRuleApi } from "@/services/adMonitor/earlyWarning"
+import { delAdSysWarningRuleApi, delAdSysWarningRuleBlackApi, DelAdSysWarningRuleProps, getSysWarningRuleApi, removeAccountApi } from "@/services/adMonitor/earlyWarning"
 import { SyncOutlined } from "@ant-design/icons"
-import { message, Table } from "antd"
-import React, { useEffect } from "react"
-import columns from "./expandedColumns"
+import { Drawer, message, Space, Table } from "antd"
+import React, { useEffect, useState } from "react"
+import columns, { columnsAccount, columnsBlack } from "./expandedColumns"
 
-
-const ExpandedRowTable: React.FC<{ data: any }> = ({ data }) => {
+interface Props {
+    data: any,
+    onClose?: () => void
+    onChange?: () => void
+    visible?: boolean
+}
+const ExpandedRowTable: React.FC<Props> = ({ data, visible, onChange, onClose }) => {
 
     /*****************************/
+    const [adgroupList, setAdgroupList] = useState<any[]>([])
+    const [accountList, setAccountList] = useState<any[]>([])
+    const [blackAdgroupList, setBlackAdgroupList] = useState<any[]>([])
     const getSysWarningRule = useAjax((params) => getSysWarningRuleApi(params), { formatResult: true })
     const delAdSysWarningRule = useAjax((params) => delAdSysWarningRuleApi(params))
+    const delAdSysWarningRuleBlack = useAjax((params) => delAdSysWarningRuleBlackApi(params))
+    const removeAccount = useAjax((params) => removeAccountApi(params))
     /*****************************/
 
     useEffect(() => {
-        console.log(data);
         if (data?.id) {
-            getSysWarningRule.run(data.id)
+            getList()
         }
     }, [])
 
+    const getList = () => {
+        getSysWarningRule.run(data.id).then(res => {
+            console.log(res)
+            if (res?.data) {
+                setAdgroupList(res?.data?.adgroupList)
+                setAccountList(res?.data?.accountList)
+                setBlackAdgroupList(res?.data?.blackAdgroupList)
+            }
+        })
+    }
+
+
+    const delAdgroup = (value: DelAdSysWarningRuleProps) => {
+        let { accountId, adgroupId } = value
+        delAdSysWarningRule.run({ accountId, adgroupId, ruleId: data.id }).then(res => {
+            message.success('删除成功')
+            getList()
+        })
+    }
+
+    const delAccount = (value: any) => {
+        let { accountId } = value
+        removeAccount.run({ accountId, ruleId: data.id }).then(res => {
+            message.success('删除成功')
+            getList()
+        })
+    }
 
-    const del = (data: DelAdSysWarningRuleProps) => {
-        let { accountId, adgroupId, campaignId } = data
-        delAdSysWarningRule.run([{ accountId, adgroupId, campaignId }]).then(res => {
+    const delBlack = (value: DelAdSysWarningRuleProps) => {
+        let { accountId, adgroupId } = value
+        delAdSysWarningRuleBlack.run({ accountId, adgroupId, ruleId: data.id }).then(res => {
             message.success('删除成功')
-            getSysWarningRule.refresh()
+            getList()
         })
     }
 
-    return <Table
-        size="small"
-        bordered
-        columns={columns(del)}
-        loading={getSysWarningRule.loading}
-        dataSource={getSysWarningRule?.data?.data}
-        title={() => <div style={{ textAlign: 'center', color: '#1890ff', fontWeight: 700, position: 'relative' }}>
-            <span>当前规则配置下的广告</span>
-            <a style={{ position: 'absolute', left: 0 }} onClick={() => getSysWarningRule.refresh()}><SyncOutlined /></a>
-        </div>}
-        pagination={false}
-    />;
+    return <Drawer
+        title="详情"
+        visible={visible}
+        onClose={onClose}
+        width={'70%'}
+        extra={<a onClick={() => getList()}><SyncOutlined />刷新</a>}
+    >
+        <Space style={{ width: '100%' }} direction="vertical">
+            <Table
+                size="small"
+                bordered
+                columns={columns(delAdgroup)}
+                loading={getSysWarningRule.loading}
+                dataSource={adgroupList}
+                rowKey={'adgroupId'}
+                title={() => <div style={{ fontWeight: 700, color: '#1890ff' }}>
+                    <span>广告列表</span>
+                </div>}
+            />
+            <Table
+                size="small"
+                bordered
+                columns={columnsAccount(delAccount)}
+                loading={getSysWarningRule.loading}
+                dataSource={accountList}
+                rowKey={'accountId'}
+                title={() => <div style={{ fontWeight: 700, color: '#1890ff' }}>
+                    <span>广告账号列表</span>
+                </div>}
+            />
+            <Table
+                size="small"
+                bordered
+                columns={columnsBlack(delBlack)}
+                loading={getSysWarningRule.loading}
+                dataSource={blackAdgroupList}
+                rowKey={'adgroupId'}
+                title={() => <div style={{ fontWeight: 700, color: '#1890ff' }}>
+                    <span>广告黑名单列表</span>
+                </div>}
+            />
+        </Space>
+    </Drawer>;
 }
 
 export default React.memo(ExpandedRowTable)

+ 53 - 39
src/components/EarlyWarning/index.tsx

@@ -1,11 +1,12 @@
 import { useAjax } from "@/Hook/useAjax"
 import TableData from "@/pages/launchSystemNew/components/TableData"
 import { delSysWarningRuleApi, getSysWarningRuleListApi, RuleProps } from "@/services/adMonitor/earlyWarning"
-import { Button, Drawer, Input, message, Space } from "antd"
+import { Button, Card, Input, message, Space } from "antd"
 import React, { useEffect, useState } from "react"
 import AddEdit from "./addEdit"
 import ExpandedRowTable from "./expandedRowTable"
 import tableConfig from "./tableConfig"
+import RuleLog from "./ruleLog"
 
 
 
@@ -16,11 +17,17 @@ import tableConfig from "./tableConfig"
 const EarlyWarning: React.FC = () => {
 
     /**************************/
-    const [visible, setVisible] = useState<boolean>(false)
     const [addVisible, setAddVisible] = useState<boolean>(false)
     const [queryParams, setQueryParams] = useState<{ ruleName?: string }>({})
     const [editData, setEditData] = useState<{ initialValues?: any }>({})
 
+    const [logVisible, setLogVisible] = useState<boolean>(false)
+    const [ruleId, setrRleId] = useState<number>(0)
+    const [ruleName, setrRuleName] = useState<string>('')
+
+    const [detailsVisible, setDetailsVisible] = useState<boolean>(false)
+    const [detailsData, setDetailsData] = useState<any>({})
+
     const getSysWarningRuleList = useAjax((params) => getSysWarningRuleListApi(params), { formatResult: true })
     const delSysWarningRule = useAjax((params) => delSysWarningRuleApi(params))
     /**************************/
@@ -39,19 +46,22 @@ const EarlyWarning: React.FC = () => {
         let newData = JSON.parse(JSON.stringify(data))
         newData.defaultRule = newData.defaultRule ? true : false
         newData.enable = newData.enable ? true : false
-        newData.rules = newData.rules.map((item: { rule: RuleProps[] }) => {
+        newData.rules = newData.rules.map((item: { rule: RuleProps[], operationType: number, operationValue: number }) => {
             let field: string[] = []
             let data: any = {}
-            item.rule.forEach(item => {
-                let { field: oldField, ...newItem } = item
+            item.rule.forEach(i => {
+                let { field: oldField, ...newItem } = i
                 field.push(oldField)
                 data[oldField] = { ...newItem }
             })
             return {
                 ...data,
-                field
+                field,
+                operationType: item.operationType,
+                operationValue: item?.operationValue
             }
         })
+        console.log(newData)
 
         setEditData({ initialValues: newData })
         setAddVisible(true)
@@ -64,42 +74,46 @@ const EarlyWarning: React.FC = () => {
         })
     }
 
-    return <>
-        <Button type="primary" onClick={() => { setVisible(true); setEditData({}) }}>监控预警</Button>
-
-        {/* 预警列表 */}
-        {visible && <Drawer
-            title="监控预警"
-            placement="right"
-            onClose={() => setVisible(false)}
-            visible={visible}
-            width='800px'
-        >
-            <Space direction="vertical">
-                <Space>
-                    <Input placeholder="请输入规则名字" value={queryParams?.ruleName} onChange={(e) => setQueryParams({ ...queryParams, ruleName: e.target.value })} />
-                    <Button type="primary" onClick={getList}>搜索</Button>
-                    <Button type="primary" onClick={() => setAddVisible(true)}>设置预警</Button>
-                </Space>
-
-                <TableData
-                    size="small"
-                    isCard={false}
-                    columns={() => tableConfig(edit, del)}
-                    ajax={getSysWarningRuleList}
-                    dataSource={getSysWarningRuleList?.data?.data}
-                    loading={getSysWarningRuleList?.loading}
-                    total={getSysWarningRuleList?.data?.data?.length}
-                    gutter={[0, 10]}
-                    leftChild={<Space direction='vertical'></Space>}
-                    expandedRowRender={(data) => <ExpandedRowTable data={data}/>}
-                />
-            </Space>
-        </Drawer>}
+    const log = (value: any) => {
+        setrRleId(value.id)
+        setrRuleName(value.ruleName)
+        setLogVisible(true)
+    }
+
+    const details = (value: any) => {
+        setDetailsData(value)
+        setDetailsVisible(true)
+    }
+
+    return <Card
+        title={<div style={{ color: 'red', fontWeight: 600 }}>
+            {`广告告警规则可以配置为默认规则或者给指定广告账号、广告配置。告警的优先级为 广告 > 广告账号 > 默认规则(只会触发同一级别的告警规则,如果在广告上配置了告警规则,那么广告账号的告警规则就不会触发)。`}
+        </div>}
+    >
+
+        <TableData
+            size="small"
+            isCard={false}
+            columns={() => tableConfig(edit, del, log, details)}
+            ajax={getSysWarningRuleList}
+            dataSource={getSysWarningRuleList?.data?.data}
+            loading={getSysWarningRuleList?.loading}
+            total={getSysWarningRuleList?.data?.data?.length}
+            gutter={[0, 10]}
+            leftChild={<Space>
+                <Input placeholder="请输入规则名字" value={queryParams?.ruleName} onChange={(e) => setQueryParams({ ...queryParams, ruleName: e.target.value })} />
+                <Button type="primary" onClick={getList}>搜索</Button>
+                <Button type="primary" onClick={() => setAddVisible(true)}>新增告警规则</Button>
+            </Space>}
+        />
 
         {/* 设置预警 */}
         {addVisible && <AddEdit visible={addVisible} onChange={() => { setAddVisible(false); getSysWarningRuleList.refresh(); setEditData({}) }} onClose={() => { setAddVisible(false); setEditData({}) }} {...editData} />}
-    </>
+
+        {logVisible && <RuleLog ruleName={ruleName} ruleId={ruleId} visible={logVisible} onClose={() => setLogVisible(false)} />}
+
+        {detailsVisible && <ExpandedRowTable data={detailsData} onClose={() => setDetailsVisible(false)} visible={detailsVisible} onChange={() => { }} />}
+    </Card>
 }
 
 export default React.memo(EarlyWarning)

+ 6 - 3
src/components/EarlyWarning/look.tsx

@@ -3,13 +3,16 @@ import { EyeOutlined } from "@ant-design/icons"
 import { Tooltip } from "antd"
 import React from "react"
 import { MonitorFieldEnum } from "./config"
+import { OperationTypeObj } from "./ruleLog"
 
 /**
  * 查看规则
  */
 interface Props {
     rules: {
-        rule: RuleProps[]
+        rule: RuleProps[],
+        operationType: number,
+        operationValue?: number
     }[]
 }
 const Look: React.FC<Props> = ({ rules }) => {
@@ -19,8 +22,8 @@ const Look: React.FC<Props> = ({ rules }) => {
         title={<span
             style={{ color: '#000' }}
             dangerouslySetInnerHTML={{
-                __html: rules.map((item, index) => `<span style="color: red; ">(</span> ${item.rule.map(rule => `${MonitorFieldEnum[rule.field]}${rule.condition}${rule.value}`)
-                    .join(' <span style="color: #1890ff; ">并且</span> ')} <span style="color: red; ">)</span>`)
+                __html: rules.map(item => `<span style="color: red; ">(</span> ${item.rule.map(rule => `${MonitorFieldEnum[rule.field]}${rule.condition}${rule.value}`)
+                    .join(' <span style="color: #1890ff; ">并且</span> ')},满足条件后:${OperationTypeObj[item.operationType]},${item?.operationValue ? '操作值:' + item?.operationValue : ''} <span style="color: red; ">)</span>`)
                     .join(` <span style="color: red; font-weight: 700; font-size: 16px ">或者</span> `)
             }}
         ></span>}

+ 80 - 0
src/components/EarlyWarning/ruleAccountLog.tsx

@@ -0,0 +1,80 @@
+import { useAjax } from "@/Hook/useAjax"
+import TableData from "@/pages/launchSystemNew/components/TableData"
+import { GetAdWarningLogAdProps, getAdWarningLogAdListApi } from "@/services/adMonitor/earlyWarning"
+import { Drawer, Input, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import { ruleAdLogTableConfig } from "./tableConfig"
+
+const OperationType = [
+    { label: '告警', value: 0 },
+    { label: '暂停广告', value: 1 },
+    { label: '启动广告', value: 2 },
+    { label: '增加预算', value: 3 },
+    { label: '减少预算', value: 4 },
+    { label: '广告置顶标黄', value: 5 },
+    { label: '广告置顶标红', value: 6 }
+]
+
+interface Props {
+    accountId: string
+    adgroupName: string,
+    adgroupId: string,
+    onClose?: () => void
+    visible?: boolean
+}
+/**
+ * 通过规则获取日志
+ * @returns 
+ */
+const RuleAccountLog: React.FC<Props> = ({ accountId, adgroupName, adgroupId, onClose, visible }) => {
+
+    /****************************/
+    const [queryForm, setQueryForm] = useState<GetAdWarningLogAdProps>({ pageNum: 1, pageSize: 20, adgroupId, accountId })
+    const getAdWarningLogAdList = useAjax((params) => getAdWarningLogAdListApi(params))
+    /****************************/
+
+    useEffect(() => {
+        getAdWarningLogAdList.run(queryForm)
+    }, [queryForm])
+
+    return <Drawer title={`${adgroupName} 操作日志`} placement="right" width={'70%'} onClose={onClose} visible={visible}>
+        <TableData
+            size="small"
+            isCard={false}
+            columns={() => ruleAdLogTableConfig()}
+            ajax={getAdWarningLogAdList}
+            dataSource={getAdWarningLogAdList?.data?.records}
+            loading={getAdWarningLogAdList?.loading}
+            total={getAdWarningLogAdList?.data?.total}
+            page={queryForm.pageNum}
+            pageSize={queryForm.pageSize}
+            onChange={({ pagination }: any) => {
+                let { current, pageSize } = pagination
+                let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                newQueryForm.pageNum = current
+                newQueryForm.pageSize = pageSize
+                setQueryForm(newQueryForm)
+            }}
+            gutter={[0, 10]}
+            scroll={{ x: 1200 }}
+            leftChild={<Space>
+                <Input placeholder="请输入规则ID" allowClear value={queryForm.ruleId} onChange={(e) => setQueryForm({ ...queryForm, ruleId: e.target.value, pageNum: 1 })} />
+                <Select
+                    style={{ width: 150 }}
+                    showSearch
+                    allowClear
+                    placeholder="选择操作类型"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    value={queryForm.operationType}
+                    onChange={(e) => setQueryForm({ ...queryForm, operationType: e, pageNum: 1 })}
+                >
+                    {OperationType.map((item, index) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                </Select>
+            </Space>}
+        />
+    </Drawer>
+}
+
+export default React.memo(RuleAccountLog)

+ 81 - 0
src/components/EarlyWarning/ruleLog.tsx

@@ -0,0 +1,81 @@
+import { useAjax } from "@/Hook/useAjax"
+import TableData from "@/pages/launchSystemNew/components/TableData"
+import { GetAdWarningLogRuleProps, getAdWarningLogRuleListApi } from "@/services/adMonitor/earlyWarning"
+import { Drawer, Input, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import { ruleLogTableConfig } from "./tableConfig"
+
+export const OperationType = [
+    { label: '告警', value: 0 },
+    { label: '暂停广告', value: 1 },
+    { label: '启动广告', value: 2 },
+    { label: '增加预算', value: 3 },
+    { label: '减少预算', value: 4 },
+    { label: '广告置顶标黄', value: 5 },
+    { label: '广告置顶标红', value: 6 }
+]
+export const OperationTypeObj = { 0: '告警', 1: '暂停广告', 2: '启动广告', 3: '增加预算', 4: '减少预算', 5: '广告置顶标黄', 6: '广告置顶标红' }
+
+interface Props {
+    ruleName: string,
+    ruleId: number,
+    onClose?: () => void
+    visible?: boolean
+}
+/**
+ * 通过规则获取日志
+ * @returns 
+ */
+const RuleLog: React.FC<Props> = ({ ruleName, ruleId, onClose, visible }) => {
+
+    /****************************/
+    const [queryForm, setQueryForm] = useState<GetAdWarningLogRuleProps>({ pageNum: 1, pageSize: 20, ruleId })
+    const getAdWarningLogRuleList = useAjax((params) => getAdWarningLogRuleListApi(params))
+    /****************************/
+
+    useEffect(() => {
+        getAdWarningLogRuleList.run(queryForm)
+    }, [queryForm])
+
+    return <Drawer title={`${ruleName} 操作日志`} placement="right" width={'70%'} onClose={onClose} visible={visible}>
+        <TableData
+            size="small"
+            isCard={false}
+            columns={() => ruleLogTableConfig()}
+            ajax={getAdWarningLogRuleList}
+            dataSource={getAdWarningLogRuleList?.data?.records}
+            loading={getAdWarningLogRuleList?.loading}
+            total={getAdWarningLogRuleList?.data?.total}
+            page={queryForm.pageNum}
+            pageSize={queryForm.pageSize}
+            onChange={({ pagination }: any) => {
+                let { current, pageSize } = pagination
+                let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                newQueryForm.pageNum = current
+                newQueryForm.pageSize = pageSize
+                setQueryForm(newQueryForm)
+            }}
+            gutter={[0, 10]}
+            scroll={{ x: 1200 }}
+            leftChild={<Space>
+                <Input placeholder="请输入广告账号" allowClear value={queryForm.accountId} onChange={(e) => setQueryForm({ ...queryForm, accountId: e.target.value, pageNum: 1 })} />
+                <Input placeholder="请输入广告ID" allowClear value={queryForm.adgroupId} onChange={(e) => setQueryForm({ ...queryForm, adgroupId: e.target.value, pageNum: 1 })} />
+                <Select
+                    style={{ width: 150 }}
+                    showSearch
+                    allowClear
+                    placeholder="选择操作类型"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    value={queryForm.operationType}
+                    onChange={(e) => setQueryForm({ ...queryForm, operationType: e, pageNum: 1 })}
+                >
+                    {OperationType.map((item, index) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                </Select>
+            </Space>}
+        />
+    </Drawer>
+}
+
+export default React.memo(RuleLog)

+ 2 - 2
src/components/EarlyWarning/setEarlyWarning.tsx

@@ -23,12 +23,12 @@ const SetEarlyWarning: React.FC<Props> = (props) => {
     const openModal = () => {
         setVisible(true)
         getSysWarningRuleList.run()
-        form.setFieldsValue({ adgroupId: selectedRows?.map(item => item.adgroupId).toString() })
+        form.setFieldsValue({ adgroupId: selectedRows?.map(item => item.adgroup_id).toString() })
     }
 
     const handleOk = () => {
         form.validateFields().then(values => {
-            let params = selectedRows?.map(item => ({ adgroupId: item.adgroupId, accountId: item.accountId, campaignId: item.campaignId }))
+            let params = selectedRows?.map(item => ({ adgroupId: item.adgroup_id, accountId: item.account_id, campaignId: item.campaign_id }))
             console.log('values--->', params);
             modifySysWarningRule.run({ addAds: params, ruleId: values.ruleId }).then(res => {
                 message.success('设置成功')

+ 77 - 0
src/components/EarlyWarning/setEarlyWarnings.tsx

@@ -0,0 +1,77 @@
+import { useAjax } from "@/Hook/useAjax"
+import { configAdWarningRuleApi } from "@/services/adMonitor/adMonitor"
+import { getSysWarningRuleListApi } from "@/services/adMonitor/earlyWarning"
+import { AlertOutlined } from "@ant-design/icons"
+import { Button, Form, message, Modal, Select } from "antd"
+import React, { useState } from "react"
+
+
+
+interface Props {
+    accountId: any
+    adgroupId: any
+    onChange?: () => void
+}
+const SetEarlyWarnings: React.FC<Props> = (props) => {
+
+    /******************************/
+    const { accountId, adgroupId, onChange } = props
+    const [visible, setVisible] = useState<boolean>(false)
+    const [form] = Form.useForm();
+    const getSysWarningRuleList = useAjax((params) => getSysWarningRuleListApi(params))
+    const configAdWarningRule = useAjax((params) => configAdWarningRuleApi(params), { formatResult: true })
+    /******************************/
+
+    const openModal = () => {
+        setVisible(true)
+        getSysWarningRuleList.run()
+        form.setFieldsValue({ adgroupId })
+    }
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            configAdWarningRule.run({ accountId, adgroupId, ruleIds: values.ruleIds?.toString() }).then(res => {
+                message.success('设置成功')
+                onChange?.()
+                setVisible(false)
+            })
+        })
+    }
+
+    return <>
+        <Button icon={<AlertOutlined />} type="link" onClick={openModal}>设置预警</Button>
+        {visible && <Modal
+            visible={visible}
+            title="预警设置"
+            onOk={handleOk}
+            confirmLoading={configAdWarningRule.loading}
+            onCancel={() => setVisible(false)}
+        >
+            <Form
+                form={form}
+                labelCol={{ span: 5 }}
+                colon={false}
+                initialValues={{
+
+                }}
+            >
+                <Form.Item name="ruleIds" label={<strong>选择预警规则</strong>} rules={[{ required: true, message: '请选择预警规则' }]}>
+                    <Select
+                        showSearch
+                        allowClear
+                        mode='multiple'
+                        placeholder="选择预警规则"
+                        filterOption={(input, option) =>
+                            ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                        }
+                    >
+                        {getSysWarningRuleList?.data?.filter((item: any) => !item?.defaultRule)?.map((item: any) => <Select.Option value={item.id} key={item.id}>{item.ruleName}</Select.Option>)}
+                    </Select>
+                </Form.Item>
+            </Form>
+        </Modal>}
+    </>
+}
+
+
+export default React.memo(SetEarlyWarnings)

+ 223 - 2
src/components/EarlyWarning/tableConfig.tsx

@@ -2,7 +2,9 @@ import { Badge, Popconfirm, Space, Tag } from 'antd'
 import React from 'react'
 import { WarningTypeEnum } from './config'
 import Look from './look'
-function tableConfig(edit: (data: any) => void, del: (id: number) => void): any {
+import { OperationTypeObj } from './ruleLog'
+import { copy } from '@/utils/utils'
+function tableConfig(edit: (data: any) => void, del: (id: number) => void, log: (value: any) => void, details: (value: any) => void): any {
     return [
         {
             title: 'ID',
@@ -45,7 +47,7 @@ function tableConfig(edit: (data: any) => void, del: (id: number) => void): any
             align: 'center',
             width: 80,
             render: (a: 0 | 1, b: any) => {
-                return <Badge status={{ '0': 'error', '1': 'success' }[a] as any} text={{ '0': '否', '1': '是' }[a]} />
+                return <Badge status={a ? 'success' : 'error'} text={a ? '是' : '否'} />
             }
         },
         {
@@ -77,6 +79,8 @@ function tableConfig(edit: (data: any) => void, del: (id: number) => void): any
             render: (a: 0 | 1, b: any) => {
                 return <Space>
                     <a onClick={() => edit(b)}>修改</a>
+                    <a onClick={() => log(b)}>日志</a>
+                    <a onClick={() => details(b)}>详情</a>
                     <Popconfirm
                         title="确定删除"
                         onConfirm={() => del(b?.id)}
@@ -90,4 +94,221 @@ function tableConfig(edit: (data: any) => void, del: (id: number) => void): any
         },
     ]
 }
+
+
+export function ruleLogTableConfig(): any {
+
+    return [
+        {
+            title: '广告账号',
+            dataIndex: 'accountId',
+            key: 'accountId',
+            align: 'center',
+            width: 80,
+        },
+        {
+            title: '广告名字',
+            dataIndex: 'adgroupName',
+            key: 'adgroupName',
+            width: 140,
+            ellipsis: true,
+            align: 'center',
+            render: (a: string, b: any) => {
+                return <a onClick={() => copy(a)}>{a || '--'}</a> 
+            }
+        },
+        {
+            title: '广告ID',
+            dataIndex: 'adgroupId',
+            key: 'adgroupId',
+            width: 90,
+            align: 'center',
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <a onClick={() => copy(a)}>{a || '--'}</a> 
+            }
+        },
+        {
+            title: '操作内容',
+            dataIndex: 'operationContext',
+            key: 'operationContext',
+            width: 120,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return a || '--'
+            }
+        },
+        {
+            title: '操作类型',
+            dataIndex: 'operationType',
+            key: 'operationType',
+            width: 90,
+            ellipsis: true,
+            align: 'center',
+            render: (a: string, b: any) => {
+                return OperationTypeObj[a]
+            }
+        },
+        {
+            title: '操作执行状态',
+            dataIndex: 'operationStatus',
+            key: 'operationStatus',
+            width: 95,
+            align: 'center',
+            render: (a: number, b: any) => {
+                return a === 0 ? <Badge status="default" text="等待执行" /> : a === 100 ? <Badge status="success" text="发送成功" /> : <Badge status="error" text="发送失败" />
+            }
+        },
+        {
+            title: '操作发生时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            width: 140,
+            align: 'center',
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return a || '--'
+            }
+        },
+        {
+            title: '操作执行时间',
+            dataIndex: 'executionTime',
+            key: 'executionTime',
+            width: 140,
+            ellipsis: true,
+            align: 'center',
+            render: (a: string, b: any) => {
+                return a || '--'
+            }
+        },
+        {
+            title: '告警内容',
+            dataIndex: 'sendMsg',
+            key: 'sendMsg',
+            width: 200,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <a onClick={() => copy(a)}>{a || '--'}</a> 
+            }
+        },
+        {
+            title: '失败原因',
+            dataIndex: 'failMsg',
+            key: 'failMsg',
+            width: 250,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <a onClick={() => copy(a)}>{a || '--'}</a> 
+            }
+        },
+    ]
+}
+
+export function ruleAdLogTableConfig(): any {
+
+    return [
+        {
+            title: '广告账号',
+            dataIndex: 'accountId',
+            key: 'accountId',
+            align: 'center',
+            width: 80,
+        },
+        {
+            title: '规则名称',
+            dataIndex: 'ruleName',
+            key: 'ruleName',
+            width: 140,
+            ellipsis: true,
+            align: 'center',
+            render: (a: string, b: any) => {
+                return <a onClick={() => copy(a)}>{a || '--'}</a> 
+            }
+        },
+        {
+            title: '规则ID',
+            dataIndex: 'ruleId',
+            key: 'ruleId',
+            width: 90,
+            align: 'center',
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <a onClick={() => copy(a)}>{a || '--'}</a> 
+            }
+        },
+        {
+            title: '操作内容',
+            dataIndex: 'operationContext',
+            key: 'operationContext',
+            width: 120,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return a || '--'
+            }
+        },
+        {
+            title: '操作类型',
+            dataIndex: 'operationType',
+            key: 'operationType',
+            width: 90,
+            ellipsis: true,
+            align: 'center',
+            render: (a: string, b: any) => {
+                return OperationTypeObj[a]
+            }
+        },
+        {
+            title: '操作执行状态',
+            dataIndex: 'operationStatus',
+            key: 'operationStatus',
+            width: 95,
+            align: 'center',
+            render: (a: number, b: any) => {
+                return a === 0 ? <Badge status="default" text="等待执行" /> : a === 100 ? <Badge status="success" text="发送成功" /> : <Badge status="error" text="发送失败" />
+            }
+        },
+        {
+            title: '操作发生时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            width: 140,
+            align: 'center',
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return a || '--'
+            }
+        },
+        {
+            title: '操作执行时间',
+            dataIndex: 'executionTime',
+            key: 'executionTime',
+            width: 140,
+            ellipsis: true,
+            align: 'center',
+            render: (a: string, b: any) => {
+                return a || '--'
+            }
+        },
+        {
+            title: '告警内容',
+            dataIndex: 'sendMsg',
+            key: 'sendMsg',
+            width: 200,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <a onClick={() => copy(a)}>{a || '--'}</a> 
+            }
+        },
+        {
+            title: '失败原因',
+            dataIndex: 'failMsg',
+            key: 'failMsg',
+            width: 250,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <a onClick={() => copy(a)}>{a || '--'}</a> 
+            }
+        },
+    ]
+}
 export default tableConfig

+ 17 - 0
src/components/StatisticNull/index.tsx

@@ -0,0 +1,17 @@
+import { Statistic } from "antd"
+import React from "react"
+
+interface Props {
+    data: any
+    field: string
+}
+
+const StatisticNull: React.FC<Props> = ({ field, data }) => {
+    if (data?.[field] !== undefined && data?.[field] !== null) {
+        return <Statistic value={data?.[field] || 0} />
+    } else {
+        return <span>--</span>
+    }
+}
+
+export default React.memo(StatisticNull)

+ 6 - 4
src/components/Tables/index.tsx

@@ -53,9 +53,10 @@ interface Props {
     loading?: boolean,
     defaultPageSize?: 10 | 20 | 30 | 100,
     bordered?: boolean,
+    columnWidth?: string | number
     onChange?: (pagination: any, filters: any, sorter: any) => void//服务端排序回调
     pagination?: boolean,
-    showHeader?: 1,
+    showHeader?: boolean,
     hideOnSinglePage?: boolean,
     className?: string,
     handelResize?: (columns: any) => void//当表头被拖动改变宽度时回调设置对应的本地保存
@@ -66,7 +67,7 @@ interface Props {
 
 function Tables(props: Props) {
     // //导出数据
-    let { columns, dataSource, excle, total, handelResize, rowSelection, onRow, isShowTotal = true, hideOnSinglePage, rowClassName, className, expandedRowRender, scroll, summary, showHeader, bordered, size = 'small', onChange, sortDirections, pagination, myKey, ...prop } = props
+    let { columns, dataSource, excle, total, handelResize, columnWidth, rowSelection, onRow, isShowTotal = true, hideOnSinglePage, rowClassName, className, expandedRowRender, scroll, summary, showHeader, bordered, size = 'small', onChange, sortDirections, pagination, myKey, ...prop } = props
     let handleExcle = () => {
         if (dataSource.length < 1) {
             message.error('请先搜索再导出');
@@ -126,7 +127,7 @@ function Tables(props: Props) {
             }}
             bordered={bordered ? bordered : false}
             dataSource={dataSource}
-            showHeader={showHeader === 1 ? false : true}
+            showHeader={showHeader}
             scroll={scroll ? { ...scroll, scrollToFirstRowOnChange: true } : undefined}
             size={size}
             rowKey={(a: any) => {
@@ -145,7 +146,8 @@ function Tables(props: Props) {
             className={className}
             expandable={expandedRowRender ? {
                 defaultExpandedRowKeys: ['0'],
-                expandRowByClick: false, 
+                expandRowByClick: false,
+                columnWidth, 
                 expandedRowRender: (data) => {
                     return expandedRowRender(data)
                 }

+ 8 - 0
src/global.less

@@ -399,3 +399,11 @@ body {
 .ant-modal-header {
   border-radius: 4px 4px 0 0;
 }
+
+
+.header_table_body {
+  margin-bottom: 5px;
+  td {
+    font-weight: 600;
+  }
+}

+ 17 - 2
src/models/useAdMonitor/useMonitor.ts

@@ -3,7 +3,7 @@ import {
     ListType, getPlanListApi, getTotalCostApi, getPlanCostApi, getCostSpeedApi, getUserGroupApi, getAllPlanListApi,
     allPlanProps, getDetailListApi, getMinuteListApi, downLoadUpAdApi, downLoadDetailApi,
     downLoadDetailMinuteApi, downLoadSpeedApi, downLoadAllAdListApi, addEditGroupApi, getAdGroupListApi, deleteAdGroupApi, 
-    getAccountListApi, AccountListProps, addDelAccountApi, getBookListAllApi, getAdqAccountListApi
+    getAccountListApi, AccountListProps, addDelAccountApi, getBookListAllApi, getAdqAccountListApi, getCostTopListApi, getCostTrendListApi, getListForHourApi, getColumnTrendApi, getAdTotalDataApi, getListForAdApi
 } from '@/services/adMonitor/adMonitor'
 
 
@@ -40,14 +40,26 @@ export default function useMonitor() {
 
     // 获取广告账号列表
     const getAdqAccountList = useAjax(() => getAdqAccountListApi(), { formatResult: true })
+
+    // NEW
+    const getListForHour = useAjax((params) => getListForHourApi(params), { formatResult: true, debounceInterval: 100 })
+    const getCostTrendList = useAjax((params: ListType) => getCostTrendListApi(params), { formatResult: true })
+    const getCostTopList = useAjax((params: ListType) => getCostTopListApi(params), { formatResult: true })
+    const getColumnTrend = useAjax((params) => getColumnTrendApi(params), { formatResult: true })
+    const getAdTotalData = useAjax((params) => getAdTotalDataApi(params), { formatResult: true })
+    const getListForAd = useAjax((params) => getListForAdApi(params), { formatResult: true })
+
     return {
         getPlanList,
         getTotalCost,
+        getCostTrendList,
         getPlanCost,
+        getCostTopList,
         getCostSpeed,
         getUserGroup,
         getAllPlanList,
         getPlanDetailList,
+        getListForHour,
         getMinuteList,
         downLoadUpAd,
         downLoadDetail,
@@ -60,6 +72,9 @@ export default function useMonitor() {
         getAccountList,
         addDelAccount,
         getBookListAll,
-        getAdqAccountList
+        getAdqAccountList,
+        getColumnTrend,
+        getAdTotalData,
+        getListForAd
     }
 }

+ 190 - 0
src/pages/adMonitor/adMonitorList/components/Details.tsx

@@ -0,0 +1,190 @@
+import useEcharts from "@/Hook/useEcharts"
+import { GetColumnTrendProps, GetListForAdProps } from "@/services/adMonitor/adMonitor"
+import { Card, Drawer, Select, Space, Spin, Tabs } from "antd"
+import React, { useEffect, useState } from "react"
+import { useModel } from "umi"
+import './index.less'
+import { columnsList } from "../tableMonitorConfig1"
+import TableData from "@/pages/launchSystemNew/components/TableData"
+import { detailsConfig } from "../config1"
+import TabAd from "./TabAd"
+
+interface Props {
+    data: any,
+    onClose?: () => void,
+    visible?: boolean
+}
+const Details: React.FC<Props> = ({ data, onClose, visible }) => {
+
+    /****************************************/
+    const { adgroup_id, account_id } = data
+    const [activeKey, setActiveKey] = useState<string>('1')
+    const [queryColumnTrend, setQueryColumnTrend] = useState<GetColumnTrendProps>({ accountId: account_id, adgroupId: adgroup_id, trendColumns: ['view', 'cost'], timeUnit: 'hour' })
+    const [queryList, setQueryList] = useState<GetListForAdProps>({ accountId: account_id, adgroupId: adgroup_id, columns: [], pageNum: 1, pageSize: 20, timeUnit: 'hour' })
+    const [lineDis, setLineDis] = useState<any[]>([])
+    const { LineMonitor } = useEcharts()
+    const configName = '数据报表'
+    const [totalData, setTotalData] = useState<any[]>([])
+
+    const { getColumnTrend, getAdTotalData, getListForAd } = useModel('useAdMonitor.useMonitor')
+    /****************************************/
+
+    useEffect(() => {
+        getCTList()
+    }, [queryColumnTrend])
+
+    const getCTList = () => {
+        getColumnTrend.run(queryColumnTrend).then(res => {
+            if (res?.data) {
+                let spendData: any = { legendName: '花费' }
+                let exposureData: any = { legendName: '曝光次数' }
+                res?.data?.forEach((item: { day: string, trend_unit: string, view: string, cost: string }) => {
+                    spendData[item?.trend_unit] = item?.cost
+                    exposureData[item?.trend_unit] = item?.view
+                });
+                setLineDis(() => [spendData, exposureData])
+            }
+        })
+    }
+
+    useEffect(() => {
+        getListList()
+    }, [queryList])
+
+    const getListList = () => {
+        let columns: string[] = []
+        let message = localStorage.getItem(`myAdMonitorConfig1.0.0_${configName}`)
+        if (message) {
+            message = JSON.parse(message)
+        }
+        if (message && Array.isArray(message)) {
+            message.forEach((item: { serverIndex: any; dataIndex: string; }) => {
+                columns.push('adgroup_data.' + item.dataIndex)
+            })
+        } else {
+            detailsConfig.forEach((item: any) => {
+                item?.data?.forEach((d: { default: any, serverIndex: string, dataIndex: string }) => {
+                    if (d.default) {
+                        columns.push('adgroup_data.' + d.dataIndex)
+                    }
+                })
+            })
+        }
+        getListForAd.run({ ...queryList, columns })
+        let queryTotal = JSON.parse(JSON.stringify(queryList))
+        delete queryTotal.pageNum
+        delete queryTotal.pageSize
+        delete queryTotal.timeUnit
+        getAdTotalData.run({ ...queryTotal, columns }).then(res => {
+            console.log(res)
+            if (res?.data) {
+                let data = res.data
+                data.id = 1
+                data.time = '总计'
+                setTotalData([data])
+            }
+        })
+    }
+
+    return <Drawer
+        title={data?.adgroup_name}
+        placement="right"
+        onClose={onClose}
+        visible={visible}
+        width={'70%'}
+        bodyStyle={{ padding: '0 10px', background: '#efefef' }}
+        className="detail_drawer"
+    >
+        <Tabs activeKey={activeKey} onChange={(e) => setActiveKey(e)} tabBarExtraContent={activeKey === '1' ? <Select
+            size="small"
+            value={queryColumnTrend.timeUnit}
+            style={{ width: 78 }}
+            onChange={(e) => {
+                setQueryColumnTrend({ ...queryColumnTrend, timeUnit: e })
+                setQueryList({ ...queryList, timeUnit: e })
+            }}
+        >
+            <Select.Option value="day">天</Select.Option>
+            <Select.Option value="hour">小时</Select.Option>
+            <Select.Option value="minute">5min</Select.Option>
+        </Select> : null}
+        >
+            <Tabs.TabPane tab="效果数据" key="1">
+                <Space direction="vertical" style={{ width: '100%' }}>
+                    <Card
+                        className="detail_card"
+                        hoverable
+                    >
+                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px 0' }}>
+                            <strong>趋势</strong>
+                            <Space align="center">
+                                <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getColumnTrend?.data?.reqTime}</span>
+                                <a onClick={() => getCTList()} style={{ fontSize: 14 }}>刷新</a>
+                            </Space>
+                        </div>
+                        <div style={{ width: '100%', height: 260, textAlign: 'center' }}>
+                            {getColumnTrend?.loading ? <Spin /> : <LineMonitor style={{ width: '100%', height: 260 }} series smooth data={lineDis} />}
+                        </div>
+                    </Card>
+                    <div className={'MYtable'}>
+                        <TableData
+                            refreshData={getListList}
+                            isZj
+                            totalData={totalData}
+                            bodyStyle={{ padding: '12px 16px' }}
+                            gutter={[0, 12]}
+                            columns={columnsList()}
+                            dataSource={getListForAd?.data?.data?.records}
+                            loading={getListForAd?.loading}
+                            ajax={getListForAd}
+                            leftChild={
+                                <Space>
+                                    <strong>数据报表</strong>
+                                </Space>
+                            }
+                            fixed={{ left: 0, right: 0 }}
+                            total={getListForAd?.data?.data?.total}
+                            onChange={(props: any) => {
+                                let { sortData, pagination } = props
+                                let { current, pageSize } = pagination
+                                let newQueryForm = JSON.parse(JSON.stringify(queryList))
+                                newQueryForm.pageNum = current
+                                newQueryForm.pageSize = pageSize
+                                if (sortData && JSON.stringify('sortData') !== '{}') {
+                                    let { field, order } = sortData   // descend 降序 大到小  ascend 升序 小到大
+                                    if (order) {
+                                        // newQueryForm.sortColumn = field
+                                        newQueryForm.sortAsc = order === 'ascend'
+                                    } else {
+                                        Object.keys(newQueryForm).forEach(key => {
+                                            if (key === 'sortColumn' || key === 'sortAsc') {
+                                                delete newQueryForm[key]
+                                            }
+                                        })
+                                    }
+                                } else {
+                                    Object.keys(newQueryForm).forEach(key => {
+                                        if (key === 'sortField' || key === 'sort') {
+                                            delete newQueryForm[key]
+                                        }
+                                    })
+                                }
+                                setQueryList({ ...newQueryForm })
+                            }}
+                            page={queryList.pageNum}
+                            pageSize={queryList.pageSize}
+                            scroll={{ y: 500 }}
+                            config={detailsConfig}
+                            configName={configName}
+                        />
+                    </div>
+                </Space>
+            </Tabs.TabPane>
+            <Tabs.TabPane tab="广告详情" key="2">
+                <TabAd accountId={account_id} adgroupId={adgroup_id} />
+            </Tabs.TabPane>
+        </Tabs>
+    </Drawer>
+}
+
+export default React.memo(Details)

+ 245 - 0
src/pages/adMonitor/adMonitorList/components/FilterQuery.tsx

@@ -0,0 +1,245 @@
+import { CloseOutlined, DownOutlined, FilterOutlined, SearchOutlined, UpOutlined } from "@ant-design/icons"
+import { Button, DatePicker, Form, Input, InputNumber, Popover, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import './filterQuery.less'
+import moment from "moment"
+import { useLocalStorageState, useUpdateEffect } from "ahooks"
+
+type TypeProps = 'DatePicker' | 'Input' | 'InputNumber' | 'Select'
+interface QueryProps {
+    lable: string,
+    name: string,
+    type: TypeProps,
+    value: (data?: { value?: any, onChange?: (value?: any) => void }) => JSX.Element,
+    isValue?: boolean
+}
+
+interface Props {
+    onChange?: (data: any) => void
+}
+/**
+ * 请求体 合集
+ * @returns 
+ */
+const FilterQuery: React.FC<Props> = ({ onChange }) => {
+
+    const queryList: QueryProps[] = [
+        {
+            lable: '广告创建时间',
+            name: 'adCreateTime',
+            type: 'DatePicker',
+            value: (params) => <DatePicker.RangePicker {...params} />
+        },
+        {
+            lable: '最低今日转化数',
+            name: 'conversionsCountDayMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低今日转化数" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最低总转化数',
+            name: 'conversionsCountTotalMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低总转化数" style={{ width: '100%' }}  {...params} />
+        },
+        {
+            lable: '最低今日消耗',
+            name: 'costDayMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低今日消耗" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最小总消耗',
+            name: 'costTotalMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最小总消耗" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '推广目标名称',
+            name: 'promotedObjectName',
+            type: 'Input',
+            value: (params) => <Input placeholder="请输入推广目标名称" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '推广目标类型',
+            name: 'promotedObjectType',
+            type: 'Input',
+            value: (params) => <Input placeholder="请输入推广目标类型" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最低今日千次曝光成本',
+            name: 'thousandDisplayPriceDayMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低今日千次曝光成本" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最低总千次曝光成本',
+            name: 'thousandDisplayPriceTotalMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低总千次曝光成本" style={{ width: '100%' }} {...params} />
+        },
+    ]
+
+    /********************************/
+    const [form] = Form.useForm();
+    const [visible, setVisible] = useState<boolean>(false)
+    const [filterTrueList, setFilterTrueList] = useState<QueryProps[]>([])
+    const [message, setMessage] = useLocalStorageState('filterQueryContentDayMessage', '');
+    /********************************/
+
+    useEffect(() => {
+        setFiled(message)
+    }, [message])
+
+    const setFiled = (message?: string) => {
+        if (message) {
+            let newFiter: any = []
+            message.split(',').forEach(key => {
+                newFiter.push(queryList.find(item => item.name === key))
+            })
+            console.log('==========>', newFiter)
+            setFilterTrueList(newFiter)
+        } else {
+            setFilterTrueList([])
+        }
+    }
+
+    const onFinish = async () => {
+        let newAllValue = await form.validateFields()
+        if (newAllValue?.adCreateTime && newAllValue?.adCreateTime?.length > 0) {
+            newAllValue.adCreateTimeMin = moment(newAllValue?.adCreateTime[0]).format('YYYY-MM-DD')
+            newAllValue.adCreateTimeMax = moment(newAllValue?.adCreateTime[1]).format('YYYY-MM-DD')
+            delete newAllValue?.adCreateTime
+        }
+        onChange?.(newAllValue)
+    }
+
+    return <div id="filterQueryContent">
+        <div style={{ width: '100%', display: 'flex', gap: 8 }}>
+            <Popover
+                content={<div style={{ width: 500, height: 400, overflowY: 'auto', padding: '10px 16px' }}>
+                    <Form
+                        name="FilterQuery"
+                        form={form}
+                        colon={false}
+                        layout="vertical"
+                        onValuesChange={(changedValues) => {
+                            console.log(changedValues)
+                            let messageArr = message ? message?.split(',') : []
+                            Object.keys(changedValues).forEach(key => {
+                                if (!(messageArr?.includes(key))) {
+                                    messageArr.push(key)
+                                    setMessage(messageArr.join(','))
+                                    // setFiled(messageArr.join(','))
+                                }
+                            })
+                        }}
+                        onFinish={onFinish}
+                    >
+                        {queryList.map(item => <Form.Item label={<strong>{item.lable}</strong>} name={item.name} key={item.name}>
+                            {item.value()}
+                        </Form.Item>)}
+                    </Form>
+                </div>}
+                title={<Space direction="vertical" size={0}>
+                    <h3 style={{ fontWeight: 'bold', marginBottom: 0 }}>你可能找这类广告</h3>
+                </Space>}
+                trigger={'click'}
+                placement="bottomLeft"
+                onVisibleChange={(e) => setVisible(e)}
+                getPopupContainer={() => document.getElementById('filterQueryContent') as any}
+            >
+                <Button><FilterOutlined />筛选 {visible ? <UpOutlined /> : <DownOutlined />}</Button>
+            </Popover>
+            <Button type="primary" onClick={onFinish} icon={<SearchOutlined />}>搜索</Button>
+            <Button onClick={() => {
+                form.resetFields();
+            }}>重置</Button>
+        </div>
+        {filterTrueList.length > 0 && <div className="selectedFilter">
+            {filterTrueList.map(item => {
+                let value = form.getFieldValue([item.name])
+                // if (item.type === 'DatePicker' && value && value?.length > 0) {
+                //     console.log(value, Array.isArray(value))
+                //     let start = new Date()
+                //     // let end = moment(value(1)).format('YYYY-MM-DD')
+                //     // value = start + '-' + end
+                // }
+                switch (item.name) {
+                    case 'adCreateTime':
+                        value = value ? '已经选择时间' : '请选择时间'
+                        break
+                    default:
+                        value = value || '请输入'
+                        break
+                }
+                return <Popover
+                    placement="bottom"
+                    trigger={'click'}
+                    destroyTooltipOnHide={true}
+                    content={<PopoverSetValue
+                        type={item.type}
+                        dataEntry={item.value}
+                        value={form.getFieldValue([item.name])}
+                        onChange={(value) => {
+                            form.setFieldsValue({ [item.name]: value })
+                            onFinish()
+                        }}
+                    />}
+                    title={item.lable}
+                    key={item.name}
+                >
+                    <div>
+                        <div className="text">
+                            {item.lable}:{value || '请选择'}
+                        </div>
+                        <DownOutlined style={{ fontSize: 10, marginRight: 6 }} />
+                        <div className="close" onClick={() => {
+                            form.setFieldsValue({ [item.name]: undefined })
+                            let messageArr = message?.split(',') || []
+                            messageArr = messageArr.filter(key => key !== item.name)
+                            let messageStr = messageArr.length > 0 ? messageArr.join(',') : undefined
+                            setMessage(messageStr)
+                            // setFiled(messageStr)
+                        }}><CloseOutlined /></div>
+                    </div>
+                </Popover>
+            })}
+        </div>}
+    </div>
+}
+
+export default React.memo(FilterQuery)
+
+interface PopoverSetValueProps {
+    type: TypeProps,
+    dataEntry: (data?: { value?: any, onChange?: (value?: any) => void }) => JSX.Element,
+    value?: any
+    onChange?: (value?: any) => void
+}
+const PopoverSetValue: React.FC<PopoverSetValueProps> = ({ type, dataEntry, value, onChange }) => {
+
+    /*************************/
+    const [data, setData] = useState<any>()
+    /*************************/
+
+    useEffect(() => {
+        setData(value)
+    }, [value])
+
+    return <Space>
+        {dataEntry({
+            value: data,
+            onChange(value) {
+                if (['Input'].includes(type)) {
+                    setData(value.target.value)
+                } else {
+                    setData(value)
+                }
+            },
+        })}
+        <Button type="primary" onClick={() => {
+            onChange?.(data)
+        }}>确定</Button>
+    </Space>
+}

+ 175 - 0
src/pages/adMonitor/adMonitorList/components/TabAd.tsx

@@ -0,0 +1,175 @@
+import { useAjax } from "@/Hook/useAjax"
+import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook"
+import { addAdToRuleBlackListApi, delAdToRuleBlackListApi, delAdWarningRuleApi, getAdgroupsDetailsApi } from "@/services/adMonitor/adMonitor"
+import { AdStatusEnum, BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType } from "@/services/launchAdq/enum"
+import { Button, Card, Descriptions, Empty, Popover, Space, Spin, Typography, message } from "antd"
+import React, { useEffect, useMemo, useState } from "react"
+import Box from "./box"
+import TableData from "@/pages/launchSystemNew/components/TableData"
+import tableConfigEw from "./tableConfigEw"
+import SetEarlyWarnings from "@/components/EarlyWarning/setEarlyWarnings"
+import RuleLog from "@/components/EarlyWarning/ruleLog"
+
+export enum EWTypeEnum {
+    DEFAULT = '默认告警规则',
+    ACCOUNT = '广告账号告警规则',
+    ADGROUP = '广告告警规则',
+    ADGROUP_BLACK_LIST = '告警黑名单'
+}
+
+interface Props {
+    accountId: any
+    adgroupId: any
+}
+const TabAd: React.FC<Props> = ({ accountId, adgroupId }) => {
+
+    /*******************************/
+    const [logVisible, setLogVisible] = useState<boolean>(false)
+    const [ruleId, setrRleId] = useState<number>(0)
+    const [ruleName, setrRuleName] = useState<string>('')
+    
+    const getAdgroupsDetails = useAjax((params) => getAdgroupsDetailsApi(params), { formatResult: true })
+    const delAdWarningRule = useAjax((params) => delAdWarningRuleApi(params), { formatResult: true })
+    const addAdToRuleBlackList = useAjax((params) => addAdToRuleBlackListApi(params), { formatResult: true })
+    const delAdToRuleBlackList = useAjax((params) => delAdToRuleBlackListApi(params), { formatResult: true })
+    /*******************************/
+
+    useEffect(() => {
+        getAdgroupsDetails.run({ adgroupId, accountId })
+    }, [adgroupId, accountId])
+
+    const addBlack = (value: any) => {
+        console.log(value)
+        addAdToRuleBlackList.run({ adgroupId, accountId, ruleId: value.id }).then(res => {
+            if (res?.data) {
+                message.success('添加成功')
+                getAdgroupsDetails.refresh()
+            }
+        })
+    }
+
+    const remove = (value: any) => {
+        delAdWarningRule.run({ adgroupId, accountId, ruleId: value.id }).then(res => {
+            if (res?.data) {
+                message.success('移除成功')
+                getAdgroupsDetails.refresh()
+            }
+        })
+    }
+
+    const removeBlack = (value: any) => {
+        delAdToRuleBlackList.run({ adgroupId, accountId, ruleId: value.id }).then(res => {
+            if (res?.data) {
+                message.success('移出成功')
+                getAdgroupsDetails.refresh()
+            }
+        })
+    }
+
+    const log = (value: any) => {
+        setrRleId(value.id)
+        setrRuleName(value.ruleName)
+        setLogVisible(true)
+    }
+
+    const AdContent = useMemo(() => {
+        if (getAdgroupsDetails?.data?.data) {
+            const { adgroupName, bidAmount, bidMode, optimizationGoal, adgroupId, rejectMessageList, status, smartBidType, beginDate,
+                endDate, targetingTranslation, timeSeries, firstDayBeginTime, dailyBudget, bidStrategy, promotedObjectType, accountId,
+                creativeName, creativePreview
+            } = getAdgroupsDetails?.data?.data
+            return <Spin spinning={getAdgroupsDetails.loading}>
+                <Descriptions column={2} size="small" colon={false}>
+                    <Descriptions.Item labelStyle={{ width: 100, flex: '0 0 auto' }} label="广告名称">
+                        <div style={{ width: '90%' }}>
+                            <Typography.Text ellipsis={{ tooltip: true }}>{adgroupName}</Typography.Text>
+                        </div>
+                    </Descriptions.Item>
+                    <Descriptions.Item labelStyle={{ width: 100 }} label="出价">{`${BidModeEnum[bidMode]} ${bidAmount}元/${bidMode === 'BID_MODE_CPM' ? '千次曝光' : bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[optimizationGoal]}`}</Descriptions.Item>
+                    <Descriptions.Item labelStyle={{ width: 100 }} label="广告ID">{adgroupId}</Descriptions.Item>
+                    <Descriptions.Item labelStyle={{ width: 100 }} label="状态">
+                        {rejectMessageList?.filter((str: any) => str)?.length > 0 ? <Popover
+                            style={{ width: 500 }}
+                            overlayStyle={{ width: 500, fontSize: 12 }}
+                            placement="left"
+                            content={rejectMessageList?.map((str: string, eq: number) => {
+                                return str ? <><strong style={{ fontSize: 13 }}>{eq + 1}:</strong>{str}<br /></> : ""
+                            })}>
+                            {AdStatusEnum[status]}
+                        </Popover> :
+                            AdStatusEnum[status]}
+                    </Descriptions.Item>
+                    <Descriptions.Item label="广告账号" labelStyle={{ width: 100 }}>{accountId}</Descriptions.Item>
+                    <Descriptions.Item label="出价类型" labelStyle={{ width: 100 }}>{smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</Descriptions.Item>
+                    <Descriptions.Item label="推广目标类型" labelStyle={{ width: 100 }}>{PromotedObjectType[promotedObjectType]}</Descriptions.Item>
+                    <Descriptions.Item label="投放日期" labelStyle={{ width: 100 }}>{endDate ? beginDate + '~' + endDate : beginDate + '~' + '长期投放'}</Descriptions.Item>
+                    <Descriptions.Item label="定向" labelStyle={{ width: 100 }} span={2}>{targetingTranslation}</Descriptions.Item>
+                    <Descriptions.Item label="投放时间" labelStyle={{ width: 100 }}>{<TimeSeriesLook timeSeries={timeSeries} />}</Descriptions.Item>
+                    <Descriptions.Item label="首日开始时间" labelStyle={{ width: 100 }}>{firstDayBeginTime}</Descriptions.Item>
+                    <Descriptions.Item label="日预算" labelStyle={{ width: 100 }}>{dailyBudget}</Descriptions.Item>
+                    <Descriptions.Item label="创意名称" labelStyle={{ width: 100 }}>{creativeName}</Descriptions.Item>
+                    <Descriptions.Item label="出价策略" labelStyle={{ width: 100 }}>{BidStrategyEnum[bidStrategy]}</Descriptions.Item>
+                    <Descriptions.Item label="创意预览" labelStyle={{ width: 100 }}><div style={{ width: 50, height: 22 }}>{<Box b={{ creativePreview }} />}</div></Descriptions.Item>
+                </Descriptions>
+            </Spin>
+        }
+        return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+    }, [getAdgroupsDetails?.data?.data, getAdgroupsDetails.loading])
+
+    const EWContent = useMemo(() => {
+
+        if (getAdgroupsDetails?.data?.data?.warningRuleMap && Object.keys(getAdgroupsDetails?.data?.data?.warningRuleMap).length > 0) {
+            let data = getAdgroupsDetails?.data?.data?.warningRuleMap
+            let dataArr = Object.keys(getAdgroupsDetails?.data?.data?.warningRuleMap)
+            if (!dataArr.includes('ADGROUP')) {
+                dataArr.push('ADGROUP')
+            }
+            return dataArr.map(key => <Card
+                hoverable
+                style={{ width: '100%' }}
+                key={key}
+                bodyStyle={{ padding: 16 }}
+            >
+                <TableData
+                    size="small"
+                    isCard={false}
+                    columns={() => tableConfigEw(key as any, addBlack, remove, removeBlack, log)}
+                    ajax={getAdgroupsDetails}
+                    dataSource={data[key]}
+                    leftChild={<Space>
+                        <strong>{EWTypeEnum[key]}</strong>
+                        {key === 'ADGROUP' && <SetEarlyWarnings  accountId={accountId} adgroupId={adgroupId} onChange={() => getAdgroupsDetails.refresh()}/>}
+                    </Space>}
+                    loading={getAdgroupsDetails?.loading}
+                    total={getAdgroupsDetails?.data?.data?.length}
+                    gutter={[0, 10]}
+                />
+            </Card>)
+        }
+        return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+    }, [getAdgroupsDetails?.data?.data, getAdgroupsDetails.loading])
+
+    return <Space style={{ width: '100%' }} direction="vertical">
+        <Card
+            hoverable
+            className="detail_card"
+            style={{ width: '100%' }}
+        >
+            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px 0' }}>
+                <strong>广告</strong>
+                <Space align="center">
+                    <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getAdgroupsDetails?.data?.reqTime}</span>
+                    <a onClick={() => getAdgroupsDetails.refresh()} style={{ fontSize: 14 }}>刷新</a>
+                </Space>
+            </div>
+            <div style={{ padding: 16 }}>
+                {AdContent}
+            </div>
+        </Card>
+        {EWContent}
+
+        {logVisible && <RuleLog ruleName={ruleName} ruleId={ruleId} visible={logVisible} onClose={() => setLogVisible(false)}/>}
+    </Space>
+}
+
+export default React.memo(TabAd)

+ 1 - 1
src/pages/adMonitor/adMonitorList/components/box.tsx

@@ -5,7 +5,7 @@ import React, { useMemo } from 'react'
 function Box(props: { b: any }) {
     const { b } = props
     const creativePreview = b?.creativePreview || {}
-
+    
     let el = useMemo(() => {
         let image_list = creativePreview?.image_list || []
         let image = creativePreview?.image

+ 64 - 0
src/pages/adMonitor/adMonitorList/components/filterQuery.less

@@ -0,0 +1,64 @@
+#filterQueryContent .ant-popover-inner-content {
+    padding: 0;
+}
+
+.selectedFilter {
+    overflow: auto;
+    display: flex;
+    gap: 10px;
+    max-width: 100%;
+    margin-top: 10px;
+    
+    &::-webkit-scrollbar {
+        // display: none; /* Chrome Safari */
+        width: 1px;
+        height: 1px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+        border-radius: 4px;
+        -webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0);
+        background-color: #ddd;
+        background: rgba(0, 0, 0, 0);
+    }
+
+    &:hover::-webkit-scrollbar-thumb {
+        background: rgba(82, 82, 82, 0.3);
+        -webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.2);
+    }
+
+    >div {
+        max-width: 250px;
+        height: 32px;
+        display: flex;
+        align-items: center;
+        gap: 10;
+        border: 1px solid #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        background-color: #F2F4F7;
+        color: #313233;
+
+
+        .text {
+            width: calc(100% - 40px);
+            white-space: nowrap;
+            text-overflow: ellipsis;
+            overflow: hidden;
+            padding: 0 0 0 10px;
+        }
+
+        .close {
+            height: 32px;
+            width: 32px;
+            border-left: 1px solid #d9d9d9;
+            text-align: center;
+            line-height: 32px;
+            color: red;
+
+            &:hover {
+                color: #1890ff;
+            }
+        }
+    }
+}

+ 26 - 0
src/pages/adMonitor/adMonitorList/components/index.less

@@ -0,0 +1,26 @@
+.totalRow {
+    .ant-card-body {
+        padding: 6px 10px;
+    }
+    .ant-statistic-content {
+        color: rgba(0, 0, 0, 0.85);
+        font-size: 24px;
+    }
+}
+
+.detail_card {
+    .ant-card-body {
+        padding: 0;
+    }
+}
+
+.detail_drawer .ant-tabs-nav{
+    margin: 0;
+    background: #fff;
+    padding: 0 10px;
+    margin-top: 10px;
+}
+
+.select_field .ant-popover-inner-content {
+    padding: 0;
+}

+ 95 - 0
src/pages/adMonitor/adMonitorList/components/tableConfigEw.tsx

@@ -0,0 +1,95 @@
+import { WarningTypeEnum } from '@/components/EarlyWarning/config'
+import Look from '@/components/EarlyWarning/look'
+import { Badge, Space, Tag } from 'antd'
+import React from 'react'
+function tableConfigEw(type: string, addBlack: (valud: any) => void, remove: (value: any) => void, removeBlack: (value: any) => void, log: (value: any) => void): any {
+    return [
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            align: 'center',
+            width: 50,
+        },
+        {
+            title: '规则名称',
+            dataIndex: 'ruleName',
+            key: 'ruleName',
+            width: 120,
+            ellipsis: true,
+        },
+        {
+            title: '通知频率',
+            dataIndex: 'notifyFrequency',
+            key: 'notifyFrequency',
+            align: 'center',
+            width: 70,
+            render: (a: string, b: any) => {
+                return <span>{a}分钟</span>
+            }
+        },
+        {
+            title: '告警方式',
+            dataIndex: 'warningType',
+            key: 'warningType',
+            align: 'center',
+            width: 115,
+            render: (a: string[], b: any) => {
+                return a?.map((item, index) => <Tag color={['#f50', '#2db7f5'][index]} key={index}>{WarningTypeEnum[item]}</Tag>)
+            }
+        },
+        {
+            title: 'Enable?',
+            dataIndex: 'enable',
+            key: 'enable',
+            align: 'center',
+            width: 70,
+            render: (a: 0 | 1, b: any) => {
+                return <Badge status={{ '0': 'error', '1': 'success' }[a] as any} text={{ '0': '否', '1': '是' }[a]} />
+            }
+        },
+        {
+            title: '查看',
+            dataIndex: 'rules',
+            key: 'rules',
+            align: 'center',
+            width: 60,
+            render: (a: any, b: any) => {
+                return <Look rules={a} />
+            }
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 80,
+            render: (a: 0 | 1, b: any) => {
+                switch (type) {
+                    case 'DEFAULT':
+                        return <Space>
+                            <a onClick={() => { addBlack(b) }}>加入黑名单</a>
+                            <a onClick={() => { log(b) }}>告警日志</a>
+                        </Space>
+                    case 'ADGROUP':
+                        return <Space>
+                            <a onClick={() => { remove(b) }}>移除规则</a>
+                            <a onClick={() => { log(b) }}>告警日志</a>
+                        </Space>
+                    case 'ACCOUNT':
+                        return <Space>
+                            <a onClick={() => { addBlack(b) }}>加入黑名单</a>
+                            <a onClick={() => { log(b) }}>告警日志</a>
+                        </Space>
+                    case 'ADGROUP_BLACK_LIST':
+                        <Space>
+                            <a onClick={() => { removeBlack(b) }}>加入黑名单</a>
+                        </Space>
+                    default:
+                        return null
+                }
+            }
+        },
+    ]
+}
+export default tableConfigEw

+ 153 - 0
src/pages/adMonitor/adMonitorList/config1.ts

@@ -0,0 +1,153 @@
+/**起量表小时列表 */
+const qiliangpaihanghour = [
+    {
+        label: '设置信息',
+        data: [
+            { title: '时间', dataIndex: 'day', label: '设置信息', default: 1, width: 110 },
+            { title: '广告名称', dataIndex: 'adgroup_name', label: '设置信息', default: 2, width: 170 },
+            { title: '广告ID', dataIndex: 'adgroup_id', label: '设置信息', default: 9, width: 90 },
+            { title: '计划ID', dataIndex: 'campaign_id', label: '设置信息' },
+            { title: '广告账户', dataIndex: 'account_id', label: '设置信息', default: 10, width: 70 },
+            { title: '投手', dataIndex: 'put_user_name', serverIndex: 'sys_user.put_user_name', label: '设置信息', default: 11, width: 65 },
+            { title: '创意预览', dataIndex: 'creative_preivew', serverIndex: 'adgroup_data.creative_id', label: '设置信息', width: 80 },
+            { title: '投放时间', dataIndex: 'begin_date', serverIndex: 'adgroups.begin_date, adgroups.end_date', label: '设置信息', default: 12, width: 135 },
+            { title: '广告状态', dataIndex: 'status', serverIndex: 'adgroups.status', label: '设置信息', width: 85 },
+            { title: '推广目标', dataIndex: 'promoted_object_type', serverIndex: 'adgroups.promoted_object_type', label: '设置信息' },
+            { title: '操作', dataIndex: 'event', label: '设置信息', default: 13, width: 170 },
+        ]
+    },
+    {
+        label: '费用',
+        data: [
+            { title: '广告预算', dataIndex: 'daily_budget', serverIndex: 'adgroups.daily_budget', label: '费用', width: 80 },
+            { title: '出价方式', dataIndex: 'bid_mode', serverIndex: 'adgroups.bid_mode',label: '费用', width: 65 },
+            { title: '当前出价', dataIndex: 'bid_amount', serverIndex: 'adgroups.bid_amount', label: '费用', width: 80 },
+            // { title: '计划预算', dataIndex: 'planBudget', label: '费用' },
+            { title: '广告总消耗', dataIndex: 'cost_total', label: '费用', default: 24, width: 100 },
+            { title: '今日消耗', dataIndex: 'cost_day', label: '费用', default: 15, width: 90 },
+            { title: '当前小时消耗', dataIndex: 'cost_hour', label: '费用', default: 16, width: 80 },
+            { title: '前第1小时消耗', dataIndex: 'cost_last_hour', label: '费用', default: 17, width: 80 },
+            { title: '前第2小时消耗', dataIndex: 'cost_last_two_hour', label: '费用', default: 18, width: 80 },
+            { title: '前第3小时消耗', dataIndex: 'cost_last_three_hour', label: '费用', default: 19, width: 80 },
+            { title: '当前小时消耗差额', dataIndex: 'cost_diff_before_hour', label: '费用', default: 20, width: 80 },
+            { title: '前第1小时消耗差额', dataIndex: 'cost_diff_before_two_hour', label: '费用', default: 21, width: 90 },
+            { title: '前第2小时消耗差额', dataIndex: 'cost_diff_before_three_hour', label: '费用', default: 22, width: 90 },
+            { title: '前三小时消耗趋势', dataIndex: 'cost_trend_last_three_hour', label: '费用', default: 23, width: 70 },
+            { title: '当前5min消耗流速', dataIndex: 'cost_speed', label: '费用', default: 14, width: 70 },
+        ]
+    },
+    {
+        label: '曝光',
+        data: [
+            { title: '曝光量(曝光次数)', dataIndex: 'view_day', label: '曝光', default: 3, width: 70 },
+            { title: '千次曝光成本', dataIndex: 'thousand_display_price_day', label: '曝光', default: 4, width: 80 },
+        ]
+    },
+    {
+        label: '点击',
+        data: [
+            { title: '点击量(点击次数)', dataIndex: 'click_day', label: '点击', width: 75 },
+            { title: '点击均价', dataIndex: 'cpc_day', label: '点击', width: 75 },
+            { title: '点击率', dataIndex: 'ctr_day', label: '点击', width: 75 },
+        ]
+    },
+    {
+        label: '转化指标组',
+        data: [
+            { title: '优化目标(转化目标)', dataIndex: 'optimization_goal', serverIndex: 'adgroups.optimization_goal', label: '转化指标组', width: 120 },
+            { title: '转化目标量', dataIndex: 'conversions_count_day', label: '转化指标组', default: 7, width: 75 },
+            { title: '转化目标成本', dataIndex: 'conversions_cost_day', label: '转化指标组', default: 8, width: 75 },
+            { title: '目标转化率', dataIndex: 'conversions_rate_day', label: '转化指标组', width: 75 },
+        ]
+    },
+    {
+        label: '商品转化',
+        data: [
+            { title: '下单量', dataIndex: 'order_count_day', label: '商品转化', width: 70 },
+            // { title: '首日新增下单量', dataIndex: 'first_dayOrder_count_day', label: '商品转化', width: 80 },
+            { title: '下单成本', dataIndex: 'order_cost_day', label: '商品转化', width: 75 },
+            { title: '下单率', dataIndex: 'order_rate_day', label: '商品转化', width: 70 },
+            { title: '下单金额', dataIndex: 'order_amount_day', label: '商品转化', width: 70 },
+            // { title: '首日新增下单金额', dataIndex: 'first_day_order_amount_day', label: '商品转化', width: 75 },
+            { title: '下单客单价', dataIndex: 'atv_day', label: '商品转化', width: 70 },
+            { title: '下单ROI', dataIndex: 'order_roi_day', label: '商品转化', width: 75, default: 5 },
+            // { title: '首日新增下单ROI', dataIndex: 'firstDayOrderRoiDay', label: '商品转化', width: 90 },
+        ]
+    },
+    {
+        label: '转化',
+        data: [
+            { title: '公众号关注人数', dataIndex: 'mp_follow_uv_day', label: '转化', default: 6, width: 85 },
+            { title: '公众号关注率', dataIndex: 'mp_follow_rate_day', label: '转化', width: 70 },
+            { title: '公众号关注成本', dataIndex: 'mp_follow_cost_day', label: '转化', width: 85 },
+            { title: '加粉成本', dataIndex: 'add_fans_cost_day', label: '转化' },
+            { title: '公众号关注次数成本', dataIndex: 'mp_follow_pv_cost_day', label: '转化' },
+            { title: '加粉数', dataIndex: 'add_fans_count_day', label: '转化' },
+            { title: '公众号关注次数', dataIndex: 'mp_follow_pv_day', label: '转化' },
+            { title: '加企业微信客服人数', dataIndex: 'scan_follow_uv_day', label: '转化' },
+            { title: '加企业微信客服成本', dataIndex: 'scan_follow_cost_day', label: '转化' },
+            { title: '加企业微信客服率', dataIndex: 'scan_follow_rate_day', label: '转化' },
+        ]
+    },
+]
+
+const detailsConfig = [
+    {
+        label: '广告信息',
+        data: [
+            { title: '时间', dataIndex: 'time', label: '广告信息', default: 1, width: 140 },
+            { title: '数据更新时间', dataIndex: 'create_time', label: '广告信息', width: 85 },
+            { title: '广告账户', dataIndex: 'account_id', label: '广告信息', default: 2, width: 70 },
+            { title: '广告ID', dataIndex: 'adgroup_id', label: '广告信息', default: 3, width: 90 },
+            { title: '消耗', dataIndex: 'cost', label: '广告信息', default: 4, width: 90 },
+            { title: '曝光量', dataIndex: 'view', label: '广告信息', default: 5, width: 90 },
+            { title: '千次曝光成本', dataIndex: 'thousand_display_price', label: '广告信息', default: 6, width: 90 },
+            { title: '点击量', dataIndex: 'click', label: '广告信息', default: 7, width: 90 },
+            { title: '点击率', dataIndex: 'ctr', label: '广告信息', default: 8, width: 90 },
+            { title: '点击均价', dataIndex: 'cpc', label: '广告信息', default: 9, width: 90 },
+            { title: '不感兴趣点击次数', dataIndex: 'no_interest_count', label: '广告信息', width: 90 },
+            { title: '朋友圈视频播放次数', dataIndex: 'video_play_count', label: '广告信息', width: 90 },
+        ]
+    },
+    {
+        label: '广告转化信息',
+        data: [
+            { title: '下载次数', dataIndex: 'download_count', label: '广告转化信息', width: 80 },
+            { title: '安装次数', dataIndex: 'install_count', label: '广告转化信息', width: 80 },
+            { title: '激活次数', dataIndex: 'activated_count', label: '广告转化信息', width: 80 },
+            { title: '公众号关注人数', dataIndex: 'mp_follow_uv', label: '广告转化信息', width: 80 },
+            { title: '公众号关注成本', dataIndex: 'mp_follow_cost', label: '广告转化信息', width: 80 },
+            { title: '公众号关注率', dataIndex: 'mp_follow_rate', label: '广告转化信息', width: 80 },
+            { title: '公众号关注次数', dataIndex: 'mp_follow_pv', label: '广告转化信息', width: 80 },
+            { title: '公众号关注次数成本', dataIndex: 'mp_follow_pv_cost', label: '广告转化信息', width: 90 },
+            { title: '快应用添加次数', dataIndex: 'add_quick_app_pv', label: '广告转化信息', width: 80 },
+            { title: '快应用添加成本', dataIndex: 'add_quick_app_cost', label: '广告转化信息', width: 80 },
+            { title: '快应用添加率', dataIndex: 'add_quick_app_rate', label: '广告转化信息', width: 80 },
+            { title: '加企业微信客服人数', dataIndex: 'scan_follow_uv', label: '广告转化信息', width: 90 },
+            { title: '加企业微信客服成本', dataIndex: 'scan_follow_cost', label: '广告转化信息', width: 90 },
+            { title: '加企业微信客服率', dataIndex: 'scan_follow_rate', label: '广告转化信息', width: 90 },
+        ]
+    },
+    {
+        label: '商品转化',
+        data: [
+            { title: '首日新增下单量', dataIndex: 'first_day_order_count', label: '商品转化', width: 80 },
+            { title: '首日新增下单金额', dataIndex: 'first_day_order_amount', label: '商品转化', width: 80 },
+            { title: '首日新增下单ROI', dataIndex: 'first_day_order_roi', label: '商品转化', width: 80 },
+            { title: '订单量', dataIndex: 'order_count', label: '商品转化', width: 80, default: 10 },
+            { title: '订单金额', dataIndex: 'order_amount', label: '商品转化', width: 80 },
+            { title: '下单成本', dataIndex: 'order_cost', label: '商品转化', width: 80 },
+            { title: '下单率', dataIndex: 'order_rate', label: '商品转化', width: 80, default: 11 },
+            { title: '下单ROI', dataIndex: 'order_roi', label: '商品转化', width: 80 },
+            { title: '客单价', dataIndex: 'atv', label: '商品转化', width: 80 },
+            { title: '转化量', dataIndex: 'conversions_count', label: '商品转化', width: 80 },
+            { title: '转化成本', dataIndex: 'conversions_cost', label: '商品转化', width: 80 },
+            { title: '深度转化', dataIndex: 'deep_conversions_count', label: '商品转化', width: 80 },
+            { title: '转化率', dataIndex: 'conversions_rate', label: '商品转化', width: 80 },
+            { title: '加粉数', dataIndex: 'add_fans_count', label: '商品转化', width: 80 },
+            { title: '加粉成本', dataIndex: 'add_fans_cost', label: '商品转化', width: 80 },
+        ]
+    },
+]
+
+export { qiliangpaihanghour, detailsConfig }

+ 5 - 33
src/pages/adMonitor/adMonitorList/index.tsx

@@ -1,16 +1,14 @@
-import Ad from "@/pages/launchSystemNew/adq/ad"
-import { Card, Select, Tabs } from "antd"
+import { Card, Tabs } from "antd"
 import React, { useEffect, useState } from "react"
 import { useModel } from "umi"
-import Monitor from "./monitor"
-import PlanList from "./planList"
-
+import Monitor from "./monitor1"
+import AdPlanList from "@/pages/launchSystemNew/adq/ad/adPlanList"
 
 const AdMonitorList: React.FC = () => {
     // 变量开始
     const [tab, setTab] = useState<string>('monitor')  // tab切换
     const { getPlanList, getPlanDetailList, getAllPlanList } = useModel('useAdMonitor.useMonitor')
-    const [userId, setUserId] = useState(localStorage.getItem("userId") as string)
+    const [userId] = useState(localStorage.getItem("userId") as string)
     const { getPicherList } = useModel('useOperating.useWxGroupList')
     // 变量结束
     // 获取投手
@@ -29,34 +27,8 @@ const AdMonitorList: React.FC = () => {
         }}>
             <Tabs.TabPane tab="今日起量广告监控" key="monitor" />
             <Tabs.TabPane tab="广告列表" key="list" />
-            <Tabs.TabPane tab="广告列表2" key="list2" />
         </Tabs>
-        {tab === 'monitor' ? <Monitor onChange={() => { setTab('list') }} /> : tab === 'list' ? <PlanList /> : <Card bodyStyle={{ padding: '12px 16px' }}><Ad userId={userId}  Ts={()=>{
-            return <Select
-            showSearch
-            value={userId ? Number(userId) : undefined}
-            style={{ minWidth: 180, maxWidth: 250 }}
-            maxTagCount={1}
-            allowClear
-            placeholder="请选择投手"
-            // disabled={queryForm?.adgroup || queryForm?.accountId?.length > 0}
-            onChange={(value) => {
-                    setUserId(value?.toString() || '')
-            }}
-            filterOption={(input, option) =>
-                (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
-            }
-        >
-            {getPicherList?.data?.map((item: { nickname: string, userId: number }, index: number) =>
-                <Select.Option
-                    value={item.userId}
-                    key={item.userId + '' + index}
-                >
-                    {item.nickname}
-                </Select.Option>
-            )}
-        </Select>
-        }}/></Card>}
+        {tab === 'monitor' ? <Monitor onChange={() => { setTab('list') }} /> : <Card bodyStyle={{ padding: '12px 16px' }}><AdPlanList userId={userId} /></Card>}
     </div>
 }
 

+ 414 - 0
src/pages/adMonitor/adMonitorList/monitor1.tsx

@@ -0,0 +1,414 @@
+import { Card, Input, Radio, Select, Space, Spin, Tag, TimePicker, Tooltip } from "antd";
+import React, { useCallback, useEffect, useState } from "react";
+import { ColumnHeightOutlined, ColumnWidthOutlined, EyeInvisibleOutlined, EyeOutlined, RedoOutlined } from "@ant-design/icons";
+import useEcharts from '@/Hook/useEcharts'
+import { columnsMonitor } from './tableMonitorConfig1'
+import { useModel } from 'umi'
+import { ListHourProps, ListType } from '@/services/adMonitor/adMonitor'
+import { qiliangpaihanghour } from './config1'
+import Details from './components/Details'
+import './table.less'
+import moment from "moment";
+import TableData from "@/pages/launchSystemNew/components/TableData";
+import FilterQuery from "./components/FilterQuery";
+import RuleAccountLog from "@/components/EarlyWarning/ruleAccountLog";
+interface newListType extends ListType {
+    totalTimeUnit: 'total' | 'minute' | 'hour' | 'day',
+    planTimeUnit: 'minute' | 'hour' | 'day'
+}
+
+/**
+ * 今日起量监控
+ * @param props 
+ * @returns 
+ */
+function Monitor(props: { onChange: () => void }) {
+    const { onChange } = props
+    const { getCostTrendList, getCostTopList, getListForHour, getMinuteList, getAdqAccountList } = useModel('useAdMonitor.useMonitor')
+    const { getPicherList } = useModel('useOperating.useWxGroupList')
+    // 变量开始
+    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'),
+    const { BarMonitor, LineMonitor } = useEcharts()
+    const [barDis, setBarDis] = useState<any[]>([])
+    const [lineDis, setLineDis] = useState<any[]>([])
+    const [lineTitle, setLineTitle] = useState<string>('广告总消耗趋势')
+    const [px, setPx] = useState<boolean>(false)//设置顶部图形的排列方式
+    const [visible, setVisible] = useState<boolean>(false) // 详情弹窗控制
+    const [aId, setAId] = useState<any>()
+    const [showEacharts, setShowEacharts] = useState<boolean>(true)
+    const [queryForHour, setQueryForHour] = useState<ListHourProps>({ pageNum: 1, pageSize: 20 })
+    const [filterQuery, setFilterQuery] = useState<any>({})
+
+    const [logVisible, setLogVisible] = useState<boolean>(false)
+    const [adgroupId, setAdgroupId] = useState<string>('')
+    const [adgroupName, setAdgroupName] = useState<string>('')
+    const [accountIdRule, setAccountIdRule] = useState<string>('')
+
+    const { totalTimeUnit, planTimeUnit, adgroup, accountId, sysUserId } = queryForm
+    const configName = '起量广告排行明细New'
+
+    useEffect(() => {
+        getList()
+    }, [queryForHour, filterQuery, queryForm?.sysUserId, queryForm?.accountId, queryForm?.adgroup])
+
+    const getList = () => {
+        let message = localStorage.getItem(`myAdMonitorConfig1.0.0_${configName}`)
+        if (message) {
+            message = JSON.parse(message)
+        }
+        let isAccountId = true
+        let isAdgroupId = true
+        let isAdgroupName = true
+        let columns: string[] = []
+        if (message && Array.isArray(message)) {
+            message.forEach((item: { serverIndex: any; dataIndex: string; }) => {
+                if (!['event'].includes(item.dataIndex)) {
+                    if (item.dataIndex === 'account_id') {
+                        isAccountId = false
+                    } else if (item.dataIndex === 'adgroup_id') {
+                        isAdgroupId = false
+                    } else if (item.dataIndex === 'adgroup_name') {
+                        isAdgroupName = false
+                    }
+                    columns.push(item?.serverIndex || 'adgroup_data.' + item.dataIndex)
+                }
+            })
+        } else {
+            qiliangpaihanghour.forEach((item: any) => {
+                item?.data?.forEach((d: { default: any, serverIndex: string, dataIndex: string }) => {
+                    if (d.default && !['event'].includes(d.dataIndex)) {
+                        if (d.dataIndex === 'account_id') {
+                            isAccountId = false
+                        } else if (d.dataIndex === 'adgroup_id') {
+                            isAdgroupId = false
+                        } else if (item.dataIndex === 'adgroup_name') {
+                            isAdgroupName = false
+                        }
+                        columns.push(d?.serverIndex || 'adgroup_data.' + d.dataIndex)
+                    }
+                })
+            })
+        }
+        if (isAccountId) {
+            columns.push('adgroup_data.account_id')
+        }
+        if (isAdgroupId) {
+            columns.push('adgroup_data.adgroup_id')
+        }
+        if (isAdgroupName) {
+            columns.push('adgroup_data.adgroup_name')
+        }
+
+        let params = { ...queryForHour, ...filterQuery }
+        if (queryForm?.sysUserId) {
+            params.sysUserIds = queryForm?.sysUserId
+        }
+        if (queryForm?.accountId && queryForm?.accountId?.length > 0) {
+            params.accountIdStr = queryForm?.accountId.toString()
+        }
+        if (queryForm?.adgroup) {
+            params.adgroupIdStr = queryForm?.adgroup
+        }
+        params.columns = columns
+        getListForHour.run(params)
+    }
+
+    // 获取投手
+    useEffect(() => {
+        !getPicherList.data && getPicherList.run()
+    }, [])
+    // 获取广告账号
+    useEffect(() => {
+        !getAdqAccountList.data && getAdqAccountList.run()
+    }, [])
+    // // 获取排行数据,柱图
+    useEffect(() => {
+        getPlanCostList()
+    }, [totalTimeUnit, accountId, sysUserId])
+    // 获取今日计划总消耗图谱,折线
+    useEffect(() => {
+        getTootalCostList()
+    }, [planTimeUnit, adgroup, accountId, sysUserId])
+
+    /** 获取折线图 */
+    const getTootalCostList = useCallback(async () => {
+        let { totalTimeUnit, planTimeUnit, pageNum, pageSize, adgroup, sysUserId, accountId, ...newQueryForm } = queryForm
+        let params = adgroup ? { ...newQueryForm, adgroupId: adgroup } : newQueryForm
+        let newPitcherIds = sysUserId?.join()
+        let res = await getCostTrendList.run({ ...params, timeUnit: planTimeUnit, sysUserId: newPitcherIds, accountId: accountId?.join() })
+        let data: any[] = [{ legendName: '消耗' }]
+        res?.data?.forEach((item: any) => {
+            data[0][item.trendUnit] = item.costOfUnit
+        })
+        setLineDis(() => data)
+        setLineTitle(() => res?.data?.adName ? res?.data?.adName + '_消耗趋势' : '广告总消耗趋势')
+    }, [queryForm, lineDis])
+
+    /** 获取柱状图 */
+    const getPlanCostList = useCallback(async () => {
+        let { totalTimeUnit, planTimeUnit, pageNum, pageSize, sysUserId, accountId, ...newQueryForm } = queryForm
+        let { adgroup, ...planQueryFrom } = newQueryForm
+        let newPitcherIds = sysUserId?.join()
+        let res = await getCostTopList.run({ ...planQueryFrom, timeUnit: totalTimeUnit, sysUserId: newPitcherIds, accountId: accountId?.join(), topN: 10 })
+        let data = res?.data?.map((item: { adgroupId: number, cost: number, adgroupName: string, accountId: number }) => {
+            return { name: item.adgroupId.toString(), value: item.cost, adName: item.adgroupName, accountId: item.accountId }
+        })
+        data = data?.sort((a: any, b: any) => {
+            var value1 = a['value'];
+            var value2 = b['value'];
+            return value2 - value1;
+        })
+        setBarDis(() => data)
+    }, [queryForm, barDis])
+
+    // 详情
+    const details = (data: any) => {
+        setAId(data)
+        setVisible(true)
+    }
+
+    // 计划详情
+    const planDetail = (data: any) => {
+        sessionStorage.setItem('ADIDORNAME', JSON.stringify(data))
+        onChange && onChange()
+    }
+
+    //全部接口刷新
+    const refresh = () => {
+        getCostTrendList.refresh()
+        getCostTopList.refresh()
+        getListForHour.refresh()
+        getMinuteList.refresh()
+    }
+
+    // 接口自动刷新10分钟一次
+    useEffect(() => {
+        let time = setInterval(() => {
+            refresh()
+        }, 1000 * 60 * 10)
+        return () => {
+            clearInterval(time)
+        }
+    }, [])
+
+    //图形排列样式改变重新获取数据刷新图形
+    const set = useCallback((b) => {
+        setPx(b)
+        getCostTrendList.refresh()
+        getCostTopList.refresh()
+    }, [getCostTopList, getCostTrendList])
+
+    // 处理折线图数据
+    const timePickerHandle = async (values: any, formatString: [string, string]) => {
+        let res = await getCostTrendList.data
+        let data: any[] = [{ legendName: '消耗' }]
+        res?.data?.forEach((item: any) => {
+            data[0][item.trendUnit] = item.costOfUnit
+        })
+        if (values) {
+            let date = moment().format('YYYY-MM-DD')
+            let strTime = formatString[0]
+            let endTime = formatString[1]
+            let dateTimeStr = `${date} ${strTime}:00`
+            let dateTimeEnd = `${date} ${endTime}:00`
+            let { legendName, ...otherData } = data[0]
+            let newData: any = { legendName }
+            for (const key in otherData) {
+                if (Object.prototype.hasOwnProperty.call(otherData, key)) {
+                    const value = otherData[key];
+                    if (moment(dateTimeStr) <= moment(key) && moment(key) <= moment(dateTimeEnd)) {
+                        newData[key] = value
+                    }
+                }
+            }
+            setLineDis(() => [newData])
+        } else {
+            setLineDis(() => data)
+        }
+    }
+
+    const log = (value: any) => {
+        setAccountIdRule(value.account_id)
+        setAdgroupId(value.adgroup_id)
+        setAdgroupName(value.adgroup_name)
+        setLogVisible(true)
+    }
+
+    return <Space direction='vertical' style={{ width: '100%' }} className="monitor">
+        <Card hoverable bodyStyle={{ padding: '12px 16px' }}>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <Space>
+                    <Select
+                        showSearch
+                        value={queryForm.sysUserId}
+                        style={{ minWidth: 180, maxWidth: 250 }}
+                        mode='multiple'
+                        maxTagCount={1}
+                        allowClear
+                        placeholder="请选择投手"
+                        disabled={queryForm?.adgroup || queryForm?.accountId?.length > 0}
+                        onChange={(value: number[]) => {
+                            setQueryForm({ ...queryForm, sysUserId: value, pageNum: 1 })
+                            sessionStorage.setItem('SYSUSERID', value ? JSON.stringify(value) : '')
+                        }}
+                        filterOption={(input, option) =>
+                            (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        }
+                    >
+                        {getPicherList?.data?.map((item: { nickname: string, userId: number }, index: number) =>
+                            <Select.Option
+                                value={item.userId}
+                                key={item.userId + '' + index}
+                            >
+                                {item.nickname}
+                            </Select.Option>
+                        )}
+                    </Select>
+                    <Select
+                        showSearch
+                        mode='multiple'
+                        maxTagCount={1}
+                        value={queryForm.accountId}
+                        style={{ minWidth: 220, maxWidth: 250 }}
+                        allowClear
+                        placeholder="请选择广告账号"
+                        onChange={(value: number[]) => {
+                            setQueryForm({ ...queryForm, accountId: value, pageNum: 1 })
+                        }}
+                    >
+                        {getAdqAccountList?.data?.data?.map((item: { id: number, accountId: number }) => <Select.Option
+                            value={item.accountId}
+                            key={item.id}
+                        >
+                            {item.accountId}
+                        </Select.Option>)}
+                    </Select>
+                    <Input
+                        value={queryForm.campaign}
+                        placeholder="计划ID"
+                        onChange={(e) => {
+                            let value = e.target.value
+                            setQueryForm({ ...queryForm, campaign: value })
+                        }}
+                        allowClear
+                    />
+                    <Input
+                        value={queryForm.adgroup}
+                        placeholder="广告ID"
+                        onChange={(e) => {
+                            setQueryForm({ ...queryForm, adgroup: e.target.value })
+                        }}
+                        allowClear
+                    />
+                </Space>
+                <Space>
+                    <Tag onClick={() => setShowEacharts(!showEacharts)}>{showEacharts ? <><EyeInvisibleOutlined /> 隐藏</> : <><EyeOutlined /> 显示</>}</Tag>
+                    <Tag color="#2db7f5" onClick={refresh}><RedoOutlined />   刷新</Tag>
+                </Space>
+            </div>
+        </Card>
+        {showEacharts && <Card hoverable bodyStyle={{ padding: '12px 16px' }}>
+            <span style={{ position: 'absolute', top: 10, zIndex: 10 }}>
+                {px ?
+                    <Tooltip title='左右排列'><Tag color='#f50' onClick={() => { set(false) }}><ColumnWidthOutlined /><span style={{ fontSize: 10 }}>左右排列</span></Tag></Tooltip>
+                    :
+                    <Tooltip title='上下排列'><Tag color='#f50' onClick={() => { set(true) }} ><ColumnHeightOutlined /><span style={{ fontSize: 10 }}>上下排列</span></Tag></Tooltip>
+                }
+            </span>
+            <div className={!px ? 'charts' : 'charts charts100'}>
+                <div>
+                    <div className="selectTime">
+                        <Space>
+                            <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getCostTopList?.data?.reqTime}</span>
+                            <Radio.Group value={queryForm.totalTimeUnit} buttonStyle="solid" size='small' onChange={(e) => { setQueryForm({ ...queryForm, totalTimeUnit: e.target.value }) }}>
+                                <Radio.Button value="total">总</Radio.Button>
+                                <Radio.Button value="day">天</Radio.Button>
+                                <Radio.Button value="hour">小时</Radio.Button>
+                                <Radio.Button value="minute">5min</Radio.Button>
+                            </Radio.Group>
+                        </Space>
+                    </div>
+                    {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} />}
+                </div>
+                <div>
+                    <div className="selectTime">
+                        <Space>
+                            <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getCostTrendList?.data?.reqTime}</span>
+                            <Radio.Group value={queryForm.planTimeUnit} buttonStyle="solid" size='small' onChange={(e) => { setQueryForm({ ...queryForm, planTimeUnit: e.target.value }) }}>
+                                <Radio.Button value="hour">小时</Radio.Button>
+                                <Radio.Button value="minute">5min</Radio.Button>
+                            </Radio.Group>
+                            {queryForm.planTimeUnit === 'minute' && <TimePicker.RangePicker
+                                size="small"
+                                format='HH:mm'
+                                minuteStep={5}
+                                onChange={timePickerHandle}
+                            />}
+                        </Space>
+                    </div>
+                    {getCostTrendList?.loading ? <Spin /> : <LineMonitor style={{ width: '100%', height: '100%' }} series smooth data={lineDis} title={lineTitle} />}
+                </div>
+            </div>
+        </Card>}
+        <div className={'MYtable'}>
+            <TableData
+                refreshData={getList}
+                bodyStyle={{ padding: '12px 16px' }}
+                gutter={[0, 12]}
+                columns={columnsMonitor(planDetail, details, (value) => { setQueryForm({ ...queryForm, adgroup: value }); }, log)}
+                dataSource={getListForHour?.data?.data?.records}
+                loading={getListForHour?.loading}
+                ajax={getListForHour}
+                leftChild={
+                    <Space>
+                        <FilterQuery onChange={(data) => {
+                            setFilterQuery(data)
+                        }} />
+                    </Space>
+                }
+                myKey={'adgroup_id'}
+                fixed={{ left: 0, right: 2 }}
+                total={getListForHour?.data?.data?.total}
+                onChange={(props: any) => {
+                    let { sortData, pagination } = props
+                    let { current, pageSize } = pagination
+                    let newQueryForm = JSON.parse(JSON.stringify(queryForHour))
+                    newQueryForm.pageNum = current
+                    newQueryForm.pageSize = pageSize
+                    if (sortData && JSON.stringify('sortData') !== '{}') {
+                        let { field, order } = sortData   // descend 降序 大到小  ascend 升序 小到大
+                        if (order) {
+                            newQueryForm.sortColumn = field
+                            newQueryForm.sortAsc = order === 'ascend'
+                        } else {
+                            Object.keys(newQueryForm).forEach(key => {
+                                if (key === 'sortColumn' || key === 'sortAsc') {
+                                    delete newQueryForm[key]
+                                }
+                            })
+                        }
+                    } else {
+                        Object.keys(newQueryForm).forEach(key => {
+                            if (key === 'sortField' || key === 'sort') {
+                                delete newQueryForm[key]
+                            }
+                        })
+                    }
+                    setQueryForHour({ ...newQueryForm })
+                }}
+                page={queryForHour.pageNum}
+                pageSize={queryForHour.pageSize}
+                scroll={{ y: 750 }}
+                config={qiliangpaihanghour}
+                configName={configName}
+            />
+        </div>
+
+        {visible && <Details visible={visible} onClose={() => { setVisible(false) }} data={aId} />}
+
+        {logVisible && <RuleAccountLog accountId={accountIdRule} adgroupName={adgroupName} adgroupId={adgroupId} visible={logVisible} onClose={() => setLogVisible(false)}/>}
+    </Space>
+}
+
+
+export default React.memo(Monitor)

+ 1 - 1
src/pages/adMonitor/adMonitorList/planList.tsx

@@ -13,7 +13,7 @@ import columnsPlanList from './tablePlanListConfig'
 
 
 const PlanList: React.FC = () => {
-    const { getAllPlanList, getBookListAll, getAdqAccountList } = useModel('useAdMonitor.useMonitor')
+    const { getAllPlanList, getAdqAccountList } = useModel('useAdMonitor.useMonitor')
     const { getPicherList } = useModel('useOperating.useWxGroupList')
     // 变量开始
     const [queryForm, setQueryForm] = useState<allPlanProps>({ pageNum: 1, pageSize: 20, sysUserId: JSON.parse(sessionStorage.getItem('SYSUSERID') || '[]'), createStartTime: moment().subtract(3, 'days').format('YYYY-MM-DD'), createEndTime: moment().format('YYYY-MM-DD') }) // 搜索变量

+ 1 - 1
src/pages/adMonitor/adMonitorList/tableMonitorConfig.tsx

@@ -5,7 +5,7 @@ import { ColumnsType } from 'antd/lib/table'
 import React from 'react'
 import { ReactComponent as RocketSvg } from '@/assets/rocket.svg'
 import './index.less'
-import { CHUANGYIZHUANGTAI, CHUJIAFANGSHI, GOUMAILEIXING, GUANGGAOZHUANGTAI, TUIGUANGMUBIAO, YOUHUAMUBIAO } from './enum'
+import { CHUJIAFANGSHI, TUIGUANGMUBIAO, YOUHUAMUBIAO } from './enum'
 import Box from './components/box'
 import { GGStateData } from './data'
 function columnsMonitor(planDetail: (id: number) => void, getDetailList: (adId: any, accountId: any[]) => void, details: (id: number) => void, getMinuList: (id: number, accountId: any[]) => void, mode: string) {

+ 1067 - 0
src/pages/adMonitor/adMonitorList/tableMonitorConfig1.tsx

@@ -0,0 +1,1067 @@
+import useCopy from '@/Hook/useCopy'
+import { RiseOutlined } from '@ant-design/icons'
+import {  Progress, Space, Statistic } from 'antd'
+import { ColumnsType } from 'antd/lib/table'
+import React from 'react'
+import { ReactComponent as RocketSvg } from '@/assets/rocket.svg'
+import './index.less'
+import { CHUJIAFANGSHI, TUIGUANGMUBIAO, YOUHUAMUBIAO } from './enum'
+import Box from './components/box'
+import { GGStateData } from './data'
+import { copy } from '@/utils/utils'
+function columnsMonitor(planDetail: (id: number) => void, details: (id: number) => void, onChange: (value: any) => void, log: (value: any) => void) {
+    const { copy } = useCopy()
+    return function columns() {
+        let newArr: ColumnsType<any> = [
+            {
+                title: '时间',
+                dataIndex: 'day',
+                key: 'day',
+                align: 'center',
+                width: 120,
+                ellipsis: true
+            },
+            {
+                title: '广告名称',
+                dataIndex: 'adgroup_name',
+                key: 'adgroup_name',
+                align: 'center',
+                width: 170,
+                ellipsis: true,
+                render: (str, b) => {
+                    return <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { onChange(b.adgroup_id) }}>{str}</a>
+                }
+            },
+            {
+                title: '广告ID',
+                dataIndex: 'adgroup_id',
+                key: 'adgroup_id',
+                align: 'center',
+                width: 100,
+                ellipsis: true,
+                render: (a: any) => {
+                    return <a onClick={() => { copy(a) }} style={{ color: '#3946c3' }}>{a}</a>
+                }
+            },
+            {
+                title: '计划ID',
+                dataIndex: 'campaign_id',
+                key: 'campaign_id',
+                align: 'center',
+                width: 100,
+                ellipsis: true,
+                render: (a: any) => {
+                    return <a onClick={() => { copy(a) }} style={{ color: '#3946c3' }}>{a}</a>
+                }
+            },
+            {
+                title: '广告账户',
+                dataIndex: 'account_id',
+                key: 'account_id',
+                width: 70,
+                ellipsis: true,
+                align: 'center'
+            },
+            {
+                title: '投手',
+                dataIndex: 'put_user_name',
+                key: 'put_user_name',
+                align: 'center',
+                ellipsis: true,
+                width: 65
+            },
+            {
+                title: '创意预览',
+                dataIndex: 'creative_preivew',
+                key: 'creative_preivew',
+                width: 110,
+                align: 'center',
+                render: (a: any, b: any) => {
+                    return <Box b={a} />
+                }
+            },
+            {
+                title: '投放时间',
+                dataIndex: 'begin_date',
+                key: 'begin_date',
+                width: 110,
+                align: 'center',
+                ellipsis: true,
+                render(value, record) {
+                    return value + '~' + (record?.end_date === '1970-01-01' ? '长期投放' : record?.end_date)
+                },
+            },
+            {
+                title: '广告状态',
+                dataIndex: 'status',
+                key: 'status',
+                align: 'center',
+                width: 105,
+                ellipsis: true,
+                render: (a: any) => {
+                    return GGStateData[a] || '--'
+                }
+            },
+            {
+                title: '推广目标',
+                dataIndex: 'promoted_object_type',
+                key: 'promoted_object_type',
+                align: 'center',
+                width: 80,
+                ellipsis: true,
+                render: (a: any) => {
+                    return TUIGUANGMUBIAO[a]
+                }
+            },
+            {
+                title: '广告预算',
+                dataIndex: 'daily_budget',
+                key: 'daily_budget',
+                width: 110,
+                align: 'center',
+                ellipsis: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '出价方式',
+                dataIndex: 'bid_mode',
+                key: 'bid_mode',
+                align: 'center',
+                width: 80,
+                render: (a: any) => {
+                    return CHUJIAFANGSHI[a] || '--'
+                }
+            },
+            {
+                title: '当前出价',
+                dataIndex: 'bid_amount',
+                key: 'bid_amount',
+                width: 110,
+                align: 'center',
+                ellipsis: true,
+                render: (a: any) => {
+                    return <div style={a >= 500 ? { backgroundColor: 'rgba(255, 80, 82, .72)', height: 26, color: '#fff', display: 'flex', justifyContent: 'center', alignItems: 'center', fontWeight: 600 } : {}}><Statistic value={a || 0} valueStyle={a >= 500 ? { fontSize: 14 } : {}} /></div>
+                }
+            },
+            {
+                title: '广告总消耗',
+                dataIndex: 'cost_total',
+                key: 'cost_total',
+                align: 'center',
+                width: 100,
+                ellipsis: true,
+                sorter: true,
+                render: (a: any) => {
+                    return <div style={{ height: 26, position: 'relative' }}>
+                        <Progress
+                            strokeColor={{
+                                from: '#10c1e9',
+                                to: '#6892d0',
+                            }}
+                            status="active"
+                            showInfo={false}
+                            percent={a ? a / 30000 * 100 : 0}
+                        />
+                        <span style={{ position: 'absolute', left: 0, top: 2, bottom: 0, right: 0 }}><Statistic value={a || 0} valueStyle={a >= 30000 ? { color: '#000', fontWeight: 500 } : { fontWeight: 500 }} /></span>
+                    </div>
+                }
+            },
+            {
+                title: '今日消耗',
+                dataIndex: 'cost_day',
+                key: 'cost_day',
+                align: 'center',
+                width: 105,
+                sorter: true,
+                render: (a: any) => {
+                    return <div style={{ height: 26, position: 'relative' }}>
+                        <Progress
+                            strokeColor={{
+                                from: '#ff5900',
+                                to: '#ffd380',
+                            }}
+                            status="active"
+                            showInfo={false}
+                            percent={a ? a / 2000 * 100 : 0}
+                        />
+                        <span style={{ position: 'absolute', left: 0, top: 2, bottom: 0, right: 0 }}><Statistic value={a || 0} valueStyle={a >= 2000 ? { color: '#000', fontWeight: 500 } : { fontWeight: 500 }} /></span>
+                    </div>
+                }
+            },
+            {
+                title: '当前小时消耗',
+                dataIndex: 'cost_hour',
+                key: 'cost_hour',
+                align: 'center',
+                width: 110,
+                sorter: true,
+                render: (a: any) => {
+                    return <div style={{ height: 26, position: 'relative' }}>
+                        <Progress
+                            strokeColor={{
+                                from: '#e7a0f5',
+                                to: '#d161f7',
+                            }}
+                            status="active"
+                            showInfo={false}
+                            percent={a ? a / 300 * 100 : 0}
+                        />
+                        <span style={{ position: 'absolute', left: 0, top: 2, bottom: 0, right: 0 }}><Statistic value={a || 0} valueStyle={a >= 300 ? { color: '#000', fontWeight: 500, fontSize: 15 } : { fontWeight: 500, fontSize: 15 }} /></span>
+                    </div>
+                }
+            },
+            {
+                title: '前第1小时消耗',
+                dataIndex: 'cost_last_hour',
+                key: 'cost_last_hour',
+                align: 'center',
+                width: 110,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '前第2小时消耗',
+                dataIndex: 'cost_last_two_hour',
+                key: 'cost_last_two_hour',
+                align: 'center',
+                width: 80,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '前第3小时消耗',
+                dataIndex: 'cost_last_three_hour',
+                key: 'cost_last_three_hour',
+                align: 'center',
+                width: 110,
+                sorter: true,
+                render: (a: number) => {
+                    return <Statistic value={a || 0} />
+                },
+            },
+            {
+                title: '当前小时消耗差额',
+                dataIndex: 'cost_diff_before_hour',
+                key: 'cost_diff_before_hour',
+                align: 'center',
+                width: 125,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} valueStyle={a > 0 ? { color: 'red' } : { color: 'green' }} />
+                }
+            },
+            {
+                title: '前第1小时消耗差额',
+                dataIndex: 'cost_diff_before_two_hour',
+                key: 'cost_diff_before_two_hour',
+                align: 'center',
+                width: 125,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} valueStyle={a > 0 ? { color: 'red' } : { color: 'green' }} />
+                }
+            },
+            {
+                title: "前第2小时消耗差额",
+                dataIndex: 'cost_diff_before_three_hour',
+                key: 'cost_diff_before_three_hour',
+                align: 'center',
+                width: 125,
+                sorter: true,
+                render: (a: number) => {
+                    return <Statistic value={a || 0} valueStyle={a > 0 ? { color: 'red' } : { color: 'green' }} />
+                },
+            },
+            {
+                title: "前三小时消耗趋势",
+                dataIndex: 'cost_trend_last_three_hour',
+                key: 'cost_trend_last_three_hour',
+                align: 'center',
+                width: 75,
+                render: (a: number) => {
+                    return a > 0 ? <RiseOutlined style={{ color: 'red', fontWeight: 900, fontSize: 22 }} /> : '--'
+                },
+            },
+            {
+                title: '当前5min消耗流速',
+                dataIndex: 'cost_speed',
+                key: 'cost_speed',
+                align: 'center',
+                width: 115,
+                render: (a: any) => {
+                    a = a ? parseFloat(a.toFixed(2)) : 0
+                    return <div style={{ height: 26, position: 'relative' }}>
+                        <Progress
+                            strokeColor={{
+                                from: '#00DDFF',
+                                to: '#37A2FF',
+                            }}
+                            status="active"
+                            showInfo={false}
+                            percent={a ? a / 100 * 100 : 0}
+                        />
+                        <span style={{ position: 'absolute', left: 0, top: 2, bottom: 0, right: 0 }}><Statistic value={a || 0} valueStyle={a >= 100 ? { color: '#000', fontWeight: 500 } : { fontWeight: 500 }} /></span>
+                    </div>
+                }
+            },
+            {
+                title: '曝光量',
+                dataIndex: 'view_day',
+                key: 'view_day',
+                align: 'center',
+                width: 70,
+                sorter: true,
+                render: (a: number) => {
+                    return <Statistic value={a || 0} />
+                },
+            },
+            {
+                title: '千次曝光成本',
+                dataIndex: 'thousand_display_price_day',
+                key: 'thousand_display_price_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '点击量',
+                dataIndex: 'click_day',
+                key: 'click_day',
+                align: 'center',
+                width: 70,
+                sorter: true,
+                render: (a: number) => {
+                    return <Statistic value={a || 0} />
+                },
+            },
+            {
+                title: '点击均价',
+                dataIndex: 'cpc_day',
+                key: 'cpc_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '点击率',
+                dataIndex: 'ctr_day',
+                key: 'ctr_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '优化目标',
+                dataIndex: 'optimization_goal',
+                key: 'optimization_goal',
+                align: 'center',
+                width: 115,
+                render: (a: any) => {
+                    return YOUHUAMUBIAO[a] || '--'
+                }
+            },
+            {
+                title: '转化目标量',
+                dataIndex: 'conversions_count_day',
+                key: 'conversions_count_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '转化目标成本',
+                dataIndex: 'conversions_cost_day',
+                key: 'conversions_cost_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <div style={a > 500 ? { backgroundColor: '#efea5b', height: 26, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 600 } : {}}><Statistic value={a || 0} /></div>
+                }
+            },
+            {
+                title: '目标转化率',
+                dataIndex: 'conversions_rate_day',
+                key: 'conversions_rate_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '下单量',
+                dataIndex: 'order_count_day',
+                key: 'order_count_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            // {
+            //     title: '首日新增下单量',
+            //     dataIndex: 'first_dayOrder_count_day',
+            //     key: 'first_dayOrder_count_day',
+            //     align: 'center',
+            //     width: 115,
+            //     sorter: true,
+            //     render: (a: any) => {
+            //         return <Statistic value={a || 0} />
+            //     }
+            // },
+            {
+                title: '下单成本',
+                dataIndex: 'order_cost_day',
+                key: 'order_cost_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单率',
+                dataIndex: 'order_rate_day',
+                key: 'order_rate_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '下单金额',
+                dataIndex: 'order_amount_day',
+                key: 'order_amount_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            // {
+            //     title: '首日新增下单金额',
+            //     dataIndex: 'first_day_order_amount_day',
+            //     key: 'first_day_order_amount_day',
+            //     align: 'center',
+            //     width: 115,
+            //     sorter: true,
+            //     render: (a: any) => {
+            //         return <Statistic value={a || 0} />
+            //     }
+            // },
+            {
+                title: '下单客单价',
+                dataIndex: 'atv_day',
+                key: 'atv_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单ROI',
+                dataIndex: 'order_roi_day',
+                key: 'order_roi_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            // {
+            //     title: '首日新增下单ROI',
+            //     dataIndex: 'first_day_order_roi_day',
+            //     key: 'first_day_order_roi_day',
+            //     align: 'center',
+            //     width: 115,
+            //     sorter: true,
+            //     render: (a: any) => {
+            //         a = a ? parseFloat((a * 100).toFixed(2)) : 0
+            //         return a + '%'
+            //     }
+            // },
+            {
+                title: '公众号关注人数',
+                dataIndex: 'mp_follow_uv_day',
+                key: 'mp_follow_uv_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return a
+                }
+            },
+            {
+                title: '公众号关注率',
+                dataIndex: 'mp_follow_rate_day',
+                key: 'mp_follow_rate_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '公众号关注成本',
+                dataIndex: 'mp_follow_cost_day',
+                key: 'mp_follow_cost_day',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '加粉成本',
+                dataIndex: 'add_fans_cost_day',
+                key: 'add_fans_cost_day',
+                align: 'center',
+                width: 75,
+                // sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '公众号关注次数成本',
+                dataIndex: 'mp_follow_pv_cost_day',
+                key: 'mp_follow_pv_cost_day',
+                align: 'center',
+                width: 80,
+                // sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '加粉数',
+                dataIndex: 'add_fans_count_day',
+                key: 'add_fans_count_day',
+                align: 'center',
+                width: 75,
+                // sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '公众号关注次数',
+                dataIndex: 'mp_follow_pv_day',
+                key: 'mp_follow_pv_day',
+                align: 'center',
+                width: 75,
+                // sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '加企业微信客服人数',
+                dataIndex: 'scan_follow_uv_day',
+                key: 'scan_follow_uv_day',
+                align: 'center',
+                width: 80,
+                // sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '加企业微信客服成本',
+                dataIndex: 'scan_follow_cost_day',
+                key: 'scan_follow_cost_day',
+                align: 'center',
+                width: 80,
+                // sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '加企业微信客服率',
+                dataIndex: 'scan_follow_rate_day',
+                key: 'scan_follow_rate_day',
+                align: 'center',
+                width: 80,
+                // sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '操作',
+                dataIndex: 'event',
+                key: 'event',
+                align: 'center',
+                width: 180,
+                render: (a: string, b: any) => (<Space>
+                    <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', fontSize: 14 }} onClick={() => details(b)}>
+                        <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><RocketSvg /></span><a style={{ marginLeft: 4, color: '#1890ff', fontSize: 12 }}>详情</a>
+                    </div>
+                    <a onClick={() => log(b)}>告警日志</a>
+                    <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => window.open(`https://ad.qq.com/atlas/${b?.account_id}/admanage/adgroup?tab=adgroup&query={%22operation_status%22:[%22CALCULATE_STATUS_EXCLUDE_DEL%22],%22system_status%22:[],%22search_name%22:%22${b.adgroup_id}%22}`)} target="_blank">腾讯广告</a>
+                </Space>
+                )
+            }
+        ]
+        return newArr
+    }
+}
+
+let columnsList = () => {
+
+    return function columns() {
+        let newArr: ColumnsType<any> = [
+            {
+                title: '时间',
+                dataIndex: 'time',
+                key: 'time',
+                align: 'center',
+                width: 120,
+                sorter: true,
+                ellipsis: true
+            },
+            {
+                title: '数据更新时间',
+                dataIndex: 'create_time',
+                key: 'create_time',
+                align: 'center',
+                width: 120,
+                ellipsis: true
+            },
+            {
+                title: '广告ID',
+                dataIndex: 'adgroup_id',
+                key: 'adgroup_id',
+                align: 'center',
+                width: 100,
+                ellipsis: true,
+                render: (a: any) => {
+                    return <a onClick={() => { copy(a) }} style={{ color: '#3946c3' }}>{a}</a>
+                }
+            },
+            {
+                title: '广告账户',
+                dataIndex: 'account_id',
+                key: 'account_id',
+                width: 70,
+                ellipsis: true,
+                align: 'center'
+            },
+            {
+                title: '消耗',
+                dataIndex: 'cost',
+                key: 'cost',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '曝光量',
+                dataIndex: 'view',
+                key: 'view',
+                align: 'center',
+                width: 80
+            },
+            {
+                title: '千次曝光成本',
+                dataIndex: 'thousand_display_price',
+                key: 'thousand_display_price',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '点击量',
+                dataIndex: 'click',
+                key: 'click',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '点击率',
+                dataIndex: 'ctr',
+                key: 'ctr',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                }
+            },
+            {
+                title: '点击均价',
+                dataIndex: 'cpc',
+                key: 'cpc',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '不感兴趣点击次数',
+                dataIndex: 'no_interest_count',
+                key: 'no_interest_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '朋友圈视频播放次数',
+                dataIndex: 'video_play_count',
+                key: 'video_play_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+
+
+            {
+                title: '下载次数',
+                dataIndex: 'download_count',
+                key: 'download_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '安装次数',
+                dataIndex: 'install_count',
+                key: 'install_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '激活次数',
+                dataIndex: 'activated_count',
+                key: 'activated_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '公众号关注人数',
+                dataIndex: 'mp_follow_uv',
+                key: 'mp_follow_uv',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '公众号关注成本',
+                dataIndex: 'mp_follow_cost',
+                key: 'mp_follow_cost',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '公众号关注率',
+                dataIndex: 'mp_follow_rate',
+                key: 'mp_follow_rate',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                }
+            },
+            {
+                title: '公众号关注次数',
+                dataIndex: 'mp_follow_pv',
+                key: 'mp_follow_pv',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '公众号关注次数成本',
+                dataIndex: 'mp_follow_pv_cost',
+                key: 'mp_follow_pv_cost',
+                align: 'center',
+                width: 100,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+
+            {
+                title: '快应用添加次数',
+                dataIndex: 'add_quick_app_pv',
+                key: 'add_quick_app_pv',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '快应用添加成本',
+                dataIndex: 'add_quick_app_cost',
+                key: 'add_quick_app_cost',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '快应用添加率',
+                dataIndex: 'add_quick_app_rate',
+                key: 'add_quick_app_rate',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                }
+            },
+            {
+                title: '加企业微信客服人数',
+                dataIndex: 'scan_follow_uv',
+                key: 'scan_follow_uv',
+                align: 'center',
+                width: 100,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '加企业微信客服成本',
+                dataIndex: 'scan_follow_cost',
+                key: 'scan_follow_cost',
+                align: 'center',
+                width: 100,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '加企业微信客服率',
+                dataIndex: 'scan_follow_rate',
+                key: 'scan_follow_rate',
+                align: 'center',
+                width: 100,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                }
+            },
+
+
+            {
+                title: '首日新增下单量',
+                dataIndex: 'first_day_order_count',
+                key: 'first_day_order_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '首日新增下单金额',
+                dataIndex: 'first_day_order_amount',
+                key: 'first_day_order_amount',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '首日新增下单ROI',
+                dataIndex: 'first_day_order_roi',
+                key: 'first_day_order_roi',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                }
+            },
+            {
+                title: '订单量',
+                dataIndex: 'order_count',
+                key: 'order_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '订单金额',
+                dataIndex: 'order_amount',
+                key: 'order_amount',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单成本',
+                dataIndex: 'order_cost',
+                key: 'order_cost',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单率',
+                dataIndex: 'order_rate',
+                key: 'order_rate',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                }
+            },
+            {
+                title: '下单ROI',
+                dataIndex: 'order_roi',
+                key: 'order_roi',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                }
+            },
+            {
+                title: '客单价',
+                dataIndex: 'atv',
+                key: 'atv',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '转化量',
+                dataIndex: 'conversions_count',
+                key: 'conversions_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '转化成本',
+                dataIndex: 'conversions_cost',
+                key: 'conversions_cost',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '深度转化',
+                dataIndex: 'deep_conversions_count',
+                key: 'deep_conversions_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '转化率',
+                dataIndex: 'conversions_rate',
+                key: 'conversions_rate',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                }
+            },
+            {
+                title: '加粉数',
+                dataIndex: 'add_fans_count',
+                key: 'add_fans_count',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '加粉成本',
+                dataIndex: 'add_fans_cost',
+                key: 'add_fans_cost',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+        ]
+        return newArr
+    }
+
+}
+export {
+    columnsMonitor,
+    columnsList
+}

+ 489 - 0
src/pages/launchSystemNew/adq/ad/FilterQuery.tsx

@@ -0,0 +1,489 @@
+import { CloseOutlined, DownOutlined, FilterOutlined, SearchOutlined, UpOutlined } from "@ant-design/icons"
+import { Button, DatePicker, Form, Input, InputNumber, Popover, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import './index.less'
+import moment from "moment"
+import { useLocalStorageState } from "ahooks"
+import { AdStatusEnum, OptimizationGoalEnum, PromotedObjectType } from "@/services/launchAdq/enum"
+import { AdListProps } from "@/services/adMonitor/adMonitor"
+import { useModel } from "umi"
+// import { useModel } from "umi"
+
+type TypeProps = 'DatePicker' | 'Input' | 'InputNumber' | 'Select'
+interface QueryProps {
+    lable: string,
+    name: string,
+    type: TypeProps,
+    value: (data?: { value?: any, onChange?: (value?: any) => void }) => JSX.Element,
+    isValue?: boolean
+}
+
+interface Props {
+    queryForm: AdListProps,
+    setQueryForm: React.Dispatch<React.SetStateAction<AdListProps>>
+    onChange?: (data: any) => void
+    initialValues?: any
+}
+/**
+ * 请求体 AD 合集
+ * @returns 
+ */
+const FilterQuery: React.FC<Props> = ({ onChange, initialValues, queryForm, setQueryForm }) => {
+
+    /********************************/
+    const [form] = Form.useForm();
+    const [visible, setVisible] = useState<boolean>(false)
+    const [filterTrueList, setFilterTrueList] = useState<QueryProps[]>([])
+    const [message, setMessage] = useLocalStorageState('filterQueryContentAdMessage', '');
+
+    const { getPicherList } = useModel('useOperating.useWxGroupList')
+    /********************************/
+
+    useEffect(() => {
+        getPicherList.run()
+    }, [])
+
+    const queryList: QueryProps[] = [
+        {
+            lable: '广告创建时间',
+            name: 'adCreateTime',
+            type: 'DatePicker',
+            value: (params) => <DatePicker.RangePicker {...params} />
+        },
+        // {
+        //     lable: '投手',
+        //     name: 'sysUserIds',
+        //     type: 'Select',
+        //     value: (params) => <Select
+        //         showSearch
+        //         style={{ width: '100%', minWidth: 130 }}
+        //         allowClear
+        //         mode="multiple"
+        //         placeholder="请选择投手"
+        //         filterOption={(input, option) =>
+        //             (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
+        //         }
+        //         {...params}
+        //     >
+        //         {getPicherList?.data?.map((item: { nickname: string, userId: number }, index: number) =>
+        //             <Select.Option
+        //                 value={item.userId}
+        //                 key={item.userId + '' + index}
+        //             >
+        //                 {item.nickname}
+        //             </Select.Option>
+        //         )}
+        //     </Select>
+        // },
+        {
+            lable: '广告账号',
+            name: 'accountIdStr',
+            type: 'Input',
+            value: (params) => <Input placeholder="请输入广告账号(多个,隔开)" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '广告名称',
+            name: 'adgroupName',
+            type: 'Input',
+            value: (params) => <Input placeholder="请输入广告名称" style={{ width: '100%' }} {...params} />
+        },
+        // {
+        //     lable: '广告ID',
+        //     name: 'adgroupIdStr',
+        //     type: 'Input',
+        //     value: (params) => <Input placeholder="请输入广告ID(多个,隔开)" style={{ width: '100%' }} {...params} />
+        // },
+        {
+            lable: '推广目标名称',
+            name: 'promotedObjectName',
+            type: 'Input',
+            value: (params) => <Input placeholder="请输入推广目标名称" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '腾讯账号备注',
+            name: 'accountMemo',
+            type: 'Input',
+            value: (params) => <Input placeholder="请输入腾讯账号备注" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '本地账号备注',
+            name: 'accountRemark',
+            type: 'Input',
+            value: (params) => <Input placeholder="请输入本地账号备注" style={{ width: '100%' }} {...params} />
+        },
+        // {
+        //     lable: '推广目标',
+        //     name: 'promotedObjectType',
+        //     type: 'Select',
+        //     value: (params) => <Select
+        //         placeholder='推广目标'
+        //         style={{ width: '100%', minWidth: 130 }}
+        //         showSearch
+        //         filterOption={(input: any, option: any) =>
+        //             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+        //         }
+        //         allowClear
+        //         {...params}
+        //     >
+        //         {Object.keys(PromotedObjectType).map(key => {
+        //             return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
+        //         })}
+        //     </Select>
+        // },
+        {
+            lable: '优化目标',
+            name: 'optimizationGoal',
+            type: 'Select',
+            value: (params) => <Select
+                placeholder='优化目标'
+                style={{ width: '100%', minWidth: 130 }}
+                showSearch
+                allowClear
+                filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                }
+                {...params}
+            >
+                {Object.keys(OptimizationGoalEnum).map(key => <Select.Option value={key} key={key}>{OptimizationGoalEnum[key]}</Select.Option>)}
+            </Select>
+        },
+        {
+            lable: '是否深度优化',
+            name: 'isDeepConversionSpec',
+            type: 'Select',
+            value: (params) => <Select
+                placeholder='是否深度优化?'
+                style={{ width: '100%', minWidth: 130 }}
+                showSearch
+                allowClear
+                filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                }
+                {...params}
+            >
+                <Select.Option value={true}>是</Select.Option>
+                <Select.Option value={false}>否</Select.Option>
+            </Select>
+        },
+        // {
+        //     lable: '已删除?',
+        //     name: 'isDeleted',
+        //     type: 'Select',
+        //     value: (params) => <Select
+        //         placeholder='已删除?'
+        //         style={{ width: '100%', minWidth: 130 }}
+        //         showSearch
+        //         allowClear
+        //         filterOption={(input: any, option: any) =>
+        //             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+        //         }
+        //         {...params}
+        //     >
+        //         <Select.Option value={true}>是</Select.Option>
+        //         <Select.Option value={false}>否</Select.Option>
+        //     </Select>
+        // },
+        // {
+        //     lable: '广告状态',
+        //     name: 'status',
+        //     type: 'Select',
+        //     value: (params) => <Select
+        //         placeholder='广告状态'
+        //         mode="multiple"
+        //         style={{ width: '100%', minWidth: 130 }}
+        //         showSearch
+        //         filterOption={(input: any, option: any) =>
+        //             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+        //         }
+        //         allowClear
+        //         {...params}
+        //     >
+        //         {Object.keys(AdStatusEnum).map(key => {
+        //             return <Select.Option value={key} key={key}>{AdStatusEnum[key]}</Select.Option>
+        //         })}
+        //     </Select>
+        // },
+        {
+            lable: '最低今日转化数',
+            name: 'conversionsCountDayMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低今日转化数" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最低总转化数',
+            name: 'conversionsCountTotalMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低总转化数" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最低今日消耗',
+            name: 'costDayMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低今日消耗" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最小总消耗',
+            name: 'costTotalMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最小总消耗" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最低今日千次曝光成本',
+            name: 'thousandDisplayPriceDayMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低今日千次曝光成本" style={{ width: '100%' }} {...params} />
+        },
+        {
+            lable: '最低总千次曝光成本',
+            name: 'thousandDisplayPriceTotalMin',
+            type: 'InputNumber',
+            value: (params) => <InputNumber placeholder="请输入最低总千次曝光成本" style={{ width: '100%' }} {...params} />
+        },
+    ]
+
+    useEffect(() => {
+        setFiled(message)
+    }, [message])
+
+    const setFiled = (message?: string) => {
+        if (message) {
+            let newFiter: any = []
+            message.split(',').forEach(key => {
+                newFiter.push(queryList.find(item => item.name === key))
+            })
+            console.log('==========>', newFiter)
+            setFilterTrueList(newFiter)
+        } else {
+            setFilterTrueList([])
+        }
+    }
+
+    const onFinish = async () => {
+        let newAllValue = await form.validateFields()
+        if (newAllValue?.adCreateTime && newAllValue?.adCreateTime?.length > 0) {
+            newAllValue.adCreateTimeMin = moment(newAllValue?.adCreateTime[0]).format('YYYY-MM-DD')
+            newAllValue.adCreateTimeMax = moment(newAllValue?.adCreateTime[1]).format('YYYY-MM-DD')
+            delete newAllValue?.adCreateTime
+        }
+        onChange?.(newAllValue)
+    }
+
+    return <div id="filterQueryContentAd">
+        <div style={{ width: '100%', display: 'flex', gap: 8 }}>
+            <Select
+                showSearch
+                maxTagCount={1}
+                style={{ minWidth: 140 }}
+                allowClear
+                mode="multiple"
+                placeholder="请选择投手"
+                filterOption={(input, option) =>
+                    (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }
+                value={queryForm.sysUserIds}
+                onChange={(value) => setQueryForm({ ...queryForm, sysUserIds: value, pageNum: 1 })}
+            >
+                {getPicherList?.data?.map((item: { nickname: string, userId: number }, index: number) =>
+                    <Select.Option
+                        value={item.userId}
+                        key={item.userId + '' + index}
+                    >
+                        {item.nickname}
+                    </Select.Option>
+                )}
+            </Select>
+            <Select
+                placeholder='广告状态'
+                mode="multiple"
+                maxTagCount={1}
+                style={{ minWidth: 140 }}
+                showSearch
+                filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                }
+                allowClear
+                value={queryForm.status}
+                onChange={(e) => {
+                    setQueryForm({ ...queryForm, status: e, pageNum: 1 })
+                }}
+            >
+                {Object.keys(AdStatusEnum).map(key => {
+                    return <Select.Option value={key} key={key}>{AdStatusEnum[key]}</Select.Option>
+                })}
+            </Select>
+            <Input placeholder="请输入广告ID(多个,隔开)" style={{ width: 140 }} value={queryForm.adgroupIdStr} onChange={(e) => setQueryForm({ ...queryForm, adgroupIdStr: e.target.value, pageNum: 1 })} />
+            <Select
+                placeholder='已删除?'
+                style={{ minWidth: 130 }}
+                showSearch
+                allowClear
+                filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                }
+                value={queryForm.isDeleted}
+                onChange={(e) => {
+                    setQueryForm({ ...queryForm, isDeleted: e, pageNum: 1 })
+                }}
+            >
+                <Select.Option value={true}>是</Select.Option>
+                <Select.Option value={false}>否</Select.Option>
+            </Select>
+            <Select
+                placeholder='推广目标'
+                style={{ minWidth: 130 }}
+                showSearch
+                filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                }
+                allowClear
+                value={queryForm.promotedObjectType}
+                onChange={(e) => {
+                    setQueryForm({ ...queryForm, promotedObjectType: e, pageNum: 1 })
+                }}
+            >
+                {Object.keys(PromotedObjectType).map(key => {
+                    return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
+                })}
+            </Select>
+            <Popover
+                title={<Space direction="vertical" size={0}>
+                    <h3 style={{ fontWeight: 'bold', marginBottom: 0 }}>你可能找这类广告</h3>
+                </Space>}
+                trigger={'click'}
+                placement="bottomLeft"
+                onVisibleChange={(e) => setVisible(e)}
+                getPopupContainer={() => document.getElementById('filterQueryContentAd') as any}
+                content={<div style={{ width: 500, height: 400, overflowY: 'auto', padding: '10px 16px' }}>
+                    <Form
+                        name="FilterQueryAd"
+                        form={form}
+                        colon={false}
+                        layout="vertical"
+                        initialValues={initialValues}
+                        onValuesChange={(changedValues) => {
+                            console.log(changedValues)
+                            let messageArr = message ? message?.split(',') : []
+                            Object.keys(changedValues).forEach(key => {
+                                if (!(messageArr?.includes(key))) {
+                                    messageArr.push(key)
+                                    setMessage(messageArr.join(','))
+                                    // setFiled(messageArr.join(','))
+                                }
+                            })
+                        }}
+                        onFinish={onFinish}
+                    >
+                        {queryList.map(item => <Form.Item label={<strong>{item.lable}</strong>} name={item.name} key={item.name}>
+                            {item.value()}
+                        </Form.Item>)}
+                    </Form>
+                </div>}
+            >
+                <Button className="bt"><FilterOutlined />筛选 {visible ? <UpOutlined /> : <DownOutlined />}</Button>
+            </Popover>
+            <Button type="primary" onClick={onFinish} icon={<SearchOutlined />}>搜索</Button>
+            <Button onClick={() => {
+                form.resetFields();
+            }}>重置</Button>
+        </div>
+        <div className="selectedFilterAd">
+            {filterTrueList.map(item => {
+                let value = form.getFieldValue([item.name])
+                // if (item.type === 'DatePicker' && value && value?.length > 0) {
+                //     console.log(value, Array.isArray(value))
+                //     let start = new Date()
+                //     // let end = moment(value(1)).format('YYYY-MM-DD')
+                //     // value = start + '-' + end
+                // }
+                switch (item.name) {
+                    case 'status':
+                        value = AdStatusEnum[value]
+                        break
+                    case 'isDeleted':
+                        value = typeof value === 'boolean' ? value ? '是' : '否' : '请选择'
+                        break
+                    case 'isDeepConversionSpec':
+                        value = typeof value === 'boolean' ? value ? '是' : '否' : '请选择'
+                        break
+                    case 'optimizationGoal':
+                        value = OptimizationGoalEnum[value] || '请选择'
+                        break
+                    case 'promotedObjectType':
+                        value = PromotedObjectType[value] || '请选择'
+                        break
+                    case 'adCreateTime':
+                        value = value ? '已经选择时间' : '请选择时间'
+                        break
+                    default:
+                        value = value || '请输入'
+                }
+                return <Popover
+                    placement="bottom"
+                    trigger={'click'}
+                    destroyTooltipOnHide={true}
+                    content={<PopoverSetValue
+                        type={item.type}
+                        dataEntry={item.value}
+                        value={form.getFieldValue([item.name])}
+                        onChange={(value) => {
+                            form.setFieldsValue({ [item.name]: value })
+                            onFinish()
+                        }}
+                    />}
+                    title={item.lable}
+                    key={item.name}
+                >
+                    <div >
+                        <div className="text">
+                            {item.lable}:{value || '请选择'}
+                        </div>
+                        <DownOutlined style={{ fontSize: 10, marginRight: 6 }} />
+                        <div className="close" onClick={() => {
+                            form.setFieldsValue({ [item.name]: undefined })
+                            let messageArr = message?.split(',') || []
+                            messageArr = messageArr.filter(key => key !== item.name)
+                            let messageStr = messageArr.length > 0 ? messageArr.join(',') : undefined
+                            setMessage(messageStr)
+                            // setFiled(messageStr)
+                        }}><CloseOutlined /></div>
+                    </div>
+                </Popover>
+            })}
+        </div>
+    </div>
+}
+
+export default React.memo(FilterQuery)
+
+interface PopoverSetValueProps {
+    type: TypeProps,
+    dataEntry: (data?: { value?: any, onChange?: (value?: any) => void }) => JSX.Element,
+    value?: any
+    onChange?: (value?: any) => void
+}
+const PopoverSetValue: React.FC<PopoverSetValueProps> = ({ type, dataEntry, value, onChange }) => {
+
+    /*************************/
+    const [data, setData] = useState<any>()
+    /*************************/
+
+    useEffect(() => {
+        setData(value)
+    }, [value])
+
+    return <Space>
+        {dataEntry({
+            value: data,
+            onChange(value) {
+                if (['Input'].includes(type)) {
+                    setData(value.target.value)
+                } else {
+                    setData(value)
+                }
+            },
+        })}
+        <Button type="primary" onClick={() => {
+            onChange?.(data)
+        }}>确定</Button>
+    </Space>
+}

+ 128 - 0
src/pages/launchSystemNew/adq/ad/adExpandedRowRender.tsx

@@ -0,0 +1,128 @@
+import useEcharts from "@/Hook/useEcharts"
+import { GetColumnTrendProps, getAdgroupsDetailsApi } from "@/services/adMonitor/adMonitor"
+import { Card, Descriptions, Empty, Popover, Select, Space, Spin, Typography } from "antd"
+import React, { useEffect, useMemo, useState } from "react"
+import { useModel } from "umi"
+import './index.less'
+import { useAjax } from "@/Hook/useAjax"
+import TimeSeriesLook from "./timeSeriesLook"
+import { AdStatusEnum, BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType } from "@/services/launchAdq/enum"
+import Box from "@/pages/adMonitor/adMonitorList/components/box"
+
+
+const AdExpandedRowRender: React.FC<{ data: any, scrollLeft: number, width?: number }> = ({ data, scrollLeft, width = 0 }) => {
+
+    const { getColumnTrend } = useModel('useAdMonitor.useMonitor')
+    const getAdgroupsDetails = useAjax((params) => getAdgroupsDetailsApi(params), { formatResult: true })
+    const [queryColumnTrend, setQueryColumnTrend] = useState<GetColumnTrendProps>({ accountId: data.account_id, adgroupId: data.adgroup_id, trendColumns: ['view', 'cost'], timeUnit: 'hour' })
+    const { LineMonitor } = useEcharts()
+    const [lineDis, setLineDis] = useState<any[]>([])
+
+    useEffect(() => {
+        getAdgroupsDetails.run({ accountId: data.account_id, adgroupId: data.adgroup_id })
+    }, [data])
+
+    useEffect(() => {
+        getCTList()
+    }, [queryColumnTrend])
+
+    const getCTList = () => {
+        getColumnTrend.run(queryColumnTrend).then(res => {
+            if (res?.data) {
+                let spendData: any = { legendName: '花费' }
+                let exposureData: any = { legendName: '曝光次数' }
+                res?.data?.forEach((item: { day: string, trend_unit: string, view: string, cost: string }) => {
+                    spendData[item?.trend_unit] = item?.cost
+                    exposureData[item?.trend_unit] = item?.view
+                });
+                setLineDis(() => [spendData, exposureData])
+            }
+        })
+    }
+
+    const AdContent = useMemo(() => {
+        if (getAdgroupsDetails?.data?.data) {
+            const { adgroupName, bidAmount, bidMode, optimizationGoal, rejectMessageList, status, targetingTranslation, timeSeries, firstDayBeginTime, dailyBudget, bidStrategy,
+            } = getAdgroupsDetails?.data?.data
+            return <Spin spinning={getAdgroupsDetails.loading}>
+                <Descriptions column={2} size="small" colon={false}>
+                    <Descriptions.Item labelStyle={{ width: 100, flex: '0 0 auto' }} label="广告名称">
+                        <div style={{ width: (width - 20) / 4 }}>
+                            <Typography.Text ellipsis={{ tooltip: true }}>{adgroupName}</Typography.Text>
+                        </div>
+                    </Descriptions.Item>
+                    <Descriptions.Item labelStyle={{ width: 100 }} label="状态">
+                        {rejectMessageList?.filter((str: any) => str)?.length > 0 ? <Popover
+                            style={{ width: 500 }}
+                            overlayStyle={{ width: 500, fontSize: 12 }}
+                            placement="left"
+                            content={rejectMessageList?.map((str: string, eq: number) => {
+                                return str ? <><strong style={{ fontSize: 13 }}>{eq + 1}:</strong>{str}<br /></> : ""
+                            })}>
+                            {AdStatusEnum[status]}
+                        </Popover> :
+                            AdStatusEnum[status]}
+                    </Descriptions.Item>
+                    <Descriptions.Item labelStyle={{ width: 100 }} label="出价">{`${BidModeEnum[bidMode]} ${bidAmount}元/${bidMode === 'BID_MODE_CPM' ? '千次曝光' : bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[optimizationGoal]}`}</Descriptions.Item>
+                    <Descriptions.Item label="投放时间" labelStyle={{ width: 100 }}>{<TimeSeriesLook timeSeries={timeSeries} />}</Descriptions.Item>
+                    <Descriptions.Item label="定向" labelStyle={{ width: 100, flex: '0 0 auto' }} span={2}><div style={{ width: '98%' }}>
+                        <Typography.Paragraph ellipsis={{ tooltip: true, rows: 2 }}>{targetingTranslation}</Typography.Paragraph>
+                    </div></Descriptions.Item>
+                    <Descriptions.Item label="首日开始时间" labelStyle={{ width: 100 }}>{firstDayBeginTime}</Descriptions.Item>
+                    <Descriptions.Item label="日预算" labelStyle={{ width: 100 }}>{dailyBudget}</Descriptions.Item>
+                    <Descriptions.Item label="出价策略" labelStyle={{ width: 100 }}>{BidStrategyEnum[bidStrategy]}</Descriptions.Item>
+                </Descriptions>
+            </Spin>
+        }
+        return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+    }, [getAdgroupsDetails?.data?.data, getAdgroupsDetails.loading])
+
+    return <div style={{ width: width - 20, left: scrollLeft }} className="expandContent">
+        <Card
+            className="detail_card"
+            hoverable
+            style={{ width: (width - 20) / 2 }}
+        >
+            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px 0' }}>
+                <strong>广告</strong>
+                <Space align="center">
+                    <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getAdgroupsDetails?.data?.reqTime}</span>
+                    <a onClick={() => getAdgroupsDetails.refresh()} style={{ fontSize: 14 }}>刷新</a>
+                </Space>
+            </div>
+            <div style={{ padding: 16 }}>
+                {AdContent}
+            </div>
+        </Card>
+        <Card
+            className="detail_card"
+            hoverable
+            style={{ width: (width - 20) / 2 }}
+        >
+            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px 0' }}>
+                <strong>趋势</strong>
+                <Space align="center">
+                    <Select
+                        size="small"
+                        value={queryColumnTrend.timeUnit}
+                        style={{ width: 78 }}
+                        onChange={(e) => {
+                            setQueryColumnTrend({ ...queryColumnTrend, timeUnit: e })
+                        }}
+                    >
+                        <Select.Option value="day">天</Select.Option>
+                        <Select.Option value="hour">小时</Select.Option>
+                        <Select.Option value="minute">5min</Select.Option>
+                    </Select>
+                    <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getColumnTrend?.data?.reqTime}</span>
+                    <a onClick={() => getCTList()} style={{ fontSize: 14 }}>刷新</a>
+                </Space>
+            </div>
+            <div style={{ width: '100%', height: 260, textAlign: 'center' }}>
+                {getColumnTrend?.loading ? <Spin /> : <LineMonitor style={{ width: '100%', height: 260 }} series smooth data={lineDis} />}
+            </div>
+        </Card>
+    </div>
+}
+
+export default React.memo(AdExpandedRowRender)

+ 436 - 0
src/pages/launchSystemNew/adq/ad/adPlanList.tsx

@@ -0,0 +1,436 @@
+
+import { useAjax } from '@/Hook/useAjax'
+import { Col, Row, message, Space, Button, Switch, notification, Modal, Tooltip, Dropdown, Menu, Select, Input } from 'antd'
+import React, { useEffect, useCallback, useState, useRef } from 'react'
+import TableData from '../../components/TableData'
+import { putAdqAdgroupsSync, delListAdqAdgroupsApi, newEditAdqAdgroupsDataApi, editAdqAdgroupsDataApi, putAdqAdgroupsSyncBatch, putModifyCustomAudienceApi, getPutUserApi } from '@/services/launchAdq/adq'
+import { CopyOutlined, DeleteOutlined, DownOutlined, ExclamationCircleOutlined, FieldTimeOutlined, FormOutlined, PauseCircleOutlined, PlayCircleOutlined, QuestionCircleOutlined, SyncOutlined, TransactionOutlined } from '@ant-design/icons'
+import UpdateAd from './updateAd'
+import Copy from './copy'
+import { planAdConfig } from '../config'
+import Log from '../log'
+import SetEarlyWarning from '@/components/EarlyWarning/setEarlyWarning'
+import CrowdPackModal from '../../components/crowdPackModal'
+import './index.less'
+import tablePlanConfig from './tablePlanListConfig'
+import { AdListProps, getAdListApi } from '@/services/adMonitor/adMonitor'
+import FilterQuery from './FilterQuery'
+import Details from '@/pages/adMonitor/adMonitorList/components/Details'
+import AdExpandedRowRender from './adExpandedRowRender'
+import { useSize } from 'ahooks'
+import RuleAccountLog from '@/components/EarlyWarning/ruleAccountLog'
+
+const AdPlanList: React.FC<{ userId: string }> = (props) => {
+
+    /***********************/
+    const { userId } = props
+    const [selectedRows, setSelectedRows] = useState<any[]>([])
+    const [update, setUpdate] = useState<{ visible: boolean, title: string }>({ visible: false, title: '' })
+    const [model, setModel] = useState(true)
+    const [copyData, setCopyData] = useState<{ visible: boolean }>({ visible: false })
+    const [detailShow, setDetailShow] = useState<boolean>(false)
+    const [detailData, setDetailData] = useState<any>({})
+    const [czjlShow, setCzjlShow] = useState(false)
+    const [cpVisible, setCpVisible] = useState(false)
+    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, customAudienceList?: any[], excludedCustomAudienceList?: any[] }[]>([])
+    const [queryForm, setQueryForm] = useState<AdListProps>({ pageNum: 1, pageSize: 20, columns: [] })
+    const [filterForm, setFilterForm] = useState<AdListProps>()
+    const getAdList = useAjax((params) => getAdListApi(params), { formatResult: true })
+    const syncAjax = useAjax((adAccountId) => putAdqAdgroupsSync(adAccountId))
+    const delListAdqAdgroups = useAjax((params) => delListAdqAdgroupsApi(params))
+    const editAdqAdgroupsData = useAjax((params) => newEditAdqAdgroupsDataApi(params))
+    const editAdqAdgroups = useAjax((params) => editAdqAdgroupsDataApi(params))
+    const putModifyCustomAudience = useAjax((params) => putModifyCustomAudienceApi(params))
+    const putAdqAdgroupsSyncBatchApi = useAjax((params) => putAdqAdgroupsSyncBatch(params))
+    const getPutUser = useAjax((params) => getPutUserApi(params))
+
+    const [logVisible, setLogVisible] = useState<boolean>(false)
+    const [adgroupId, setAdgroupId] = useState<string>('')
+    const [adgroupName, setAdgroupName] = useState<string>('')
+    const [accountIdRule, setAccountIdRule] = useState<string>('')
+
+    const configName = '广告列表New'
+    const ref = useRef(null)
+    const size = useSize(ref)
+    const [scrollLeft, setScrollLeft] = useState<number>(0)
+    /************************/
+
+    useEffect(() => {
+        getList()
+    }, [filterForm, queryForm])
+
+    const getList = () => {
+        let message = localStorage.getItem(`myAdMonitorConfig1.0.0_${configName}`)
+        if (message) {
+            message = JSON.parse(message)
+        }
+        let columns: string[] = []
+        if (message && Array.isArray(message)) {
+            message.forEach((item: { serverIndex: any; dataIndex: string; }) => {
+                if (!['cz', 'cost_speed'].includes(item.dataIndex)) {
+                    columns.push(item?.serverIndex || 'adgroup_data.' + item.dataIndex)
+                }
+            })
+        } else {
+            planAdConfig.forEach((item: any) => {
+                item?.data?.forEach((d: { default: any, serverIndex: string, dataIndex: string }) => {
+                    if (d.default && !['cz', 'cost_speed'].includes(d.dataIndex)) {
+                        columns.push(d?.serverIndex || 'adgroup_data.' + d.dataIndex)
+                    }
+                })
+            })
+        }
+        getAdList.run({ ...queryForm, ...filterForm, columns })
+    }
+
+    useEffect(() => {
+        getPutUser.run({ userId })
+    }, [userId])
+
+    // 同步 
+    const sync = useCallback(() => {
+        let arr = [...new Set(selectedRows?.map(item => item.accountId))]
+        syncAjax.run({ accountIdList: arr }).then(res => {
+            res && getAdList.refresh()
+            res ? message.success('同步成功!') : message.error('同步失败!')
+
+        })
+    }, [getAdList, selectedRows])
+
+    /** 删除 */
+    const deleteHandle = (type: 0 | 1, adgroupId?: number) => {
+        const hide = message.loading('删除中。。。', 0)
+        delListAdqAdgroups.run({ adgroupIds: type === 1 ? selectedRows.map(item => item.adgroup_id) : [adgroupId] }).then(res => {
+            hide()
+            message.success('删除成功')
+            setSelectedRows([])
+            getAdList.refresh()
+        }).catch(() => hide())
+    }
+
+    /** 修改排期出价 */
+    const editScheduling = () => {
+        setUpdate({ visible: true, title: '批量修改' })
+    }
+    /** 修改排期出价 */
+    const editDeepConversion = () => {
+        setUpdate({ visible: true, title: '批量修改深度优化' })
+    }
+
+    // 单个启停
+    const onChange = () => {
+        getAdList.refresh()
+        setSelectedRows([])
+    }
+    // 批量启停
+    const adStatus = (type: 'play' | 'suspend') => {
+        let params: any = {}
+        if (type === 'play') {
+            params.configuredStatus = 'AD_STATUS_NORMAL'
+            params.adgroupIds = selectedRows.filter((item: { configuredStatus: string, adgroup_id: number }) => item.configuredStatus === 'AD_STATUS_SUSPEND').map(item => item.adgroup_id)
+        } else {
+            params.configuredStatus = 'AD_STATUS_SUSPEND'
+            params.adgroupIds = selectedRows.filter((item: { configuredStatus: string, adgroup_id: number }) => item.configuredStatus === 'AD_STATUS_NORMAL').map(item => item.adgroup_id)
+        }
+        if (params.adgroupIds.length === 0) {
+            message.warn(`所以账号都是${type === 'play' ? '启动' : '暂停'}状态,无需${type === 'play' ? '启动' : '暂停'}操作`)
+            return
+        }
+        editAdqAdgroupsData.run(params).then(res => {
+            message.success(`${type === 'play' ? '启动' : '暂停'}成功: ${res.success},失败: ${res.fail}`)//
+            if (res?.fail) {
+                notification.error({
+                    message: `${type === 'play' ? '启动' : '暂停'}失败`,
+                    description: `成功: ${res.success},修改失败${res.fail}条,失败的请到任务列表查看`,
+                    duration: 0
+                });
+            }
+            getAdList.refresh()
+            setSelectedRows([])
+        })
+    }
+    //同步广告
+    const syncAd = useCallback(() => {
+        const hide = message.loading('同步中。。。', 0)
+        putAdqAdgroupsSyncBatchApi.run({ adgroupIds: selectedRows?.map(item => item.adgroup_id) }).then(res => {
+            hide()
+            if (res) {
+                message.success('同步成功!')
+                getAdList.refresh()
+            }
+        }).catch(() => hide())
+    }, [selectedRows])
+    // 批量复制
+    const copyHandle = () => {
+        setCopyData({ visible: true })
+    }
+
+    const handleSave = (row: any) => {
+        const hide = message.loading(`广告“${row.adgroup_id}”广告名称修改成<${row.adgroup_name}>,修改中`, 0, () => {
+            message.success('修改成功');
+        });
+        editAdqAdgroups.run({ adgroupIds: [row.adgroup_id], adgroupName: row.adgroup_name }).then(res => {
+            message.success('修改广告名称成功')
+            getAdList.refresh()
+            hide()
+        })
+    }
+
+    const handleSaveDaily = (row: any) => {
+        console.log('row--->', row)
+        const hide = message.loading(`广告“${row.adgroup_id}”广告预算修改成<${row.daily_budget}元>,修改中`, 0, () => {
+            message.success('修改成功');
+        });
+        editAdqAdgroups.run({ adgroupIds: [row.adgroup_id], dailyBudget: row.daily_budget * 100 }).then(res => {
+            message.success('修改广告预算成功')
+            getAdList.refresh()
+            hide()
+        })
+    }
+
+    const details = (data: any) => {
+        setDetailData(data)
+        setDetailShow(true)
+    }
+
+    // 设置人群包
+    const setRqb = () => {
+        const { accountId } = selectedRows[0]
+        setAccountCreateLogs([{ id: accountId, adAccountId: accountId }])
+        setCpVisible(true)
+    }
+
+    // 确认提交人群包
+    const handleRqb = (value: any[]) => {
+
+        if ((value[0]?.customAudienceList && value[0]?.customAudienceList?.length > 0) || (value[0]?.excludedCustomAudienceList && value[0]?.excludedCustomAudienceList?.length > 0)) {
+            let { adAccountId, customAudienceList = [], excludedCustomAudienceList = [] } = value[0]
+            let adgroupIds = selectedRows.map((item: { adgroup_id: number }) => item.adgroup_id)
+            let customAudienceIds = customAudienceList.map((item: { id: number }) => item.id)
+            let excludedCustomAudienceIds = excludedCustomAudienceList.map((item: { id: number }) => item.id)
+
+            const hide = message.loading('正在修改。。。', 0)
+
+            setAccountCreateLogs([])
+            setCpVisible(false)
+            let params: any = { adAccountId, adgroupIds };
+            if (excludedCustomAudienceIds.length > 0) {
+                params['excludedCustomAudienceIds'] = excludedCustomAudienceIds
+            }
+            if (customAudienceIds?.length > 0) {
+                params['customAudienceIds'] = customAudienceIds
+            }
+            putModifyCustomAudience.run(params).then(res => {
+                hide()
+                setSelectedRows([])
+                message.success('修改成功,请到腾讯广告平台查看')
+            }).catch(err => hide())
+        } else {
+            message.error('请选择用户群')
+        }
+    }
+
+    useEffect(() => {
+        const headerBodyScroll = (e: any) => {
+            let el = document.querySelector(`.expandClassname .expendTable .ant-table-body`);
+            if (el) {
+                setScrollLeft(e.target.scrollLeft)
+            }
+        }
+        document.querySelector(`.expandClassname .expendTable .ant-table-body`)?.addEventListener('scroll', headerBodyScroll);
+        () => {
+            document.querySelector(`.expandClassname .expendTable .ant-table-body`)?.removeEventListener('scroll', headerBodyScroll);
+        }
+    }, [])
+
+    const log = (value: any) => {
+        setAccountIdRule(value.account_id)
+        setAdgroupId(value.adgroup_id)
+        setAdgroupName(value.adgroup_name)
+        setLogVisible(true)
+    }
+
+    return <div>
+        {/* 修改广告 */}
+        {update.visible && <UpdateAd
+            {...update}
+            selectedRows={selectedRows}
+            onChange={() => {
+                setUpdate({ visible: false, title: '' })
+                getAdList.refresh()
+                setSelectedRows([])
+            }}
+            onClose={() => { setUpdate({ visible: false, title: '' }) }}
+        />}
+        {/* 复制广告 */}
+        {copyData.visible && <Copy selectedRows={selectedRows} {...copyData} onClose={() => setCopyData({ visible: false })} onChange={() => { setCopyData({ visible: false }); getAdList.refresh(); setSelectedRows([]) }} />}
+        <Row gutter={[6, 6]} align='middle' style={{ marginBottom: 10 }}>
+            <FilterQuery
+                queryForm={queryForm}
+                setQueryForm={setQueryForm}
+                onChange={(value) => {
+                    setFilterForm({ ...value, pageNum: 1 })
+                }}
+            />
+        </Row>
+        <div ref={ref} className='expandClassname'>
+            <TableData
+                refreshData={getList}
+                isCard={false}
+                className='expendTable'
+                columns={() => tablePlanConfig(onChange, details, handleSave, handleSaveDaily, log)}
+                ajax={getAdList}
+                syncAjax={sync}
+                fixed={{ left: 2, right: 4 }}
+                dataSource={getAdList?.data?.data?.records}
+                loading={getAdList?.loading || syncAjax?.loading}
+                scroll={{ y: 560 }}
+                total={getAdList?.data?.data?.total}
+                page={getAdList?.data?.data?.current}
+                pageSize={getAdList?.data?.data?.size}
+                myKey={'adgroup_id'}
+                gutter={[0, 10]}
+                config={planAdConfig}
+                configName={configName}
+                leftChild={<Space direction='vertical'>
+                    <Row gutter={[10, 10]} align='middle'>
+                        <Col>
+                            <Switch checkedChildren="普通" unCheckedChildren="ROI" checked={model} onChange={(checked) => { setModel(checked); setSelectedRows([]) }} style={model ? {} : { background: '#67c23a' }} />
+                        </Col>
+                        {!model && <Col><Button type='primary' icon={<TransactionOutlined />} disabled={selectedRows.length === 0} onClick={editDeepConversion}>修改深度优化ROI</Button></Col>}
+                        <Col><Button type='primary' style={{ background: '#1890ff' }} icon={<CopyOutlined />} disabled={selectedRows.length === 0} onClick={copyHandle}>复制</Button></Col>
+                        <Col><Button type='primary' style={{ background: '#67c23a', borderColor: '#67c23a' }} loading={editAdqAdgroupsData.loading} icon={<PlayCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => adStatus('play')}>启动</Button></Col>
+                        <Col><Button type='primary' style={{ background: '#e6a23c', borderColor: '#e6a23c' }} loading={editAdqAdgroupsData.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => adStatus('suspend')}>暂停</Button></Col>
+                        <Col><SetEarlyWarning selectedRows={selectedRows} onChange={() => getAdList.refresh()} /></Col>
+                        <Col>
+                            <Dropdown overlay={<Menu
+                                onClick={(e) => {
+                                    switch (e.key) {
+                                        case '1':
+                                            editScheduling()
+                                            break
+                                        case '2':
+                                            setRqb()
+                                            break
+                                        case '3':
+                                            syncAd()
+                                            break
+                                        case '4':
+                                            Modal.confirm({
+                                                title: '删除',
+                                                content: '确定删除?',
+                                                icon: <ExclamationCircleOutlined />,
+                                                okType: 'danger',
+                                                onOk() {
+                                                    deleteHandle(1)
+                                                }
+                                            });
+                                            break
+                                    }
+                                }}
+                                items={[
+                                    {
+                                        key: '1',
+                                        disabled: selectedRows.length === 0,
+                                        label: <Button type="link" size='small' style={{ padding: 0 }} icon={<FieldTimeOutlined />} disabled={selectedRows.length === 0}>修改排期出价名称</Button>,
+                                    },
+                                    {
+                                        key: '2',
+                                        disabled: selectedRows.length > 0 ? !selectedRows.every((item: { accountId: number }) => item.accountId === selectedRows[0].accountId) : true,
+                                        label: <Button type="link" size='small' style={{ padding: 0 }} icon={<FormOutlined />} loading={putModifyCustomAudience.loading} disabled={selectedRows.length > 0 ? !selectedRows.every((item: { accountId: number }) => item.accountId === selectedRows[0].accountId) : true}>
+                                            修改人群包
+                                            <Tooltip title="2023/4/20 16:20:00刷新页面后平台创建的广告可修改,或者其它平台使用定向包创建广告可用">
+                                                <QuestionCircleOutlined />
+                                            </Tooltip>
+                                        </Button>,
+                                    },
+                                    {
+                                        key: '3',
+                                        disabled: selectedRows.length === 0,
+                                        label: <Button type="link" size='small' style={{ padding: 0 }} loading={putAdqAdgroupsSyncBatchApi.loading} icon={<SyncOutlined />} disabled={selectedRows.length === 0}>同步广告</Button>,
+                                    },
+                                    {
+                                        key: '4',
+                                        disabled: selectedRows.length === 0,
+                                        label: <Button danger type="link" size='small' style={{ padding: 0 }} loading={delListAdqAdgroups.loading} icon={<DeleteOutlined />} disabled={selectedRows.length === 0}>删除</Button>,
+                                    },
+                                ]} />}>
+                                <a onClick={(e) => e.preventDefault()}>
+                                    <Space>
+                                        更多
+                                        <DownOutlined />
+                                    </Space>
+                                </a>
+                            </Dropdown>
+                        </Col>
+                        <Col>
+                            <Button type='dashed' onClick={() => { setCzjlShow(true) }}>操作记录</Button>
+                        </Col>
+                    </Row>
+                </Space>}
+                rowSelection={{
+                    selectedRowKeys: selectedRows.map(item => item.adgroup_id.toString()),
+                    getCheckboxProps: (record: any) => ({
+                        disabled: model ?
+                            record.status === 'STATUS_DELETED' :
+                            record.status === 'STATUS_DELETED' ||
+                            !(!model &&
+                                JSON.parse(record?.deep_conversion_spec_json)?.deepConversionWorthSpec?.goal === 'GOAL_1DAY_PURCHASE_ROAS'
+                            )
+                    }),
+                    onSelect: (record: { adgroup_id: number, mpName: string }, selected: boolean) => {
+                        if (selected) {
+                            selectedRows.push({ ...record })
+                            setSelectedRows([...selectedRows])
+                        } else {
+                            let newSelectAccData = selectedRows.filter((item: { adgroup_id: number }) => item.adgroup_id !== record.adgroup_id)
+                            setSelectedRows([...newSelectAccData])
+                        }
+                    },
+                    onSelectAll: (selected: boolean, selectedRowss: { adgroup_id: number }[], changeRows: { adgroup_id: number }[]) => {
+                        if (selected) {
+                            let newSelectAccData = [...selectedRows]
+                            changeRows.forEach((item: { adgroup_id: number }) => {
+                                let index = newSelectAccData.findIndex((ite: { adgroup_id: number }) => ite.adgroup_id === item.adgroup_id)
+                                if (index === -1) {
+                                    newSelectAccData.push({ ...item })
+                                }
+                            })
+                            setSelectedRows([...newSelectAccData])
+                        } else {
+                            let newSelectAccData = selectedRows.filter((item: { adgroup_id: number }) => {
+                                let index = changeRows.findIndex((ite: { adgroup_id: number }) => ite.adgroup_id === item.adgroup_id)
+                                if (index !== -1) {
+                                    return false
+                                } else {
+                                    return true
+                                }
+                            })
+                            setSelectedRows([...newSelectAccData])
+                        }
+                    }
+                }}
+                onChange={(props: any) => {
+                    let { pagination } = props
+                    let { current, pageSize } = pagination
+                    setQueryForm({ ...queryForm, pageNum: current, pageSize })
+                }}
+                expandedRowRender={(data) => <AdExpandedRowRender data={data} scrollLeft={scrollLeft} width={size?.width} />}
+            />
+        </div>
+        {detailShow && <Details visible={detailShow} onClose={() => { setDetailShow(false) }} data={detailData} />}
+        {czjlShow && <Modal
+            visible={czjlShow}
+            onCancel={() => { setCzjlShow(false) }}
+            onOk={() => { setCzjlShow(false) }}
+            width={1200}
+            footer={null}
+            title={"广告操作记录"}
+        >
+            <Log {...props} />
+        </Modal>}
+        {cpVisible && <CrowdPackModal visible={cpVisible} data={accountCreateLogs} onClose={() => setCpVisible(false)} onChange={(e) => { handleRqb(e) }} />}
+        {logVisible && <RuleAccountLog accountId={accountIdRule} adgroupName={adgroupName} adgroupId={adgroupId} visible={logVisible} onClose={() => setLogVisible(false)} />}
+    </div>
+}
+export default AdPlanList

+ 90 - 0
src/pages/launchSystemNew/adq/ad/index.less

@@ -72,4 +72,94 @@
 .more {
     width: 500px;
     max-height: 200px;
+}
+
+#filterQueryContentAd {
+    width: 100%;
+
+    .ant-popover-inner-content {
+        padding: 0;
+    }
+
+    .bt {
+        border-radius: 6px !important;
+        padding: 4px 8px;
+    }
+}
+
+.selectedFilterAd {
+    overflow: auto;
+    display: flex;
+    gap: 10px;
+    max-width: 100%;
+    margin-top: 10px;
+
+    &::-webkit-scrollbar {
+        // display: none; /* Chrome Safari */
+        width: 1px;
+        height: 1px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+        border-radius: 4px;
+        -webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0);
+        background-color: #ddd;
+        background: rgba(0, 0, 0, 0);
+    }
+
+    &:hover::-webkit-scrollbar-thumb {
+        background: rgba(82, 82, 82, 0.3);
+        -webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.2);
+    }
+
+    >div {
+        max-width: 250px;
+        height: 32px;
+        display: flex;
+        align-items: center;
+        gap: 2px;
+        border: 1px solid #dfe1e6;
+        border-radius: 6px;
+        cursor: pointer;
+        background-color: #F2F4F7;
+        color: #313233;
+
+
+        .text {
+            width: calc(100% - 40px);
+            white-space: nowrap;
+            text-overflow: ellipsis;
+            overflow: hidden;
+            padding: 0 0 0 10px;
+        }
+
+        .close {
+            height: 32px;
+            width: 32px;
+            border-left: 1px solid #d9d9d9;
+            text-align: center;
+            line-height: 32px;
+            color: red;
+
+            &:hover {
+                color: #1890ff;
+            }
+        }
+    }
+}
+
+
+.expandClassname {
+    .ant-table-expanded-row>td {
+        position: relative;
+        height: 308px;
+
+        .expandContent {
+            position: absolute;
+            left: 4px;
+            top: 6px;
+            display: flex;
+            gap: 8px;
+        }
+    }
 }

+ 0 - 4
src/pages/launchSystemNew/adq/ad/index.tsx

@@ -12,7 +12,6 @@ import Copy from './copy'
 import PlanDetail from '@/pages/adMonitor/adMonitorList/components/planDetail'
 import { txAdConfig } from '../config'
 import Log from '../log'
-import EarlyWarning from '@/components/EarlyWarning'
 import SetEarlyWarning from '@/components/EarlyWarning/setEarlyWarning'
 import CrowdPackModal from '../../components/crowdPackModal'
 import './index.less'
@@ -581,9 +580,6 @@ const Ad: React.FC<Props> = (props) => {
                     <Col>
                         <Button type='dashed' onClick={() => { setCzjlShow(true) }}>操作记录</Button>
                     </Col>
-                    <Col>
-                        <EarlyWarning />
-                    </Col>
                     <Col>
                         <AdIdSearch userId={userId} onChange={(e) => {
                             getList({ ...queryFrom, pageNum: 1, adgroupIdList: e })

+ 10 - 43
src/pages/launchSystemNew/adq/ad/tableConfig.tsx

@@ -1,6 +1,6 @@
 import { AdStatusEnum, BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType } from '@/services/launchAdq/enum'
 import React from 'react'
-import { Badge, Popover, Space, Tooltip, Typography } from 'antd'
+import { Badge, Popover, Space } from 'antd'
 import Box from '@/pages/adMonitor/adMonitorList/components/box'
 import SwitchStatus from './switchStatus'
 import TimeSeriesLook from './timeSeriesLook'
@@ -12,19 +12,8 @@ function tableConfig(
     onChange: () => void,
     details: (data: any) => void,
     handleSave: (data: any) => void,
-    handleSaveDaily: (data: any) => void,
-    tableIdClick?: (props: {
-        activeKey: string,
-        parma: {
-            accountId?: string,//账户ID
-            campaignId?: string,//计划ID
-            adgroupId?: string,//广告ID
-            adcreativeId?: string,//创意ID
-            pageId?: string,//落地页ID
-            targetingId?: string,//定向ID
-
-        }
-    }) => void): any {
+    handleSaveDaily: (data: any) => void
+): any {
     return [
         {
             title: '启停',
@@ -47,10 +36,6 @@ function tableConfig(
             render: (a: string) => {
                 return <Space>
                     <a onClick={() => copy(a)} >{a}</a>
-                    {/* <a onClick={() => {
-                        tableIdClick({ activeKey: '1', parma: { accountId: a } })
-                    }}>{a} */}
-                    {/* </a> */}
                 </Space>
             }
         },
@@ -125,16 +110,6 @@ function tableConfig(
                 return PromotedObjectType[a]
             }
         },
-        // {
-        //     title: '广告优化目标类型',
-        //     dataIndex: 'optimizationGoal',
-        //     key: 'optimizationGoal',
-        //     align: 'center',
-        //     width: 70,
-        //     render: (a: string | number) => {
-        //         return OptimizationGoalEnum[a]
-        //     }
-        // },
         {
             title: '投放日期',
             dataIndex: 'beginDate',
@@ -234,16 +209,6 @@ function tableConfig(
             width: 140,
             ellipsis: true,
         },
-        // {
-        //     title: '客户设置的状态',
-        //     dataIndex: 'configuredStatus',
-        //     key: 'configuredStatus',
-        //     align: 'center',
-        //     width:130,
-        //     render:(a:string)=>{
-        //         return  <Badge status={a==='AD_STATUS_NORMAL' ? "processing" :"error" } text={ConfiguredStatusEnum[a]} />
-        //     }
-        // },
         {
             title: '是否已删除',
             dataIndex: 'isDeleted',
@@ -265,11 +230,14 @@ function tableConfig(
             render: (a: string, b: any) => {
                 return b?.rejectMessageList?.filter((str: any) => str)?.length > 0 ? <Popover
                     style={{ width: 500 }}
-                    overlayStyle={{ width: 500,fontSize:12 }}
+                    overlayStyle={{ width: 500, fontSize: 12 }}
                     placement="left"
-                    content={b?.rejectMessageList?.map((str: string,eq:number) => {
-                        return str ? <><strong style={{fontSize:13}}>{eq+1}:</strong>{str}<br /></> : ""
-                    })}>{AdStatusEnum[a]}</Popover> : AdStatusEnum[a]
+                    content={b?.rejectMessageList?.map((str: string, eq: number) => {
+                        return str ? <><strong style={{ fontSize: 13 }}>{eq + 1}:</strong>{str}<br /></> : ""
+                    })}>
+                    {AdStatusEnum[a]}
+                </Popover> :
+                    AdStatusEnum[a]
             }
         },
         {
@@ -305,7 +273,6 @@ function tableConfig(
             align: 'center',
             fixed: 'right',
             render: (a: any, b: any) => {
-                // return <a style={{ color: '#1890ff' }} onClick={() => window.open(`https://ad.qq.com/atlas/${b?.accountId}/admanage/adgroup?query={%22operation_status%22:[%22CALCULATE_STATUS_EXCLUDE_DEL%22],%22system_status%22:[],%22search_name%22:%22${b.adgroupId}%22}`)} target="_blank">腾讯广告</a>
                 return <a style={{ color: '#1890ff' }} onClick={() => window.open(`https://ad.qq.com/atlas/${b?.accountId}/admanage/adgroup?tab=adgroup&query={%22operation_status%22:[%22CALCULATE_STATUS_EXCLUDE_DEL%22],%22system_status%22:[],%22search_name%22:%22${b.adgroupId}%22}`)} target="_blank">腾讯广告</a>
             }
         },

+ 716 - 0
src/pages/launchSystemNew/adq/ad/tablePlanListConfig.tsx

@@ -0,0 +1,716 @@
+import { AdStatusEnum, BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType } from '@/services/launchAdq/enum'
+import React from 'react'
+import { Badge, Popover, Space, Statistic } from 'antd'
+import Box from '@/pages/adMonitor/adMonitorList/components/box'
+import SwitchStatus from './switchStatus'
+import TimeSeriesLook from './timeSeriesLook'
+import { ReactComponent as RocketSvg } from '@/assets/rocket.svg'
+import '../index.less'
+import { copy } from '@/utils/utils'
+import InputUpdate from './inputUpdate'
+import { ColumnsType } from 'antd/lib/table'
+import StatisticNull from '@/components/StatisticNull'
+function tablePlanConfig(
+    onChange: () => void,
+    details: (data: any) => void,
+    handleSave: (data: any) => void,
+    handleSaveDaily: (data: any) => void,
+    log: (data: any) => void
+): ColumnsType<any> {
+
+    let adArr: ColumnsType<any> = [
+        {
+            title: '启停',
+            dataIndex: 'configured_status',
+            key: 'configured_status',
+            align: 'center',
+            width: 40,
+            fixed: 'left',
+            render: (a: string, b: any) => {
+                return <SwitchStatus configuredStatus={a} isDeleted={b?.is_deleted} adgroupId={b?.adgroup_id} onChange={onChange} />
+            }
+        },
+        {
+            title: '所属账号',
+            dataIndex: 'account_id',
+            key: 'account_id',
+            align: 'center',
+            width: 80,
+            ellipsis: true,
+            render: (a: string) => {
+                return <Space>
+                    <a onClick={() => copy(a)} >{a}</a>
+                </Space>
+            }
+        },
+        {
+            title: '腾讯备注',
+            dataIndex: 'memo',
+            key: 'memo',
+            align: 'center',
+            width: 80,
+            ellipsis: true,
+        },
+        {
+            title: '本地备注',
+            dataIndex: 'remark',
+            key: 'remark',
+            align: 'center',
+            width: 80,
+            ellipsis: true,
+        },
+        {
+            title: '广告ID',
+            dataIndex: 'adgroup_id',
+            key: 'adgroup_id',
+            align: 'center',
+            width: 100,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <Space>
+                    <a onClick={() => copy(a)} >{a}</a>
+                </Space>
+            }
+        },
+        {
+            title: '所属计划ID',
+            dataIndex: 'campaign_id',
+            key: 'campaign_id',
+            align: 'center',
+            width: 100,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <Space >
+                    <a onClick={() => copy(a)} >{a}</a>
+                </Space>
+            }
+        },
+        {
+            title: '投手',
+            dataIndex: 'put_user_name',
+            key: 'put_user_name',
+            align: 'center',
+            width: 70,
+            ellipsis: true
+        },
+        {
+            title: '广告名称',
+            dataIndex: 'adgroup_name',
+            key: 'adgroup_name',
+            width: 280,
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return <InputUpdate title={a} dataIndex={'adgroup_name'} record={b} handleSave={handleSave} />
+            }
+        },
+        {
+            title: '推广目标类型',
+            dataIndex: 'promoted_object_type',
+            key: 'promoted_object_type',
+            align: 'center',
+            width: 75,
+            ellipsis: true,
+            render: (a: string | number) => {
+                return PromotedObjectType[a]
+            }
+        },
+        {
+            title: '投放日期',
+            dataIndex: 'begin_date',
+            key: 'begin_date',
+            align: 'center',
+            width: 150,
+            ellipsis: true,
+            render: (a: string, b: { end_date: string }) => {
+                return b?.end_date && b?.end_date !== '1970-01-01' ? a + '~' + b.end_date : a + '~' + '长期投放'
+            }
+        },
+        {
+            title: '投放时间',
+            dataIndex: 'time_series',
+            key: 'time_series',
+            align: 'center',
+            width: 55,
+            render: (a: string, b: { endDate: string }) => {
+                return <TimeSeriesLook timeSeries={a} />
+            }
+        },
+        {
+            title: '首日开始投放时间',
+            dataIndex: 'first_day_begin_time',
+            key: 'first_day_begin_time',
+            align: 'center',
+            width: 70,
+        },
+        {
+            title: '出价',
+            dataIndex: 'bid_amount',
+            key: 'bid_amount',
+            width: 140,
+            ellipsis: true,
+            render: (a: string, b: { bid_mode: string, optimization_goal: string }) => {
+                return `${b?.bid_mode ? BidModeEnum[b?.bid_mode] : ''} ${a}元/${b?.bid_mode === 'BID_MODE_CPM' ? '千次曝光' : b?.bid_mode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[b?.optimization_goal]}`
+            }
+        },
+        {
+            title: '深度优化ROI',
+            dataIndex: 'expected_roi',
+            key: 'expected_roi',
+            width: 70,
+            align: 'center',
+            ellipsis: true,
+            render: (a: string, b: { deep_conversion_spec_json: any }) => {
+                if (b?.deep_conversion_spec_json) {
+                    return JSON.parse(b?.deep_conversion_spec_json)?.deepConversionWorthSpec?.expectedRoi || '--'
+                } else {
+                    return '--'
+                }
+
+            }
+        },
+        {
+            title: '出价类型',
+            dataIndex: 'smart_bid_type',
+            key: 'smart_bid_type',
+            align: 'center',
+            width: 80,
+            ellipsis: true,
+            render: (a: string) => {
+                return a === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'
+            }
+        },
+        {
+            title: '出价策略',
+            dataIndex: 'bid_strategy',
+            key: 'bid_strategy',
+            align: 'center',
+            width: 70,
+            ellipsis: true,
+            render: (a: string) => {
+                return BidStrategyEnum[a]
+            }
+        },
+        {
+            title: '广告组日预算(元)',
+            dataIndex: 'daily_budget',
+            key: 'daily_budget',
+            align: 'center',
+            width: 70,
+            render: (a: string, b: any) => {
+                return <InputUpdate title={a} isNum={true} dataIndex={'daily_budget'} record={b} handleSave={handleSaveDaily} />
+            }
+        },
+        {
+            title: '是否开启自动版位功能',
+            dataIndex: 'automatic_site_enabled',
+            key: 'automatic_site_enabled',
+            align: 'center',
+            width: 80,
+            render: (a: any, b: any) => {
+                return a ? '开' : '关'
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'created_time',
+            key: 'created_time',
+            align: 'center',
+            width: 140,
+            ellipsis: true,
+        },
+        {
+            title: '是否已删除',
+            dataIndex: 'is_deleted',
+            key: 'is_deleted',
+            align: 'center',
+            width: 60,
+            render: (a: any, b: any) => {
+                return <Badge status={!a ? "processing" : "error"} text={a ? '是' : '否'} />
+            }
+        },
+        {
+            title: '广告状态',
+            dataIndex: 'status',
+            key: 'status',
+            align: 'center',
+            width: 70,
+            fixed: 'right',
+            ellipsis: true,
+            render: (a: string, b: any) => {
+                return b?.rejectMessageList?.filter((str: any) => str)?.length > 0 ? <Popover
+                    style={{ width: 500 }}
+                    overlayStyle={{ width: 500, fontSize: 12 }}
+                    placement="left"
+                    content={b?.rejectMessageList?.map((str: string, eq: number) => {
+                        return str ? <><strong style={{ fontSize: 13 }}>{eq + 1}:</strong>{str}<br /></> : ""
+                    })}>
+                    {AdStatusEnum[a]}
+                </Popover> :
+                    AdStatusEnum[a]
+            }
+        },
+        {
+            title: '创意预览',
+            dataIndex: 'creative_id',
+            key: 'creative_id',
+            width: 70,
+            align: 'center',
+            fixed: 'right',
+            render: (a: any, b: any) => {
+                return <Box b={b?.creative_preivew} />
+            }
+        },
+        {
+            title: '广告详情',
+            dataIndex: 'cost_speed',
+            key: 'cost_speed',
+            align: 'center',
+            width: 80,
+            className: 'padding2',
+            fixed: 'right',
+            render: (a: any, b: any) => {
+                return <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', lineHeight: 'normal', fontSize: 14 }}>
+                    <RocketSvg /> <a onClick={() => details(b)} style={{ marginLeft: 10 }}>详情</a>
+                </div>
+            }
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 150,
+            align: 'center',
+            fixed: 'right',
+            render: (a: any, b: any) => {
+                return <Space>
+                    <a onClick={() => log(b)}>告警日志</a>
+                    <a style={{ color: '#1890ff' }} onClick={() => window.open(`https://ad.qq.com/atlas/${b?.account_id}/admanage/adgroup?tab=adgroup&query={%22operation_status%22:[%22CALCULATE_STATUS_EXCLUDE_DEL%22],%22system_status%22:[],%22search_name%22:%22${b.adgroup_id}%22}`)} target="_blank">腾讯广告</a>
+                </Space>
+            }
+        }
+    ]
+
+    let adDataArr: ColumnsType<any> = [
+        {
+            title: '数据更新时间',
+            dataIndex: 'create_time',
+            key: 'create_time',
+            align: 'center',
+            width: 120,
+            ellipsis: true
+        },
+        {
+            title: '消耗',
+            dataIndex: 'cost_total',
+            key: 'cost_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='cost_total' />
+            }
+        },
+        {
+            title: '曝光量',
+            dataIndex: 'view_total',
+            key: 'view_total',
+            align: 'center',
+            width: 80,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='view_total' />
+            }
+        },
+        {
+            title: '千次曝光成本',
+            dataIndex: 'thousand_display_price_total',
+            key: 'thousand_display_price_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='thousand_display_price_total' />
+            }
+        },
+        {
+            title: '点击量',
+            dataIndex: 'click_total',
+            key: 'click_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='click_total' />
+            }
+        },
+        {
+            title: '点击率',
+            dataIndex: 'ctr_total',
+            key: 'ctr_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                if (b?.ctr_total !== undefined && b?.ctr_total !== null) {
+                    return <Statistic value={a ? a.toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                } else {
+                    return '--'
+                }
+            }
+        },
+        {
+            title: '点击均价',
+            dataIndex: 'cpc_total',
+            key: 'cpc_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='cpc_total' />
+            }
+        },
+        {
+            title: '不感兴趣点击次数',
+            dataIndex: 'no_interest_count_total',
+            key: 'no_interest_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='no_interest_count_total' />
+            }
+        },
+        {
+            title: '朋友圈视频播放次数',
+            dataIndex: 'video_play_count_total',
+            key: 'video_play_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='video_play_count_total' />
+            }
+        },
+
+
+        {
+            title: '下载次数',
+            dataIndex: 'download_count_total',
+            key: 'download_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='download_count_total' />
+            }
+        },
+        {
+            title: '安装次数',
+            dataIndex: 'install_count_total',
+            key: 'install_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='install_count_total' />
+            }
+        },
+        {
+            title: '激活次数',
+            dataIndex: 'activated_count_total',
+            key: 'activated_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='activated_count_total' />
+            }
+        },
+        {
+            title: '公众号关注人数',
+            dataIndex: 'mp_follow_uv_total',
+            key: 'mp_follow_uv_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='mp_follow_uv_total' />
+            }
+        },
+        {
+            title: '公众号关注成本',
+            dataIndex: 'mp_follow_cost_total',
+            key: 'mp_follow_cost_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='mp_follow_cost_total' />
+            }
+        },
+        {
+            title: '公众号关注率',
+            dataIndex: 'mp_follow_rate_total',
+            key: 'mp_follow_rate_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                if (b?.mp_follow_rate_total !== undefined && b?.mp_follow_rate_total !== null) {
+                    return <Statistic value={a ? a.toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                } else {
+                    return '--'
+                }
+            }
+        },
+        {
+            title: '公众号关注次数',
+            dataIndex: 'mp_follow_pv_total',
+            key: 'mp_follow_pv_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='mp_follow_pv_total' />
+            }
+        },
+        {
+            title: '公众号关注次数成本',
+            dataIndex: 'mp_follow_pv_cost_total',
+            key: 'mp_follow_pv_cost_total',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='mp_follow_pv_cost_total' />
+            }
+        },
+
+        {
+            title: '快应用添加次数',
+            dataIndex: 'add_quick_app_pv_total',
+            key: 'add_quick_app_pv_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='add_quick_app_pv_total' />
+            }
+        },
+        {
+            title: '快应用添加成本',
+            dataIndex: 'add_quick_app_cost_total',
+            key: 'add_quick_app_cost_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='add_quick_app_cost_total' />
+            }
+        },
+        {
+            title: '快应用添加率',
+            dataIndex: 'add_quick_app_rate_total',
+            key: 'add_quick_app_rate_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                if (b?.add_quick_app_rate_total !== undefined && b?.add_quick_app_rate_total !== null) {
+                    return <Statistic value={a ? a.toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                } else {
+                    return '--'
+                }
+            }
+        },
+        {
+            title: '加企业微信客服人数',
+            dataIndex: 'scan_follow_uv_total',
+            key: 'scan_follow_uv_total',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='scan_follow_uv_total' />
+            }
+        },
+        {
+            title: '加企业微信客服成本',
+            dataIndex: 'scan_follow_cost_total',
+            key: 'scan_follow_cost_total',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='scan_follow_cost_total' />
+            }
+        },
+        {
+            title: '加企业微信客服率',
+            dataIndex: 'scan_follow_rate_total',
+            key: 'scan_follow_rate_total',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                if (b?.scan_follow_rate_total !== undefined && b?.scan_follow_rate_total !== null) {
+                    return <Statistic value={a ? a.toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                } else {
+                    return '--'
+                }
+            }
+        },
+
+
+        {
+            title: '首日新增下单量',
+            dataIndex: 'first_day_order_count_total',
+            key: 'first_day_order_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='first_day_order_count_total' />
+            }
+        },
+        {
+            title: '首日新增下单金额',
+            dataIndex: 'first_day_order_amount_total',
+            key: 'first_day_order_amount_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='first_day_order_amount_total' />
+            }
+        },
+        {
+            title: '首日新增下单ROI',
+            dataIndex: 'first_day_order_roi_total',
+            key: 'first_day_order_roi_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                if (b?.first_day_order_roi_total !== undefined && b?.first_day_order_roi_total !== null) {
+                    return <Statistic value={a ? a.toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                } else {
+                    return '--'
+                }
+            }
+        },
+        {
+            title: '订单量',
+            dataIndex: 'order_count_total',
+            key: 'order_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='order_count_total' />
+            }
+        },
+        {
+            title: '订单金额',
+            dataIndex: 'order_amount_total',
+            key: 'order_amount_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='order_amount_total' />
+            }
+        },
+        {
+            title: '下单成本',
+            dataIndex: 'order_cost_total',
+            key: 'order_cost_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='order_cost_total' />
+            }
+        },
+        {
+            title: '下单率',
+            dataIndex: 'order_rate_total',
+            key: 'order_rate_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                if (b?.order_rate_total !== undefined && b?.order_rate_total !== null) {
+                    return <Statistic value={a ? a.toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                } else {
+                    return '--'
+                }
+            }
+        },
+        {
+            title: '下单ROI',
+            dataIndex: 'order_roi_total',
+            key: 'order_roi_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                if (b?.order_roi_total !== undefined && b?.order_roi_total !== null) {
+                    return <Statistic value={a ? a.toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                } else {
+                    return '--'
+                }
+            }
+        },
+        {
+            title: '客单价',
+            dataIndex: 'atv_total',
+            key: 'atv_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='atv_total' />
+            }
+        },
+        {
+            title: '转化量',
+            dataIndex: 'conversions_count_total',
+            key: 'conversions_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='conversions_count_total' />
+            }
+        },
+        {
+            title: '转化成本',
+            dataIndex: 'conversions_cost_total',
+            key: 'conversions_cost_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='conversions_cost_total' />
+            }
+        },
+        {
+            title: '深度转化',
+            dataIndex: 'deep_conversions_count_total',
+            key: 'deep_conversions_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='deep_conversions_count_total' />
+            }
+        },
+        {
+            title: '转化率',
+            dataIndex: 'conversions_rate_total',
+            key: 'conversions_rate_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                if (b?.conversions_rate_total !== undefined && b?.conversions_rate_total !== null) {
+                    return <Statistic value={a ? a.toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+                } else {
+                    return '--'
+                }
+            }
+        },
+        {
+            title: '加粉数',
+            dataIndex: 'add_fans_count_total',
+            key: 'add_fans_count_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='add_fans_count_total' />
+            }
+        },
+        {
+            title: '加粉成本',
+            dataIndex: 'add_fans_cost_total',
+            key: 'add_fans_cost_total',
+            align: 'center',
+            width: 90,
+            render: (a: any, b: any) => {
+                return <StatisticNull data={b} field='add_fans_cost_total' />
+            }
+        },
+    ]
+
+    return [
+        ...adArr,
+        ...adDataArr
+    ]
+}
+export default tablePlanConfig

+ 110 - 25
src/pages/launchSystemNew/adq/config.ts

@@ -1,36 +1,121 @@
 /**广告 */
 const txAdConfig = [
     {
-        label: '设置信息',
+        label: '广告详情',
         data: [
-            { title: '启停', dataIndex: 'configuredStatus', label: '设置信息', default: 1, width: 40 },
-            { title: '所属账号', dataIndex: 'accountId', label: '设置信息', default: 2, width: 75 },
-            { title: '腾讯备注', dataIndex: 'memo', label: '设置信息', default: 3, width: 80 },
-            { title: '本地备注', dataIndex: 'remark', label: '设置信息', default: 4, width: 80 },
-            { title: '广告ID', dataIndex: 'adgroupId', label: '设置信息', default: 5, width: 90 },
-            { title: '所属计划ID', dataIndex: 'campaignId', label: '设置信息', default: 6, width: 90 },
-            { title: '投手', dataIndex: 'putUserName', label: '设置信息', default: 7, width: 70 },
-            { title: '广告名称', dataIndex: 'adgroupName', label: '设置信息', default: 8, width: 280 },
-            { title: '推广目标类型', dataIndex: 'promotedObjectType', label: '设置信息', default: 9, width: 75 },
-            { title: '投放日期', dataIndex: 'beginDate', label: '设置信息', default: 10, width: 150 },
-            { title: '投放时间', dataIndex: 'timeSeries', label: '设置信息', default: 11, width: 55 },
-            { title: '首日开始投放时间', dataIndex: 'firstDayBeginTime', label: '设置信息', default: 12, width: 70 },
-            { title: '出价', dataIndex: 'bidAmount', label: '设置信息', default: 13, width: 140 },
-            { title: '深度优化ROI', dataIndex: 'expectedRoi', label: '设置信息', default: 14, width: 70 },
-            { title: '出价类型', dataIndex: 'smartBidType', label: '设置信息', default: 15, width: 80 },
-            { title: '广告组日预算(分)', dataIndex: 'dailyBudget', label: '设置信息', default: 16, width: 70 },
-            { title: '是否开启自动版位功能', dataIndex: 'automaticSiteEnabled', label: '设置信息', default: 17, width: 80 },
-            { title: '创建时间', dataIndex: 'createdTime', label: '设置信息', default: 18, width: 140 },
-            { title: '是否已删除', dataIndex: 'isDeleted', label: '设置信息', default: 19, width: 60 },
-            { title: '广告状态', dataIndex: 'status', label: '设置信息', default: 20, width: 70 },
-            { title: '创意预览', dataIndex: 'creativePreview', label: '设置信息', default: 21, width: 70 },
-            { title: '单位时间消耗速度', dataIndex: 'costSpeed', label: '设置信息', default: 22, width: 80 },
-            { title: '操作', dataIndex: 'cz', label: '设置信息', default: 23, width: 65 },
+            { title: '启停', dataIndex: 'configuredStatus', label: '广告详情', default: 1, width: 40 },
+            { title: '所属账号', dataIndex: 'accountId', label: '广告详情', default: 2, width: 75 },
+            { title: '腾讯备注', dataIndex: 'memo', label: '广告详情', default: 3, width: 80 },
+            { title: '本地备注', dataIndex: 'remark', label: '广告详情', default: 4, width: 80 },
+            { title: '广告ID', dataIndex: 'adgroupId', label: '广告详情', default: 5, width: 90 },
+            { title: '所属计划ID', dataIndex: 'campaignId', label: '广告详情', default: 6, width: 90 },
+            { title: '投手', dataIndex: 'putUserName', label: '广告详情', default: 7, width: 70 },
+            { title: '广告名称', dataIndex: 'adgroupName', label: '广告详情', default: 8, width: 280 },
+            { title: '推广目标类型', dataIndex: 'promotedObjectType', label: '广告详情', default: 9, width: 75 },
+            { title: '投放日期', dataIndex: 'beginDate', label: '广告详情', default: 10, width: 150 },
+            { title: '投放时间', dataIndex: 'timeSeries', label: '广告详情', default: 11, width: 55 },
+            { title: '首日开始投放时间', dataIndex: 'firstDayBeginTime', label: '广告详情', default: 12, width: 70 },
+            { title: '出价', dataIndex: 'bidAmount', label: '广告详情', default: 13, width: 140 },
+            { title: '深度优化ROI', dataIndex: 'expectedRoi', label: '广告详情', default: 14, width: 70 },
+            { title: '出价类型', dataIndex: 'smartBidType', label: '广告详情', default: 15, width: 80 },
+            { title: '广告组日预算(分)', dataIndex: 'dailyBudget', label: '广告详情', default: 16, width: 70 },
+            { title: '是否开启自动版位功能', dataIndex: 'automaticSiteEnabled', label: '广告详情', default: 17, width: 80 },
+            { title: '创建时间', dataIndex: 'createdTime', label: '广告详情', default: 18, width: 140 },
+            { title: '是否已删除', dataIndex: 'isDeleted', label: '广告详情', default: 19, width: 60 },
+            { title: '广告状态', dataIndex: 'status', label: '广告详情', default: 20, width: 70 },
+            { title: '创意预览', dataIndex: 'creativePreview', label: '广告详情', default: 21, width: 70 },
+            { title: '单位时间消耗速度', dataIndex: 'costSpeed', label: '广告详情', default: 22, width: 80 },
+            { title: '操作', dataIndex: 'cz', label: '广告详情', default: 23, width: 65 },
         ]
     }
 ]
 
+const planAdConfig = [
+    {
+        label: '广告详情',
+        data: [
+            { title: '启停', dataIndex: 'configured_status', serverIndex: 'adgroups.configured_status', label: '广告详情', default: 1, width: 40 },
+            { title: '所属账号', dataIndex: 'account_id', serverIndex: 'adgroups.account_id', label: '广告详情', default: 2, width: 75 },
+            { title: '腾讯备注', dataIndex: 'memo', serverIndex: 'ad_account.memo', label: '广告详情', default: 3, width: 80 },
+            { title: '本地备注', dataIndex: 'remark', serverIndex: 'ad_account.remark', label: '广告详情', default: 4, width: 80 },
+            { title: '广告ID', dataIndex: 'adgroup_id', serverIndex: 'adgroups.adgroup_id', label: '广告详情', default: 5, width: 90 },
+            { title: '所属计划ID', dataIndex: 'campaign_id', serverIndex: 'adgroups.campaign_id', label: '广告详情', default: 6, width: 90 },
+            { title: '投手', dataIndex: 'put_user_name', serverIndex: 'sys_user.put_user_name', label: '广告详情', default: 7, width: 70 },
+            { title: '广告名称', dataIndex: 'adgroup_name', serverIndex: 'adgroups.adgroup_name', label: '广告详情', default: 8, width: 280 },
+            { title: '推广目标类型', dataIndex: 'promoted_object_type', serverIndex: 'adgroups.promoted_object_type', label: '广告详情', default: 9, width: 75 },
+            { title: '投放日期', dataIndex: 'begin_date', serverIndex: 'adgroups.begin_date, adgroups.end_date', label: '广告详情', default: 10, width: 150 },
+            { title: '投放时间', dataIndex: 'time_series', serverIndex: 'adgroups.time_series', label: '广告详情', default: 11, width: 55 },
+            { title: '首日开始投放时间', dataIndex: 'first_day_begin_time', serverIndex: 'adgroups.first_day_begin_time', label: '广告详情', default: 12, width: 70 },
+            { title: '出价', dataIndex: 'bid_amount', serverIndex: 'adgroups.bid_amount,adgroups.bid_mode,adgroups.optimization_goal', label: '广告详情', default: 13, width: 140 },
+            { title: '深度优化ROI', dataIndex: 'expected_roi', serverIndex: 'adgroups.deep_conversion_spec_json', label: '广告详情', default: 14, width: 70 },
+            { title: '出价类型', dataIndex: 'smart_bid_type', serverIndex: 'adgroups.smart_bid_type', label: '广告详情', default: 15, width: 80 },
+            { title: '出价策略', dataIndex: 'bid_strategy', serverIndex: 'adgroups.bid_strategy', label: '广告详情', default: 16, width: 80 },
+            { title: '广告组日预算(元)', dataIndex: 'daily_budget', serverIndex: 'adgroups.daily_budget', label: '广告详情', default: 17, width: 70 },
+            { title: '是否开启自动版位功能', dataIndex: 'automatic_site_enabled', serverIndex: 'adgroups.automatic_site_enabled', label: '广告详情', default: 18, width: 80 },
+            { title: '创建时间', dataIndex: 'created_time', serverIndex: 'adgroups.created_time', label: '广告详情', default: 19, width: 140 },
+            { title: '是否已删除', dataIndex: 'is_deleted', serverIndex: 'adgroups.is_deleted', label: '广告详情', default: 20, width: 60 },
+            { title: '广告状态', dataIndex: 'status', serverIndex: 'adgroups.status', label: '广告详情', default: 21, width: 70 },
+            { title: '创意预览', dataIndex: 'creative_id', serverIndex: 'adgroup_data.creative_id', label: '广告详情', default: 22, width: 70 },
+            { title: '广告详情', dataIndex: 'cost_speed', label: '广告详情', default: 23, width: 80 },
+            { title: '操作', dataIndex: 'cz', label: '广告详情', default: 24, width: 136 },
+        ]
+    },
+    {
+        label: '广告消耗信息',
+        data: [
+            { title: '数据更新时间', dataIndex: 'create_time', label: '广告消耗信息', width: 85 },
+            { title: '消耗', dataIndex: 'cost_total', label: '广告消耗信息', width: 90 },
+            { title: '曝光量', dataIndex: 'view_total', label: '广告消耗信息', width: 90 },
+            { title: '千次曝光成本', dataIndex: 'thousand_display_price_total', label: '广告消耗信息', width: 90 },
+            { title: '点击量', dataIndex: 'click_total', label: '广告消耗信息', width: 90 },
+            { title: '点击率', dataIndex: 'ctr_total', label: '广告消耗信息', width: 90 },
+            { title: '点击均价', dataIndex: 'cpc_total', label: '广告消耗信息', width: 90 },
+            { title: '不感兴趣点击次数', dataIndex: 'no_interest_count_total', label: '广告消耗信息', width: 90 },
+            { title: '朋友圈视频播放次数', dataIndex: 'video_play_count_total', label: '广告消耗信息', width: 90 },
+        ]
+    },
+    {
+        label: '广告转化信息',
+        data: [
+            { title: '下载次数', dataIndex: 'download_count_total', label: '广告转化信息', width: 80 },
+            { title: '安装次数', dataIndex: 'install_count_total', label: '广告转化信息', width: 80 },
+            { title: '激活次数', dataIndex: 'activated_count_total', label: '广告转化信息', width: 80 },
+            { title: '公众号关注人数', dataIndex: 'mp_follow_uv_total', label: '广告转化信息', width: 80 },
+            { title: '公众号关注成本', dataIndex: 'mp_follow_cost_total', label: '广告转化信息', width: 80 },
+            { title: '公众号关注率', dataIndex: 'mp_follow_rate_total', label: '广告转化信息', width: 80 },
+            { title: '公众号关注次数', dataIndex: 'mp_follow_pv_total', label: '广告转化信息', width: 80 },
+            { title: '公众号关注次数成本', dataIndex: 'mp_follow_pv_cost_total', label: '广告转化信息', width: 90 },
+            { title: '快应用添加次数', dataIndex: 'add_quick_app_pv_total', label: '广告转化信息', width: 80 },
+            { title: '快应用添加成本', dataIndex: 'add_quick_app_cost_total', label: '广告转化信息', width: 80 },
+            { title: '快应用添加率', dataIndex: 'add_quick_app_rate_total', label: '广告转化信息', width: 80 },
+            { title: '加企业微信客服人数', dataIndex: 'scan_follow_uv_total', label: '广告转化信息', width: 90 },
+            { title: '加企业微信客服成本', dataIndex: 'scan_follow_cost_total', label: '广告转化信息', width: 90 },
+            { title: '加企业微信客服率', dataIndex: 'scan_follow_rate_total', label: '广告转化信息', width: 90 },
+        ]
+    },
+    {
+        label: '商品转化',
+        data: [
+            { title: '首日新增下单量', dataIndex: 'first_day_order_count_total', label: '商品转化', width: 80 },
+            { title: '首日新增下单金额', dataIndex: 'first_day_order_amount_total', label: '商品转化', width: 80 },
+            { title: '首日新增下单ROI', dataIndex: 'first_day_order_roi_total', label: '商品转化', width: 80 },
+            { title: '订单量', dataIndex: 'order_count_total', label: '商品转化', width: 80},
+            { title: '订单金额', dataIndex: 'order_amount_total', label: '商品转化', width: 80 },
+            { title: '下单成本', dataIndex: 'order_cost_total', label: '商品转化', width: 80 },
+            { title: '下单率', dataIndex: 'order_rate_total', label: '商品转化', width: 80 },
+            { title: '下单ROI', dataIndex: 'order_roi_total', label: '商品转化', width: 80 },
+            { title: '客单价', dataIndex: 'atv_total', label: '商品转化', width: 80 },
+            { title: '转化量', dataIndex: 'conversions_count_total', label: '商品转化', width: 80 },
+            { title: '转化成本', dataIndex: 'conversions_cost_total', label: '商品转化', width: 80 },
+            { title: '深度转化', dataIndex: 'deep_conversions_count_total', label: '商品转化', width: 80 },
+            { title: '转化率', dataIndex: 'conversions_rate_total', label: '商品转化', width: 80 },
+            { title: '加粉数', dataIndex: 'add_fans_count_total', label: '商品转化', width: 80 },
+            { title: '加粉成本', dataIndex: 'add_fans_cost_total', label: '商品转化', width: 80 },
+        ]
+    },
+]
 
 export {
-    txAdConfig
+    txAdConfig,
+    planAdConfig
 }

+ 2 - 2
src/pages/launchSystemNew/adq/log/index.tsx

@@ -30,7 +30,7 @@ type Props = {
 const Log: React.FC<Props> = (props) => {
 
     /**************************/
-    let { tableIdClick, queryParmas, userId } = props
+    let { userId } = props
     const [queryparams, setQueryparams] = useState<{ accountId?: string, adgroupId?: string, adgroupName?: string, pageNum: number, pageSize: number, userId: string }>({ pageNum: 1, pageSize: 20, userId })
 
     const getLogList = useAjax((params) => getLogListApi(params), { formatResult: true })
@@ -45,7 +45,7 @@ const Log: React.FC<Props> = (props) => {
     return <div>
         <TableData
             isCard={false}
-            columns={() => tableConfig(tableIdClick)}
+            columns={() => tableConfig()}
             ajax={getLogList}
             dataSource={getLogList?.data?.data?.records}
             loading={getLogList?.data?.loading}

+ 1 - 10
src/pages/launchSystemNew/adq/log/tableConfig.tsx

@@ -4,16 +4,7 @@ import React from "react"
 
 
 
-function tableConfig(tableIdClick?: (props: {
-    activeKey: string, parma: {
-        accountId?: string,//账户ID
-        campaignId?: string,//计划ID
-        adgroupId?: string,//广告ID
-        adcreativeId?: string,//创意ID
-        pageId?: string,//落地页ID
-        targetingId?: string,//定向ID
-    }
-}) => void): any {
+function tableConfig(): any {
     return [
         {
             title: '所属账号',

+ 87 - 23
src/pages/launchSystemNew/components/TableData/index.tsx

@@ -1,8 +1,8 @@
 import CustomListModel from '@/components/CustomList'
-import Tables, { DataType } from '@/components/Tables'
+import Tables from '@/components/Tables'
 import { quanpin } from '@/utils/fullScreen'
 import { FullscreenExitOutlined, FullscreenOutlined, RedoOutlined, SettingOutlined, SyncOutlined } from '@ant-design/icons'
-import { Button, Card, Col, Input, Popconfirm, Row, Space, Spin, Tooltip, } from 'antd'
+import { Button, Card, Col, Row, Space, Tooltip, } from 'antd'
 import { ColumnsType } from 'antd/lib/table'
 import React, { useEffect, useRef, useState, useCallback } from 'react'
 import { useModel } from 'umi'
@@ -16,6 +16,7 @@ interface Prosp {
     title?: string,//tabel的标题
     tooltip?: JSX.Element,//是否在标题后加问号展示说明
     dataSource: any[],//table的数据
+    columnWidth?: string | number
     expandedRowRender?: (data: any) => JSX.Element,
     className?: string,//自定义class
     isdownload?: boolean,
@@ -47,10 +48,12 @@ interface Prosp {
     bodyStyle?: any
     gutter?: any
     isCard?: boolean
+    totalData?: any[]
+    refreshData?: () => void
 }
 
 function TableData(props: Prosp) {
-    const { isZj, scroll, columns, title, dataSource, expandedRowRender, className, isCard = true, leftChild, page = undefined, rowSelection = false, pageSize = undefined, size = 'small', fixed = { left: 0, right: 1 }, total = 0, loading = false, onChange, config, configName, ajax, syncAjax, hoverable = true, myKey, gutter = [0, 20] } = props
+    const { isZj, scroll, columns, title, columnWidth, dataSource, totalData = [], refreshData, expandedRowRender, className, isCard = true, leftChild, page = undefined, rowSelection = false, pageSize = undefined, size = 'small', fixed = { left: 0, right: 1 }, total = 0, onChange, config, configName, ajax, syncAjax, hoverable = true, myKey, gutter = [0, 20] } = props
     const { state: userState } = useModel('useOperating.useUser')
     const { isFell } = userState
     const [visible, setVisible] = useState<boolean>(false)
@@ -64,7 +67,6 @@ function TableData(props: Prosp) {
         syncAjaxShow: false,
         configShow: false,
     })
-    const [syncData,setSyncData]=useState('')
     const ref = useRef(null)
     const oldName = useRef(null)
     const version = '1.0.0'
@@ -200,16 +202,16 @@ function TableData(props: Prosp) {
                             type='text'
                             style={{ display: 'flex', alignItems: 'center' }}
                             onClick={syncAjax}
-                            >
+                        >
 
-                                <Tooltip
-                                    title='同步最新'
-                                    visible={tiptopShow.syncAjaxShow}
-                                    onVisibleChange={(visible) => {
-                                        setTipTopShow({ ...tiptopShow, syncAjaxShow: visible })
-                                    }}
-                                ><SyncOutlined style={{ color: 'red', fontSize: 17 }} /></Tooltip>
-                            </Button>
+                            <Tooltip
+                                title='同步最新'
+                                visible={tiptopShow.syncAjaxShow}
+                                onVisibleChange={(visible) => {
+                                    setTipTopShow({ ...tiptopShow, syncAjaxShow: visible })
+                                }}
+                            ><SyncOutlined style={{ color: 'red', fontSize: 17 }} /></Tooltip>
+                        </Button>
                     }
                     {config && <Button
                         size='small'
@@ -233,7 +235,23 @@ function TableData(props: Prosp) {
                             <Tooltip title={!isFell ? '全屏' : '退出全屏'}>{!isFell ? <FullscreenOutlined /> : <FullscreenExitOutlined />}</Tooltip>
                         }
                     </Button>
-                    {visible && <CustomListModel sysFixed={fixed} version={version} config={config} configName={configName} visible={visible} onClose={() => { setVisible(false) }} onChange={(arr: any) => { if (arr) { setSelectData({ selectData: [], fixed: { left: fixed.left, right: fixed.right } }) } else { setSelectData({ selectData: [], fixed: { left: fixed.left, right: fixed.right } }) } }} columns={newColumns} />}
+                    {visible && <CustomListModel
+                        sysFixed={fixed}
+                        version={version}
+                        config={config}
+                        configName={configName}
+                        visible={visible}
+                        onClose={() => { setVisible(false) }}
+                        onChange={(arr: any) => {
+                            // window.location.reload()
+                            refreshData?.()
+                            if (arr) {
+                                setSelectData({ selectData: [], fixed: { left: fixed.left, right: fixed.right } })
+                            } else {
+                                setSelectData({ selectData: [], fixed: { left: fixed.left, right: fixed.right } })
+                            }
+                        }}
+                        columns={newColumns} />}
                 </div>
             </Col>
         </Row>
@@ -249,11 +267,11 @@ function TableData(props: Prosp) {
             >
                 <Row gutter={gutter}>
                     {header}
-                    <Tab {...{ size, newColumns, handelResize, className, isZj, rowSelection, columns, loading, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
+                    <Tab {...{ size, newColumns, handelResize, columnWidth, className, isZj, totalData, rowSelection, columns, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
                 </Row>
             </Card> : <Row gutter={gutter}>
                 {header}
-                <Tab {...{ size, newColumns, handelResize, className, isZj, rowSelection, columns, loading, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
+                <Tab {...{ size, newColumns, handelResize, className, isZj, columnWidth, totalData, rowSelection, columns, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
             </Row>}
         </Col>
     </Row >
@@ -264,11 +282,58 @@ function TableData(props: Prosp) {
 
 /**表格 */
 const Tab = React.memo((props: any) => {
-    const { size, newColumns, className, handelResize, columns, scroll, loading, rowSelection, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey } = props
+    const { size, newColumns, className, handelResize, isZj, columns, scroll, columnWidth, totalData, rowSelection, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey } = props
+    let ran = Math.ceil(Math.random() * 100)
+
+    useEffect(() => {
+        const contentBodyScroll = (e: any) => {
+            let el = document.querySelector(`.header_table_body_${ran} .ant-table-body`);
+            if (el) {
+                el.scrollLeft = e.target.scrollLeft
+            }
+        }
+        const headerBodyScroll = (e: any) => {
+            let el = document.querySelector(`.content_table_body_${ran} .ant-table-body`);
+            if (el) {
+                el.scrollLeft = e.target.scrollLeft
+            }
+        }
+        if (isZj) {
+            document.querySelector(`.content_table_body_${ran} .ant-table-body`)?.addEventListener('scroll', contentBodyScroll);
+            document.querySelector(`.header_table_body_${ran} .ant-table-body`)?.addEventListener('scroll', headerBodyScroll);
+        }
+        () => {
+            if (isZj) {
+                document.querySelector(`.content_table_body_${ran} .ant-table-body`)?.removeEventListener('scroll', contentBodyScroll);
+                document.querySelector(`.header_table_body_${ran} .ant-table-body`)?.removeEventListener('scroll', headerBodyScroll);
+            }
+        }
+    }, [isZj, ran])
+
     return < Col span={24} >
         <div className={`${style[size]} ${className ? style[className] : ''} `}>
-            {dataSource || !ajax?.loading ? <Tables
-                className={`all_table ${className ? className : ''}`}
+            {
+                isZj && <Tables
+                    bordered
+                    columns={newColumns}
+                    dataSource={totalData?.length > 0 ? [...totalData] : [{ id: 1 }]}
+                    scroll={scroll ? isFell ? { ...scroll, y: document.body.clientHeight - 300 } : scroll : {}}
+                    size={size}
+                    pagination={false}
+                    hideOnSinglePage
+                    current={page}
+                    pageSize={pageSize}
+                    loading={ajax?.loading}
+                    rowSelection={rowSelection}
+                    className={`all_table header_table_body header_table_body_${ran} ${className ? className : ''}`}
+                    sortDirections={['ascend', 'descend', null]}
+                    handelResize={((columns: any) => handelResize(columns, 'antd'))}
+                    onChange={(pagination: any, filters: any, sorter: any) => onChange && onChange({ pagination, filters, sortData: sorter })}
+                />
+            }
+            <Tables
+                showHeader={!isZj}
+                className={`all_table content_table_body_${ran} ${className ? className : ''}`}
                 bordered
                 sortDirections={['descend', 'ascend', null]}
                 current={page}
@@ -278,17 +343,16 @@ const Tab = React.memo((props: any) => {
                 scroll={scroll ? isFell ? { ...scroll, y: document.body.clientHeight - 300 } : scroll : {}}
                 onChange={(pagination: any, filters: any, sorter: any) => onChange && onChange({ pagination, filters, sortData: sorter })}
                 rowClassName={(record: { color: string }) => style[record['color']]}
+                columnWidth={columnWidth}
                 expandedRowRender={expandedRowRender ? expandedRowRender : undefined}
                 size={size}
                 total={total}
-                loading={loading}
+                loading={ajax.loading}
                 defaultPageSize={20}
                 rowSelection={rowSelection}
                 handelResize={((columns: any) => handelResize(columns))}
                 myKey={myKey}
-            /> : <div className={style.example}>
-                <Spin />
-            </div>}
+            />
         </div>
     </Col >
 })

+ 13 - 0
src/pages/launchSystemNew/sysWarningRule/index.tsx

@@ -0,0 +1,13 @@
+import React from "react"
+import EarlyWarning from "@/components/EarlyWarning"
+
+/**
+ * 监控告警
+ */
+const SysWarningRule: React.FC = () => {
+
+
+    return <EarlyWarning />
+}
+
+export default SysWarningRule

+ 211 - 0
src/services/adMonitor/adMonitor.ts

@@ -13,6 +13,7 @@ export interface ListType {
     pageSize?: number//条数
     sortField?: string, // "排序字段"
     sort?: 'ASC' | 'DESC'  // 排序方式
+    topN?: number
 }
 
 /** 获取起量广告列表总表 */
@@ -40,6 +41,38 @@ export async function getDetailListApi(params: ListType) {
     })
 }
 
+export interface ListHourProps {
+    pageNum: number,
+    pageSize: number,
+
+    accountIdStr?: string, // 广告账号(字符串文本,随便输,可以输入多个)
+
+    adCreateTimeMin?: string // 广告创建时间
+    adCreateTimeMax?: string // 
+
+    adgroupIdStr?: string // 广告 id(字符串文本,随便输,可以输入多个)
+    columns?: string[] // 列
+
+    conversionsCountDayMin?: number, // 最低今日转化数
+    conversionsCountTotalMin?: number // 最低总转化数
+    costDayMin?: number, // 最低今日消耗
+    costTotalMin?: number,  // 最小总消耗
+
+    promotedObjectName?: string // 推广目标名称
+    promotedObjectType?: string // 推广目标类型
+    sortAsc?: boolean,    // 是否顺序
+    sortColumn?: string,  // 排序列
+    sysUserIds?: number[],
+    thousandDisplayPriceDayMin?: number,  // 最低今日千次曝光成本
+    thousandDisplayPriceTotalMin?: number  // 最低总千次曝光成本
+}
+export async function getListForHourApi(params: ListHourProps) {
+    return request(`${api}/tencentMonitor/adData/listForHour`, {
+        method: 'POST',
+        data: params
+    })
+}
+
 /** 获取起量广告列表5min */
 export async function getMinuteListApi(params: ListType) {
     Object.keys(params).forEach(key => {
@@ -255,4 +288,182 @@ export async function getAdqAccountListApi() {
     return request(`${api}/adq/adAccount/allOfUser`, {
         method: 'Get'
     })
+}
+
+
+/**
+ * NEW 广告消耗趋势 获取今日计划总消耗图谱 折线图
+ * @param data 
+ * @returns 
+ */
+export async function getCostTrendListApi(data: ListType) {
+    Object.keys(data).forEach(key => {
+        if (!data[key]) {
+            delete data[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adData/costTrend`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * NEW 广告起量排行
+ * @param params 
+ * @returns 
+ */
+export async function getCostTopListApi(data: ListType) {
+    Object.keys(data).forEach(key => {
+        if (!data[key]) {
+            delete data[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adData/costTop`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+export interface GetColumnTrendProps {
+    accountId: number
+    adgroupId: number
+    timeUnit: 'day' | 'hour' | 'minute'
+    trendColumns: string[]
+}
+/**
+ * NEW 广告指标趋势
+ * @param data 
+ * @returns 
+ */
+export async function getColumnTrendApi(data: GetColumnTrendProps) {
+    return request(`${api}/tencentMonitor/adData/columnTrend`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * NEW 广告数据-指定广告的总数据
+ * @param data 
+ * @returns 
+ */
+export async function getAdTotalDataApi(data: { accountId: number, adgroupId: number, columns: string[] }) {
+    return request(`${api}/tencentMonitor/adData/adTotalData`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+export interface GetListForAdProps {
+    accountId: number,
+    adgroupId: number,
+    columns: string[],
+    pageNum: number,
+    pageSize: number,
+    timeUnit: 'day' | 'hour' | 'minute'
+    sortAsc?: boolean
+}
+/**
+ * NEW 广告数据列表-指定广告的所有数据
+ * @param data 
+ * @returns 
+ */
+export async function getListForAdApi(data: GetListForAdProps) {
+    return request(`${api}/tencentMonitor/adData/listForAd`, {
+        method: 'POST',
+        data
+    })
+}
+
+export interface AdListProps {
+    pageNum: number
+    pageSize: number
+    columns: string[]
+    adCreateTimeMax?: string
+    adCreateTimeMin?: string
+    conversionsCountDayMin?: number
+    conversionsCountTotalMin?: number,
+    costDayMin?: number
+    costTotalMin?: number
+    thousandDisplayPriceDayMin?: number
+    thousandDisplayPriceTotalMin?: number
+    promotedObjectName?: string
+    accountIdStr?: string,
+    accountMemo?: string,
+    accountRemark?: string,
+    adgroupIdStr?: string,
+    adgroupName?: string
+    isDeepConversionSpec?: boolean
+    isDeleted?: boolean
+    optimizationGoal?: string
+    promotedObjectType?: string
+
+    sortAsc?: boolean
+    sortColumn?: string
+    status?: string
+    sysUserIds?: number
+}
+export async function getAdListApi(data: AdListProps) {
+    return request(`${api}/tencentMonitor/ad/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 广告详情
+ * @param param0 
+ * @returns 
+ */
+export async function getAdgroupsDetailsApi({ accountId, adgroupId }: { accountId: any, adgroupId: any }) {
+    return request(`${api}/adq/adgroups/detail/${accountId}/${adgroupId}`, {
+        method: 'POST'
+    })
+}
+
+/**
+ * 移除广告的告警规则---广告告警规则表的操作
+ * @param param0 
+ * @returns 
+ */
+export async function delAdWarningRuleApi({ accountId, adgroupId, ruleId }: { accountId: any, adgroupId: any, ruleId: number }) {
+    return request(`${api}/adq/sysWarningRule/removeAdgroup/${accountId}/${adgroupId}/${ruleId}`, {
+        method: 'PUT'
+    })
+}
+
+/**
+ *  将指定广告加入规则黑名单---默认列表 + 广告账号列表的操作
+ * @param param0 
+ * @returns 
+ */
+export async function addAdToRuleBlackListApi({ accountId, adgroupId, ruleId }: { accountId: any, adgroupId: any, ruleId: number }) {
+    return request(`${api}/adq/sysWarningRule/configRuleBlackList/${accountId}/${adgroupId}/${ruleId}`, {
+        method: 'PUT'
+    })
+}
+
+/**
+ * 将广告移出告警规则的黑名单---黑名单列表的操作
+ * @param param0 
+ * @returns 
+ */
+export async function delAdToRuleBlackListApi({ accountId, adgroupId, ruleId }: { accountId: any, adgroupId: any, ruleId: number }) {
+    return request(`${api}/adq/sysWarningRule/removeBlackList/${accountId}/${adgroupId}/${ruleId}`, {
+        method: 'PUT'
+    })
+}
+
+/**
+ * 配置广告的告警规则
+ * @param param0 
+ * @returns 
+ */
+export async function configAdWarningRuleApi({ accountId, adgroupId, ruleIds }: { accountId: any, adgroupId: any, ruleIds: string }) {
+    return request(`${api}/adq/sysWarningRule/configRuleAdgroup/${accountId}/${adgroupId}/${ruleIds}`, {
+        method: 'PUT'
+    })
 }

+ 70 - 9
src/services/adMonitor/earlyWarning.ts

@@ -46,9 +46,8 @@ export async function editSysWarningRuleApi(data: AddSysWarningRuleProps) {
  * @returns 
  */
 export async function getSysWarningRuleApi(ruleId: number) {
-    return request(`${api}/adq/sysWarningRule/adList`, {
-        method: 'GET',
-        params: { ruleId }
+    return request(`${api}/adq/sysWarningRule/adConfigDetail/${ruleId}`, {
+        method: 'GET'
     })
 }
 
@@ -67,17 +66,38 @@ export async function delSysWarningRuleApi(ruleIds: string) {
 export interface DelAdSysWarningRuleProps {
     accountId: number, 
     adgroupId: number, 
-    campaignId: number
+    ruleId: number
 }
 /**
  * 删除告警规则下的广告
  * @param data 
  * @returns 
  */
-export async function delAdSysWarningRuleApi(data: DelAdSysWarningRuleProps[]) {
-    return request(`${api}/adq/sysWarningRule/deleteAd`, {
-        method: 'POST',
-        data
+export async function delAdSysWarningRuleApi(data: DelAdSysWarningRuleProps) {
+    return request(`${api}/adq/sysWarningRule/removeAdgroup/${data.accountId}/${data.adgroupId}/${data.ruleId}`, {
+        method: 'PUT'
+    })
+}
+
+/**
+ * 删除黑名单列表广告
+ * @param data 
+ * @returns 
+ */
+export async function delAdSysWarningRuleBlackApi(data: DelAdSysWarningRuleProps) {
+    return request(`${api}/adq/sysWarningRule/removeBlackList/${data.accountId}/${data.adgroupId}/${data.ruleId}`, {
+        method: 'PUT'
+    })
+}
+
+/**
+ * 删除账号
+ * @param data 
+ * @returns 
+ */
+export async function removeAccountApi(data: DelAdSysWarningRuleProps) {
+    return request(`${api}/adq/sysWarningRule/removeAccount/${data.accountId}/${data.ruleId}`, {
+        method: 'DELETE'
     })
 }
 
@@ -108,7 +128,48 @@ export interface ModifySysWarningRuleProps {
  * @returns 
  */
 export async function modifySysWarningRuleApi(data: ModifySysWarningRuleProps) {
-    return request(`${api}/adq/sysWarningRule/modifyAd`, {
+    return request(`${api}/adq/sysWarningRule/configRuleAdgroupBatch`, {
+        method: 'POST',
+        data
+    })
+}
+
+export interface GetAdWarningLogAdProps {
+    pageNum: number,
+    pageSize: number,
+    adgroupId: string,
+    accountId: string
+    ruleId?: string
+    operationType?: number
+}
+/**
+ * 告警日志(按广告获取)
+ * @param data 
+ * @returns 
+ */
+export async function getAdWarningLogAdListApi(data: GetAdWarningLogAdProps) {
+    return request(`${api}/tencentMonitor/adWarningLog/listByAd`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+export interface GetAdWarningLogRuleProps {
+    pageNum: number,
+    pageSize: number,
+    ruleId: number
+    adgroupId?: string,
+    accountId?: string
+    operationType?: number
+}
+/**
+ * 告警日志(通过规则获取)
+ * @param data 
+ * @returns 
+ */
+export async function getAdWarningLogRuleListApi(data: GetAdWarningLogRuleProps) {
+    return request(`${api}/tencentMonitor/adWarningLog/listByRule`, {
         method: 'POST',
         data
     })