wjx 2 anos atrás
pai
commit
f85f86d4f4
38 arquivos alterados com 2496 adições e 232 exclusões
  1. 4 4
      config/routerConfig.ts
  2. 161 0
      src/pages/launchSystemNew/account/groupLeft.tsx
  3. 120 3
      src/pages/launchSystemNew/account/index.less
  4. 38 30
      src/pages/launchSystemNew/account/index.tsx
  5. 3 4
      src/pages/launchSystemNew/account/tableConfig.tsx
  6. 7 0
      src/pages/launchSystemNew/adq/ad/const.ts
  7. 153 0
      src/pages/launchSystemNew/adq/ad/copy.tsx
  8. 230 45
      src/pages/launchSystemNew/adq/ad/index.tsx
  9. 40 0
      src/pages/launchSystemNew/adq/ad/switchStatus.tsx
  10. 99 39
      src/pages/launchSystemNew/adq/ad/tableConfig.tsx
  11. 71 0
      src/pages/launchSystemNew/adq/ad/timeSeriesLook.tsx
  12. 251 0
      src/pages/launchSystemNew/adq/ad/updateAd.tsx
  13. 1 0
      src/pages/launchSystemNew/adq/adAccount/index.tsx
  14. 35 8
      src/pages/launchSystemNew/adq/campaign/index.tsx
  15. 36 9
      src/pages/launchSystemNew/adq/creative/index.tsx
  16. 28 0
      src/pages/launchSystemNew/adq/index.less
  17. 31 30
      src/pages/launchSystemNew/adq/index.tsx
  18. 34 8
      src/pages/launchSystemNew/adq/landingPage/index.tsx
  19. 4 3
      src/pages/launchSystemNew/adq/landingPage/tableConfig.tsx
  20. 139 0
      src/pages/launchSystemNew/adq/log/index.tsx
  21. 142 0
      src/pages/launchSystemNew/adq/log/tableConfig.tsx
  22. 35 9
      src/pages/launchSystemNew/adq/targeting/index.tsx
  23. 11 10
      src/pages/launchSystemNew/adq/targeting/tableConfig.tsx
  24. 7 3
      src/pages/launchSystemNew/components/TableData/index.tsx
  25. 27 0
      src/pages/launchSystemNew/components/timeInSelect/Header.tsx
  26. 215 0
      src/pages/launchSystemNew/components/timeInSelect/Thead.tsx
  27. 67 0
      src/pages/launchSystemNew/components/timeInSelect/const.ts
  28. 62 0
      src/pages/launchSystemNew/components/timeInSelect/header.less
  29. 87 0
      src/pages/launchSystemNew/components/timeInSelect/index.less
  30. 16 0
      src/pages/launchSystemNew/components/timeInSelect/index.tsx
  31. 80 0
      src/pages/launchSystemNew/components/timeInSelect/thead.less
  32. 0 2
      src/pages/launchSystemNew/launchManage/createAd/ad/modal/adPositionList.tsx
  33. 2 3
      src/pages/launchSystemNew/launchManage/createAd/ad/modal/leadAd.tsx
  34. 38 2
      src/pages/launchSystemNew/launchManage/createAd/ad/modal/wechat.tsx
  35. 17 0
      src/services/launchAdq/adAuthorize.ts
  36. 109 20
      src/services/launchAdq/adq.ts
  37. 1 0
      src/services/launchAdq/enum.ts
  38. 95 0
      src/services/launchAdq/subgroup.ts

+ 4 - 4
config/routerConfig.ts

@@ -19,7 +19,7 @@ function headrRouter(initialState: any, history: any) {
         initialState?.menu?.data.forEach((item: { roles: string[], path: string, routes: { path: string, routes: any[] }[] }) => {
             if (item?.routes?.some((i: { path: string }) => i.path?.includes('/launchSystemNew'))) {
                 let path = item?.routes?.length > 0 ? (item?.routes[0]?.routes?.length > 0 && item?.routes[0]?.routes[0]?.path) ? item?.routes[0]?.routes[0]?.path : item?.routes[0].path : item?.routes[0].path
-                isPhone ? history.push(path) : window.open(location.origin+'/#'+path) 
+                isPhone ? history.push(path) : window.open(location.origin + '/#' + path)
             }
         })
     }
@@ -28,7 +28,7 @@ function headrRouter(initialState: any, history: any) {
         initialState?.menu?.data.forEach((item: { roles: string[], path: string, routes: { path: string, routes: any[] }[] }) => {
             if (item?.routes?.some((i: { path: string }) => i.path?.includes('/adMonitor'))) {
                 let path = item?.routes?.length > 0 ? (item?.routes[0]?.routes?.length > 0 && item?.routes[0]?.routes[0]?.path) ? item?.routes[0]?.routes[0]?.path : item?.routes[0].path : item?.routes[0].path
-                isPhone ? history.push(path) : window.open(location.origin+'/#'+path) 
+                isPhone ? history.push(path) : window.open(location.origin + '/#' + path)
             }
         })
     }
@@ -90,8 +90,8 @@ const launchSystem = {
         {
             path: '/launchSystemNew/account',
             name: '广告账户管理',
-            component: './launchSystemNew/account',
             access: 'account',
+            component: './launchSystemNew/account',
         },
         {
             path: '/launchSystemNew/adq',
@@ -108,7 +108,7 @@ const launchSystem = {
                     path: '/launchSystemNew/launchManage/localAd',
                     name: '广告模板',
                     access: 'localAd',
-                    component: './launchSystemNew/launchManage/localAd',  
+                    component: './launchSystemNew/launchManage/localAd',
                 },
                 {
                     path: '/launchSystemNew/launchManage/createAd',

+ 161 - 0
src/pages/launchSystemNew/account/groupLeft.tsx

@@ -0,0 +1,161 @@
+import { useAjax } from "@/Hook/useAjax"
+import { addGroupApi, delGroupApi, editGroupApi, getGroupListApi } from "@/services/launchAdq/subgroup"
+import { DeleteOutlined, FormOutlined, PlusOutlined } from "@ant-design/icons"
+import { Button, Input, message, Popconfirm, Space, Spin } from "antd"
+import React, { useEffect, useRef, useState } from "react"
+import './index.less'
+
+/**
+ * 分组管理Left
+ */
+interface Props {
+
+}
+const GroupLeft: React.FC<Props> = (props) => {
+
+    /*************************/
+    const [isNewGrouping, setIsNewGrouping] = useState<boolean>(false);  // 是否新增分组
+    const inputRef = useRef<any>(null);
+    const [groupData, setGroupData] = useState<{ groupName: string, remark?: string, groupId?: number }>({ groupName: '' }) // 分组添加
+    const [selectAccData, setSelectAccData] = useState<{ id: number, mpName: string }[]>([])  // 选中
+    const [queryForm, setQueryForm] = useState<{ groupId?: number, pageNum: number, pageSize: number }>({ pageNum: 1, pageSize: 20 })
+    const { groupId = 0 } = queryForm
+
+    const addGroup = useAjax((params) => addGroupApi(params))
+    const editGroup = useAjax((params) => editGroupApi(params))
+    const getGroupList = useAjax(() => getGroupListApi())
+    const delGroup = useAjax((params) => delGroupApi(params))
+    /*************************/
+
+    /** 点击新增处理聚焦 */
+    useEffect(() => {
+        if (isNewGrouping && inputRef?.current) {
+            inputRef.current!.focus({
+                cursor: 'end',
+            });
+        }
+    }, [inputRef, isNewGrouping])
+
+    /** 获取分组 */
+    useEffect(() => {
+        getGroupList.run()
+    }, [])
+
+    /** 新增修改分组 */
+    const addEditHandle = () => {
+        if (groupData?.groupName) {
+            if (groupData?.groupId) {
+                editGroup.run(groupData).then((res: any) => {
+                    message.success('修改成功')
+                    setGroupData({ groupName: '' })
+                    setIsNewGrouping(false)
+                    getGroupList.refresh()
+                })
+            } else {
+                addGroup.run(groupData).then((res: any) => {
+                    message.success('添加成功')
+                    setGroupData({ groupName: '' })
+                    setIsNewGrouping(false)
+                    getGroupList.refresh()
+                })
+            }
+        } else {
+            message.error('请填写分组名称')
+        }
+    }
+
+    /** 删除分组 */
+    const delGroupHandle = (groupId: number) => {
+        delGroup.run({ groupId }).then(res => {
+            message.success('删除成功')
+            getGroupList.refresh()
+            if (queryForm.groupId === groupId) {
+                setQueryForm({ ...queryForm, groupId: 0 })
+            }
+        })
+    }
+
+    return <div className="groupLeft">
+        <div className="groupLeft_header">
+            <div>全部分组</div>
+            <Button type="link" icon={<PlusOutlined />} className="stop" onClick={(e) => {
+                e.stopPropagation()
+                setGroupData({ groupName: '' })
+                setIsNewGrouping(true)
+            }}>新增分组</Button>
+        </div>
+        <div className="groupLeft_content">
+            {isNewGrouping && <div className="groupLeft_content_item">
+                <div className="edit stop">
+                    <Space direction="vertical" className="stop">
+                        <Space className="stop">
+                            <div style={{ width: 30 }} className="stop">组名</div>
+                            <Input placeholder="输入分组名称(最多10个字)" className="stop" maxLength={10} ref={inputRef} onChange={(e) => { setGroupData({ ...groupData, groupName: e.target.value }) }} value={groupData?.groupName} />
+                        </Space>
+                        {/* <Space className="stop">
+                            <div style={{ width: 70 }} className="stop">分组备注:</div>
+                            <Input placeholder="请输入分组备注(最多10个字)" className="stop" maxLength={10} onChange={(e) => { setGroupData({ ...groupData, remark: e.target.value }) }} value={groupData?.remark} />
+                        </Space> */}
+                        <div style={{ display: 'flex', justifyContent: 'flex-end' }} className="stop">
+                            <Space className="stop">
+                                <Button size="small" className="stop" onClick={() => { setIsNewGrouping(false); setGroupData({ groupName: '' }) }}>取消</Button>
+                                <Button type="primary" className="stop" size="small" onClick={addEditHandle} loading={getGroupList?.loading}>确定</Button>
+                            </Space>
+                        </div>
+                    </Space>
+                </div>
+            </div>}
+            <Spin spinning={getGroupList.loading}>
+                <div className="groupLeft_content_item">
+                    <div className={`con ${groupId === 0 ? 'select' : ''}`} onClick={() => { setQueryForm({ ...queryForm, groupId: 0 }); setSelectAccData([]) }}>
+                        <div className="left">
+                            <div className="title">默认分组</div>
+                        </div>
+                    </div>
+                </div>
+                {getGroupList?.data?.map((item: { groupName: string, groupId: number }, index: number) => <div className="groupLeft_content_item" key={item.groupId || index}>
+                    {groupData?.groupId && groupData?.groupId === item?.groupId ? <div className="edit stop">
+                        <Space direction="vertical" className="stop">
+                            <Space className="stop">
+                                <div style={{ width: 40 }} className="stop">组名</div>
+                                <Input placeholder="输入分组名称(最多10个字)" className="stop" maxLength={10} ref={inputRef} onChange={(e) => { setGroupData({ ...groupData, groupName: e.target.value }) }} value={groupData?.groupName} />
+                            </Space>
+                            {/* <Space className="stop">
+                                <div style={{ width: 70 }} className="stop">分组备注:</div>
+                                <Input placeholder="请输入分组备注(最多10个字)" className="stop" maxLength={10} onChange={(e) => { setGroupData({ ...groupData, remark: e.target.value }) }} value={groupData?.remark} />
+                            </Space> */}
+                            <div style={{ display: 'flex', justifyContent: 'flex-end' }} className="stop">
+                                <Space className="stop">
+                                    <Button size="small" className="stop" onClick={() => { setIsNewGrouping(false); setGroupData({ groupName: '' }) }}>取消</Button>
+                                    <Button type="primary" className="stop" size="small" onClick={addEditHandle} loading={editGroup?.loading}>修改</Button>
+                                </Space>
+                            </div>
+                        </Space>
+                    </div> : <div className={`con ${groupId === item.groupId ? 'select' : ''}`} onClick={() => { setQueryForm({ ...queryForm, groupId: item.groupId, pageNum: 1 }); setSelectAccData([]) }} key={item.groupId || index}>
+                        <div className="left">
+                            <div className="title">{item.groupName}</div>
+                            {/* {item?.remark && <div className="subTitle">{item?.remark}</div>} */}
+                        </div>
+                        <div className="right">
+                            <Space>
+                                <FormOutlined onClick={(e) => { e.stopPropagation(); setGroupData({ ...item }); setIsNewGrouping(false) }} />
+                                <Popconfirm
+                                    title="确定删除?"
+                                    onConfirm={() => { delGroupHandle(item.groupId) }}
+                                    okText="确定"
+                                    cancelText="取消"
+                                >
+                                    <DeleteOutlined style={{ color: 'red' }} />
+                                </Popconfirm>
+                            </Space>
+                        </div>
+                    </div>}
+
+                </div>)}
+            </Spin>
+        </div>
+    </div>
+}
+
+
+export default React.memo(GroupLeft)

+ 120 - 3
src/pages/launchSystemNew/account/index.less

@@ -10,14 +10,131 @@
   text-overflow: ellipsis;
   white-space: nowrap;
 }
-.boxCol{
+
+.boxCol {
   display: flex;
-  strong{
+
+  strong {
     width: 25%;
     text-align: right;
   }
-  span{
+
+  span {
     color: #999;
     width: 75%;
   }
+}
+
+.manage {
+  height: 100%;
+  display: flex;
+  justify-content: flex-start;
+
+  &__left {
+    width: calc(100% - 200px);
+  }
+}
+
+.groupLeft {
+  width: 200px;
+  height: 100%;
+  background-color: #fff;
+
+  &_header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 50px;
+    border-bottom: 1px solid rgb(236, 236, 236);
+    background-color: #f8f8f8;
+
+    &>div {
+      font-size: 16px;
+      font-weight: 600;
+      padding: 0 10px;
+    }
+  }
+
+  &_content {
+    height: calc(100% - 50px);
+    overflow-y: auto;
+
+    &_item {
+      &>.con {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 5px 12px;
+        box-sizing: border-box;
+        height: 50px;
+        border-bottom: 1px solid rgb(236, 236, 236);
+        transition: .1s;
+        box-sizing: border-box;
+        cursor: pointer;
+        border-right: 2px solid #fff;
+
+        >.left {
+          width: calc(100% - 45px);
+
+          >div {
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+        }
+
+        >.right {
+          width: 38px;
+        }
+
+        .title {
+          color: #000;
+          font-weight: 500;
+        }
+
+        .subTitle {
+          font-size: 12px;
+          color: rgb(122, 122, 122);
+        }
+
+        &:hover {
+          background-color: #e6f7ff;
+          border-right-color: #1890ff;
+
+          .title {
+            color: #1890ff;
+          }
+
+          .subTitle {
+            color: #1890ff;
+          }
+        }
+
+        &.select {
+          background-color: #e6f7ff;
+          border-right-color: #1890ff;
+
+          .title {
+            color: #1890ff;
+          }
+
+          .subTitle {
+            color: #1890ff;
+          }
+        }
+      }
+
+      &>.edit {
+        padding: 10px 12px;
+        box-sizing: border-box;
+        border-bottom: 1px solid rgb(236, 236, 236);
+
+        // font-size: 12px;
+        .ant-input-affix-wrapper {
+          border-radius: 4px;
+        }
+      }
+    }
+
+  }
 }

+ 38 - 30
src/pages/launchSystemNew/account/index.tsx

@@ -1,28 +1,30 @@
 
 import HocError from '@/Hoc/HocError'
-import { Card, Col, Modal, Row, Input, message, Space, Button } from 'antd'
+import { Col, Modal, Row, Input, message, Space } from 'antd'
 import React, { useCallback, useEffect, useState } from 'react'
 import { columnsMp } from './tableConfig'
 import { useModel } from 'umi'
 import { useAjax } from '@/Hook/useAjax'
-import { putAdAccountApi } from '@/services/launchAdq/adAuthorize'
+import { getAdAccountListApi, putAdAccountApi } from '@/services/launchAdq/adAuthorize'
 import style from './index.less'
 import TableData from '../components/TableData'
+import GroupLeft from './groupLeft'
 
 /** 投放管理 */
 const AdAuthorize: React.FC = () => {
+
+    /*************************/
     const { getAdAccount } = useModel('useLaunchAdq.useAdAuthorize')
     const [tableData, setTableData] = useState<any[]>([])
-    const [remarkData, set_remarkData] = useState<{
-        visible: boolean,
-        remark: string,
-        data: any
-    }>({
+    const [remarkData, set_remarkData] = useState<{ visible: boolean, remark: string, data: any }>({
         visible: false,
         remark: '',
         data: null
     })
     const putRemark = useAjax((adAccountId: any, remark: any) => putAdAccountApi(adAccountId, remark))
+    const getAdAccountList = useAjax((params) => getAdAccountListApi(params), { formatResult: true })
+    /*************************/
+
     useEffect(() => {
         getList()
     }, [])
@@ -48,28 +50,34 @@ const AdAuthorize: React.FC = () => {
     const edit = useCallback((data) => {
         set_remarkData({ ...remarkData, visible: true, data, remark: data.remark })
     }, [remarkData])
-    
-    return <Card>
-        <TableData
-            ajax={getAdAccount}
-            dataSource={tableData}
-            loading={getAdAccount?.loading}
-            columns={() => columnsMp(edit)}
-            size="small"
-            scroll={{ x: 2000, y: 600 }}
-            leftChild={<Space>
-                <Input placeholder="广告主ID" style={{ width: 150 }} allowClear  onChange={(e) => {
-                    let value = e.target.value
-                    if (value) {
-                        let newArr = tableData?.filter(item => String(item.accountId).includes(value))
-                        setTableData(newArr)
-                    } else {
-                        setTableData(getAdAccount?.data?.data)
-                    }
-                }} />
-                <Button onClick={getList} type='primary'>搜索</Button>
-            </Space>}
-        />
+
+    return <div style={{ height: '100%' }}>
+        {/* <div className={style.manage}> */}
+            {/* <GroupLeft /> */}
+            <div>
+                <TableData
+                    ajax={getAdAccount}
+                    dataSource={tableData}
+                    loading={getAdAccount?.loading}
+                    columns={() => columnsMp(edit)}
+                    size="small"
+                    scroll={{ y: 600 }}
+                    leftChild={<Space>
+                        <Input placeholder="广告主ID" style={{ width: 150 }} allowClear onChange={(e) => {
+                            let value = e.target.value
+                            if (value) {
+                                let newArr = tableData?.filter(item => String(item.accountId).includes(value))
+                                setTableData(newArr)
+                            } else {
+                                setTableData(getAdAccount?.data?.data)
+                            }
+                        }} />
+                        {/* <Button onClick={getList} type='primary'>搜索</Button> */}
+                    </Space>}
+                />
+            </div>
+        {/* </div> */}
+
         {remarkData.visible && <Modal
             visible={remarkData.visible}
             title='编辑账户'
@@ -92,7 +100,7 @@ const AdAuthorize: React.FC = () => {
                 }} /></span></Col>
             </Row>
         </Modal>}
-    </Card>
+    </div>
 }
 
 export default HocError(AdAuthorize)

+ 3 - 4
src/pages/launchSystemNew/account/tableConfig.tsx

@@ -17,7 +17,7 @@ export function columnsMp(edit:(params: any)=>void): any {
             dataIndex: 'accountId',
             key: 'accountId',
             align: 'center',
-            width: 100,
+            width: 75,
             fixed:'left'
         },
         {
@@ -25,7 +25,7 @@ export function columnsMp(edit:(params: any)=>void): any {
             dataIndex: 'sourceType',
             key: 'sourceType',
             align: 'center',
-            width: 70,
+            width: 60,
             render: (a: any, b: any) => {
                 return <span>{a == 0 ? '微信' : 'QQ'}</span>
             }
@@ -34,7 +34,6 @@ export function columnsMp(edit:(params: any)=>void): any {
             title: '备注',
             dataIndex: 'remark',
             key: 'remark',
-            align: 'center',
             width: 200,
             ellipsis:true,
         },
@@ -77,7 +76,7 @@ export function columnsMp(edit:(params: any)=>void): any {
             title: '行业ID',
             dataIndex: 'systemIndustryId',
             key: 'systemIndustryId',
-            width: 130,
+            width: 100,
             align: 'center',
             render: (a: any) => {
                 return <Tooltip title={a}>

+ 7 - 0
src/pages/launchSystemNew/adq/ad/const.ts

@@ -0,0 +1,7 @@
+import moment from "moment";
+export function getTimeSeriesList() {
+    return new Array(7 * 24 * 2).fill('1');
+}
+
+const [_, ...rest] = moment.weekdays();
+export const WEEK_SERIRES = [...rest, _];

+ 153 - 0
src/pages/launchSystemNew/adq/ad/copy.tsx

@@ -0,0 +1,153 @@
+import { useAjax } from "@/Hook/useAjax"
+import { copyAdAdApi } from "@/services/launchAdq/adq"
+import { Checkbox, DatePicker, Form, InputNumber, message, Modal, notification, Radio, Space, TimePicker } from "antd"
+import React, { useState } from "react"
+import moment from "moment"
+import { getTimeSeriesList } from "./const"
+import TimeInSelect from "../../components/timeInSelect"
+import { RangePickerProps } from "antd/lib/date-picker"
+const { RangePicker } = DatePicker;
+let DatePickers: any = DatePicker
+
+
+interface Props {
+    visible?: boolean,
+    onChange?: () => void,
+    onClose?: () => void,
+    selectedRows: any[]
+}
+const Copy: React.FC<Props> = (props) => {
+
+    /**********************************/
+    const { visible, onChange, onClose, selectedRows } = props
+    const [form] = Form.useForm();
+    let dateType = Form.useWatch('dateType', form)
+    const [state, setState] = useState<any>({ isShowTime: [] })
+    const [timeSeriesType, setTimeSeriesType] = useState<'allDayLong' | 'timeInterValS'>('allDayLong')
+
+    const copyAdAd = useAjax((params) => copyAdAdApi(params))
+    /**********************************/
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            let newValues = JSON.parse(JSON.stringify(values))
+            if (newValues?.date) {
+                if (newValues.dateType === '2') {
+                    newValues['beginDate'] = moment(newValues.date).format('YYYY-MM-DD')
+                    newValues['endDate'] = ''
+                } else {
+                    newValues['beginDate'] = moment(newValues.date[0]).format('YYYY-MM-DD')
+                    newValues['endDate'] = moment(newValues.date[1]).format('YYYY-MM-DD')
+                }
+            }
+            if (newValues?.timeSeries) {
+                newValues['timeSeries'] = newValues.timeSeries.join('')
+            }
+            if (timeSeriesType === 'allDayLong') {
+                newValues.timeSeries = getTimeSeriesList().join('')
+            }
+            if (newValues.firstDayBeginTime) {
+                newValues['firstDayBeginTime'] = moment(newValues.firstDayBeginTime).format('HH:mm:ss')
+            }
+
+            delete newValues?.date
+            delete newValues?.dateType
+            console.log(newValues);
+            copyAdAd.run({ ...newValues, adgroupIds: selectedRows.map((item: { adgroupId: number }) => item.adgroupId) }).then(res => {
+                if (res) {
+                    message.success(`复制完成.成功: ${res.success},失败: ${res.fail}`)
+                    if (res?.fail) {
+                        notification.error({
+                            message: `复制失败`,
+                            description: `成功: ${res.success},复制失败${res.fail}条,失败的请到任务列表查看`,
+                            duration: 0
+                        });
+                    }
+                    onChange?.()
+                }
+            })
+        })
+    }
+
+    /** 禁止选择以前时间 */
+    const disabledDate: RangePickerProps['disabledDate'] = current => {
+        // Can not select days before today and today
+        return current && current < moment().startOf('day');
+    };
+
+    return <Modal
+        title="复制广告"
+        visible={visible}
+        onOk={handleOk}
+        width={800}
+        onCancel={() => onClose?.()}
+        confirmLoading={copyAdAd.loading}
+    >
+        <Form
+            form={form}
+            labelCol={{ span: 4 }}
+            className='ad_form_style'
+            colon={false}
+            initialValues={{
+                dateType: '2',
+                date: moment().startOf('day'),
+                timeSeries: getTimeSeriesList(),
+                firstDayBeginTime: moment('2023-02-24 00:00:00'),
+                copyCount: 1
+            }}
+        >
+            <Form.Item label={<strong>投放日期</strong>} name='dateType'>
+                <Radio.Group onChange={(e) => {
+                    if (e.target.value === "1") {
+                        form.setFieldsValue({ date: [moment().startOf('day'), moment().startOf('day').add(1, 'M')] })
+                    }
+                    if (e.target.value === "2") {
+                        form.setFieldsValue({ date: moment().startOf('day') })
+                    }
+                }}>
+                    <Radio.Button value="1">选择开始与结束日期</Radio.Button>
+                    <Radio.Button value="2">长期投放</Radio.Button>
+                </Radio.Group>
+            </Form.Item>
+            {/* 投放日期的不同展示不同的日期选择 */}
+            {dateType === '1' ? <Form.Item name='date' rules={[{ required: true, message: '请选择日期' }]}>
+                <RangePicker style={{ marginLeft: 125 }} disabledDate={disabledDate}></RangePicker>
+            </Form.Item> : <Form.Item name='date' style={{ marginLeft: 125 }} rules={[{ required: true, message: '请选择日期' }]}>
+                <DatePickers disabledDate={disabledDate} />
+            </Form.Item>}
+
+            <Form.Item label={<strong>投放时段</strong>} style={{ marginBottom: 0 }}>
+                <Space direction='vertical' style={{ width: '100%' }}>
+                    <Radio.Group
+                        name='timeSeriesType'
+                        value={timeSeriesType}
+                        onChange={(value) => {
+                            setTimeSeriesType(value.target.value)
+                            if (value.target.value === 'timeInterValS') {
+                                setState({ isShowTime: ['1'] })
+                            }
+                        }}>
+                        <Radio.Button value={'allDayLong'}>全天投放</Radio.Button>
+                        <Radio.Button value={'timeInterValS'}>指定多个时段</Radio.Button>
+                    </Radio.Group>
+                    {timeSeriesType === 'timeInterValS' && <Form.Item name='timeSeries' noStyle rules={[{ required: true, message: '请选择时段' }]}>
+                        <TimeInSelect />
+                    </Form.Item>}
+                </Space>
+            </Form.Item>
+            <Form.Item label={<strong></strong>}>
+                <Space>
+                    <Checkbox.Group options={[{ label: '指定首日开始投放时间', value: '1' }]} disabled={timeSeriesType === 'timeInterValS'} value={state.isShowTime} onChange={(checkedValue) => { setState({ ...state, isShowTime: checkedValue }) }} />
+                    {state?.isShowTime?.length > 0 && <Form.Item name='firstDayBeginTime' noStyle rules={[{ required: true, message: '请选择时间' }]}>
+                        <TimePicker />
+                    </Form.Item>}
+                </Space>
+            </Form.Item>
+            <Form.Item label={<strong>复制数量</strong>} name='copyCount' rules={[{ required: true, message: '请输入复制数量' }]}>
+                <InputNumber min={1} />
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(Copy)

+ 230 - 45
src/pages/launchSystemNew/adq/ad/index.tsx

@@ -1,11 +1,14 @@
 
 import { useAjax } from '@/Hook/useAjax'
 import { PromotedObjectType } from '@/services/launchAdq/enum'
-import { Col, Row, Input, Select, message } from 'antd'
-import React, { useEffect, useCallback } from 'react'
+import { Col, Row, Input, Select, message, Space, Button, Popconfirm, Switch, notification } from 'antd'
+import React, { useEffect, useCallback, useState } from 'react'
 import TableData from '../../components/TableData'
 import tableConfig from './tableConfig'
-import { putAdqAdgroupsSync, getAdqAdgroupsList,putAdqAdgroupsConfigStatus } from '@/services/launchAdq/adq'
+import { putAdqAdgroupsSync, getAdqAdgroupsList, delListAdqAdgroupsApi, editAdqAdgroupsDataApi } from '@/services/launchAdq/adq'
+import { CopyOutlined, DeleteOutlined, FieldTimeOutlined, PauseCircleOutlined, PlayCircleOutlined, TransactionOutlined } from '@ant-design/icons'
+import UpdateAd from './updateAd'
+import Copy from './copy'
 type Props = {
     accountId: string,
     adAccountId: string,
@@ -30,84 +33,182 @@ type Props = {
     }) => void
 }
 
-function Ad(props:Props) {
-    let { accountId, adAccountId,userId,tableIdClick,queryParmas } = props
-    // api方法
+const Ad: React.FC<Props> = (props) => {
+
+    /***********************/
+    let { accountId, adAccountId, userId, tableIdClick, queryParmas } = 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 listAjax = useAjax((params) => getAdqAdgroupsList(params), { formatResult: true })
     const syncAjax = useAjax((adAccountId) => putAdqAdgroupsSync(adAccountId))
-    const switchAjax = useAjax((params) => putAdqAdgroupsConfigStatus(params))
-    console.log('创意=====》')
+    const delListAdqAdgroups = useAjax((params) => delListAdqAdgroupsApi(params))
+    const editAdqAdgroupsData = useAjax((params) => editAdqAdgroupsDataApi(params))
+    /************************/
+
     useEffect(() => {
-        getList({ pageNum: 1, pageSize: 20,...queryParmas })
-    }, [accountId,userId,queryParmas])
+        getList({ pageNum: 1, pageSize: 20, accountId, ...queryParmas })
+    }, [accountId, userId, queryParmas])
+
     // 获取列表
     const getList = useCallback((params: {
         pageNum: number;
         pageSize: number;
         accountId?: string;
-        adcreativeName?: string;
-        adgroupId?:string
+        adgroupName?: string;
+        adgroupId?: string;
+        promotedObjectType?: string;
+        isDeleted?: boolean
+        campaignId?: string
     }) => {
-        if (!params.adcreativeName || params.adcreativeName !== listAjax?.params[0]?.adcreativeName) {
-            !params.adcreativeName && delete params.adcreativeName
-            listAjax.run({ ...params,userId, accountId })
+        if (!params.adgroupName || params.adgroupName !== listAjax?.params[0]?.adgroupName) {
+            !params.adgroupName && delete params.adgroupName
+            listAjax.run({ ...params, userId })
         }
-    }, [accountId,userId,listAjax])
+    }, [userId, listAjax])
     // 同步 
     const sync = useCallback(() => {
-        if(!adAccountId){
+        if (!adAccountId) {
             message.error('请先选择要同步的广点通账号!')
             return
         }
-        syncAjax.run({adAccountId}).then(res => {
+        syncAjax.run({ adAccountId }).then(res => {
             res && listAjax.refresh()
             res ? message.success('同步成功!') : message.error('同步失败!')
 
         })
     }, [adAccountId, listAjax])
-    // 启停
-    const switchHandle=useCallback((data,checked)=>{
-        let { accountId,adgroupId}=data
-        console.log(checked)
-        let configuredStatus = checked ? 'AD_STATUS_NORMAL' : 'AD_STATUS_SUSPEND'
-        switchAjax.run({accountId,adgroupId,configuredStatus}).then(res=>{
-            res && listAjax.refresh()
-            res ? message.success(checked?'开启广告成功!':'关闭广告成功!') : message.error(checked?'开启广告失败':'关闭广告失败!')
+
+    /** 删除 */
+    const deleteHandle = (type: 0 | 1, adgroupId?: number) => {
+        delListAdqAdgroups.run({ adgroupIds: type === 1 ? selectedRows.map(item => item.adgroupId) : [adgroupId] }).then(res => {
+            message.success('删除成功')
+            setSelectedRows([])
+            listAjax.refresh()
         })
-    },[])
+    }
+
+    /** 修改排期出价 */
+    const editScheduling = () => {
+        setUpdate({ visible: true, title: '批量修改' })
+    }
+    /** 修改排期出价 */
+    const editDeepConversion = () => {
+        setUpdate({ visible: true, title: '批量修改深度优化' })
+    }
+
+    // 单个启停
+    const onChange = () => {
+        listAjax.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, adgroupId: number }) => item.configuredStatus === 'AD_STATUS_SUSPEND').map(item => item.adgroupId)
+        } else {
+            params.configuredStatus = 'AD_STATUS_SUSPEND'
+            params.adgroupIds = selectedRows.filter((item: { configuredStatus: string, adgroupId: number }) => item.configuredStatus === 'AD_STATUS_NORMAL').map(item => item.adgroupId)
+        }
+        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
+                });
+            }
+            listAjax.refresh()
+            setSelectedRows([])
+        })
+    }
+
+    // 批量复制
+    const copyHandle = () => {
+        setCopyData({ visible: true })
+    }
+
     return <div>
+        {/* 修改广告 */}
+        {update.visible && <UpdateAd
+            {...update}
+            selectedRows={selectedRows}
+            onChange={() => {
+                setUpdate({ visible: false, title: '' })
+                listAjax.refresh()
+                setSelectedRows([])
+            }}
+            onClose={() => { setUpdate({ visible: false, title: '' }) }}
+        />}
+        {/* 复制广告 */}
+        {copyData.visible && <Copy selectedRows={selectedRows} {...copyData} onClose={() => setCopyData({ visible: false })} onChange={() => { setCopyData({ visible: false }); listAjax.refresh(); setSelectedRows([]) }} />}
         <TableData
-            columns={()=>tableConfig(switchHandle,tableIdClick)}
+            isCard={false}
+            columns={() => tableConfig(onChange, tableIdClick)}
             ajax={listAjax}
             syncAjax={sync}
             dataSource={listAjax?.data?.data?.records}
             loading={listAjax?.loading || syncAjax?.loading}
-            scroll={{ x: 2500,y:550 }}
+            scroll={{ y: 560 }}
             total={listAjax?.data?.data?.total}
             page={listAjax?.data?.data?.current}
             pageSize={listAjax?.data?.data?.size}
             myKey={'adgroupId'}
-            leftChild={<>
-                <Row gutter={[10, 10]}>
+            leftChild={<Space direction='vertical'>
+                <Row gutter={[10, 10]} align='middle'>
+                    <Col>
+                        <Input
+                            placeholder='广告账号'
+                            allowClear
+                            style={{ width: 150 }}
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, accountId: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                        />
+                    </Col>
                     <Col>
                         <Input
                             placeholder='广告名称'
                             allowClear
+                            style={{ width: 150 }}
                             onBlur={(e) => {
                                 let value = e.target.value
-                                getList({ pageNum: 1, pageSize: 20, adcreativeName: value })
+                                getList({ pageNum: 1, pageSize: 20, adgroupName: value })
                             }}
                             onKeyDownCapture={(e: any) => {
                                 let key = e.key
                                 if (key === 'Enter') {
                                     let value = e.target.value
-                                    getList({ pageNum: 1, pageSize: 20, adcreativeName: value })
+                                    getList({ pageNum: 1, pageSize: 20, adgroupName: value })
                                 }
                             }}
                             onChange={(e) => {
                                 let value = e.target.value
                                 if (!value) {
-                                    getList({ pageNum: 1, pageSize: 20, adcreativeName: value })
+                                    getList({ pageNum: 1, pageSize: 20, adgroupName: value })
                                 }
                             }}
                         />
@@ -116,6 +217,7 @@ function Ad(props:Props) {
                         <Input
                             placeholder='广告ID'
                             allowClear
+                            style={{ width: 150 }}
                             onBlur={(e) => {
                                 let value = e.target.value
                                 getList({ pageNum: 1, pageSize: 20, adgroupId: value })
@@ -136,21 +238,104 @@ function Ad(props:Props) {
                         />
                     </Col>
                     <Col>
-                        <Select placeholder='推广目标选择' style={{ minWidth: 200 }} showSearch filterOption={(input: any, option: any) =>
-                            (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
-                        } allowClear onChange={(value: any) => {
-                            getList({ pageNum: 1, pageSize: 20, accountId })
-                        }}>
-                            {
-                                Object.keys(PromotedObjectType).map(key => {
-                                    // let obj = JSON.parse(PromotedObjectType[key])
-                                    return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
-                                })
+                        <Input
+                            placeholder='计划ID'
+                            allowClear
+                            style={{ width: 150 }}
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, campaignId: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, campaignId: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, campaignId: value })
+                                }
+                            }}
+                        />
+                    </Col>
+                    <Col>
+                        <Select
+                            placeholder='推广目标选择'
+                            style={{ width: 150 }}
+                            showSearch
+                            filterOption={(input: any, option: any) =>
+                                (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                            }
+                            allowClear
+                            onChange={(value: any) => {
+                                getList({ pageNum: 1, pageSize: 20, promotedObjectType: value })
+                            }}
+                        >
+                            {Object.keys(PromotedObjectType).map(key => {
+                                return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
+                            })}
+                        </Select>
+                    </Col>
+                    <Col>
+                        <Select
+                            placeholder='是否已删除'
+                            style={{ width: 150 }}
+                            showSearch
+                            filterOption={(input: any, option: any) =>
+                                (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                             }
+                            allowClear
+                            onChange={(value: any) => {
+                                getList({ pageNum: 1, pageSize: 20, isDeleted: value })
+                            }}
+                        >
+                            <Select.Option value={true}>已删除</Select.Option>
+                            <Select.Option value={false}>未删除</Select.Option>
                         </Select>
                     </Col>
                 </Row>
-            </>}
+                <Row gutter={[10, 10]} align='middle'>
+                    <Col>
+                        <Switch checkedChildren="普通模式" unCheckedChildren="深度优化" checked={model} onChange={(checked) => { setModel(checked); setSelectedRows([]) }} style={model ? {} : { background: '#67c23a' }} />
+                    </Col>
+                    {model ? <>
+                        <Col><Button type='primary' style={{ background: '#1890ff' }} icon={<FieldTimeOutlined />} disabled={selectedRows.length === 0} onClick={editScheduling}>修改排期出价</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>
+                            <Popconfirm
+                                title="确定删除?"
+                                onConfirm={() => deleteHandle(1)}
+                                okText="是"
+                                cancelText="否"
+                                disabled={selectedRows.length === 0}
+                            >
+                                <Button danger type='primary' loading={delListAdqAdgroups.loading} icon={<DeleteOutlined />} disabled={selectedRows.length === 0}>删除</Button>
+                            </Popconfirm>
+                        </Col>
+                    </> : <Col><Button type='primary' icon={<TransactionOutlined />} disabled={selectedRows.length === 0} onClick={editDeepConversion}>修改深度优化ROI</Button></Col>}
+                </Row>
+            </Space>}
+            rowSelection={{
+                selectedRowKeys: selectedRows.map(item => item.adgroupId.toString()),
+                getCheckboxProps: (record: any) => ({
+                    disabled: model ?
+                        record.status === 'STATUS_DELETED' :
+                        record.status === 'STATUS_DELETED' ||
+                        !(!model &&
+                            record?.promotedObjectType === 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT' &&
+                            record?.optimizationGoal === 'OPTIMIZATIONGOAL_FOLLOW' &&
+                            record?.deepConversionSpec?.deepConversionWorthSpec?.goal === 'GOAL_1DAY_PURCHASE_ROAS'
+                        )
+                }),
+                onChange: (selectedRowKeys: any, selectedRows: any) => {
+                    setSelectedRows(selectedRows)
+                }
+            }}
             onChange={(props: any) => {
                 let { sortData, pagination } = props
                 let { current, pageSize } = pagination

+ 40 - 0
src/pages/launchSystemNew/adq/ad/switchStatus.tsx

@@ -0,0 +1,40 @@
+import { useAjax } from "@/Hook/useAjax"
+import { editAdqAdgroupsDataApi } from "@/services/launchAdq/adq"
+import { message, notification, Switch } from "antd"
+import React from "react"
+
+
+
+/**
+ * 修改启停
+ */
+interface Props {
+    configuredStatus: string,
+    isDeleted?: boolean,
+    adgroupId: number,
+    onChange?: () => void
+}
+const SwitchStatus: React.FC<Props> = (prosp) => {
+
+    const { configuredStatus, isDeleted, adgroupId, onChange } = prosp
+    const editAdqAdgroupsData = useAjax((params) => editAdqAdgroupsDataApi(params))
+    
+    const switchHandle = (adgroupIds: number[], configuredStatus: string) => {
+        editAdqAdgroupsData.run({ adgroupIds, configuredStatus }).then(res => {
+            message.success(`${configuredStatus === 'AD_STATUS_NORMAL' ? '启动' : '暂停'}成功`)
+            if (res?.fail) {
+                notification.error({
+                    message: `${configuredStatus === 'AD_STATUS_NORMAL' ? '启动' : '暂停'}失败`,
+                    description: `修改失败${res.fail}条,失败的请到任务列表查看`,
+                    duration: 0
+                });
+            }
+            onChange?.()
+        })
+    }
+
+    return <Switch checked={configuredStatus === 'AD_STATUS_NORMAL'} loading={editAdqAdgroupsData.loading} disabled={isDeleted} onChange={(checked) => switchHandle([adgroupId] ,checked ? 'AD_STATUS_NORMAL' : 'AD_STATUS_SUSPEND')}/>
+}
+
+
+export default React.memo(SwitchStatus)

+ 99 - 39
src/pages/launchSystemNew/adq/ad/tableConfig.tsx

@@ -1,9 +1,12 @@
-import { AdStatusEnum, OptimizationGoalEnum, PromotedObjectType } from '@/services/launchAdq/enum'
+import { AdStatusEnum, BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType } from '@/services/launchAdq/enum'
 import React from 'react'
 import { Badge, Switch } from 'antd'
-import PreviewAd from '../../components/previewAd'
+import Box from '@/pages/adMonitor/adMonitorList/components/box'
+import { copy } from '@/utils/utils'
+import SwitchStatus from './switchStatus'
+import TimeSeriesLook from './timeSeriesLook'
 function tableConfig(
-    switchHandle: (data: any, Status: any) => void,
+    onChange: () => void,
     tableIdClick: (props: {
         activeKey: string,
         parma: {
@@ -18,15 +21,13 @@ function tableConfig(
     return [
         {
             title: '启停',
-            dataIndex: 'qt',
-            key: 'qt',
+            dataIndex: 'configuredStatus',
+            key: 'configuredStatus',
             align: 'center',
-            width: 50,
+            width: 55,
             fixed: 'left',
             render: (a: string, b: any) => {
-                return <Switch defaultChecked={b?.configuredStatus === 'AD_STATUS_NORMAL'} onChange={(checked: boolean) => {
-                    switchHandle(b, checked)
-                }} />
+                return <SwitchStatus configuredStatus={a} isDeleted={b?.isDeleted} adgroupId={b?.adgroupId} onChange={onChange}/>
             }
         },
         {
@@ -34,8 +35,9 @@ function tableConfig(
             dataIndex: 'accountId',
             key: 'accountId',
             align: 'center',
-            width: 90,
+            width: 70,
             fixed: 'left',
+            ellipsis: true,
             render: (a: string) => {
                 return <a onClick={() => {
                     tableIdClick({ activeKey: '1', parma: { accountId: a } })
@@ -47,8 +49,9 @@ function tableConfig(
             dataIndex: 'adgroupId',
             key: 'adgroupId',
             align: 'center',
-            width: 100,
+            width: 85,
             fixed: 'left',
+            ellipsis: true,
             render: (a: string) => {
                 return <a onClick={() => {
                     tableIdClick({ activeKey: '3', parma: { adgroupId: a } })
@@ -60,8 +63,9 @@ function tableConfig(
             dataIndex: 'campaignId',
             key: 'campaignId',
             align: 'center',
-            width: 100,
+            width: 85,
             fixed: 'left',
+            ellipsis: true,
             render: (a: string) => {
                 return <a onClick={() => {
                     tableIdClick({ activeKey: '2', parma: { campaignId: a } })
@@ -72,29 +76,32 @@ function tableConfig(
             title: '广告名称',
             dataIndex: 'adgroupName',
             key: 'adgroupName',
-            align: 'center',
-            width: 300,
+            width: 280,
+            ellipsis: true,
+            render: (a: string) => {
+                return <a style={{ wordBreak: 'break-all' }} onClick={() => { copy(a) }}>{a}</a>
+            }
         },
         {
             title: '推广目标类型',
             dataIndex: 'promotedObjectType',
             key: 'promotedObjectType',
             align: 'center',
-            width: 130,
+            width: 85,
             render: (a: string | number) => {
                 return PromotedObjectType[a]
             }
         },
-        {
-            title: '广告优化目标类型',
-            dataIndex: 'optimizationGoal',
-            key: 'optimizationGoal',
-            align: 'center',
-            width: 130,
-            render: (a: string | number) => {
-                return OptimizationGoalEnum[a]
-            }
-        },
+        // {
+        //     title: '广告优化目标类型',
+        //     dataIndex: 'optimizationGoal',
+        //     key: 'optimizationGoal',
+        //     align: 'center',
+        //     width: 70,
+        //     render: (a: string | number) => {
+        //         return OptimizationGoalEnum[a]
+        //     }
+        // },
         {
             title: '投放日期',
             dataIndex: 'beginDate',
@@ -105,33 +112,66 @@ function tableConfig(
                 return b?.endDate ? a + '~' + b.endDate : a + '~' + '长期投放'
             }
         },
+        {
+            title: '投放时间',
+            dataIndex: 'timeSeries',
+            key: 'timeSeries',
+            align: 'center',
+            width: 55,
+            render: (a: string, b: { endDate: string }) => {
+                return <TimeSeriesLook timeSeries={a}/>
+            }
+        },
         {
             title: '首日开始投放时间',
             dataIndex: 'firstDayBeginTime',
             key: 'firstDayBeginTime',
             align: 'center',
-            width: 130,
+            width: 75,
         },
         {
-            title: '广告出价(元)',
+            title: '出价',
             dataIndex: 'bidAmount',
             key: 'bidAmount',
+            width: 150,
+            ellipsis: true,
+            render: (a: string, b: { bidMode: string, optimizationGoal: string }) => {
+                return `${BidModeEnum[b?.bidMode]} ${a}元/${b?.bidMode === 'BID_MODE_CPM' ? '千次曝光' : b?.bidMode === 'BID_MODE_CPC' ? '点击' : OptimizationGoalEnum[b?.optimizationGoal]}`
+            }
+        },
+        {
+            title: '出价类型',
+            dataIndex: 'smartBidType',
+            key: 'smartBidType',
+            align: 'center',
+            width: 80,
+            render: (a: string, b: { endDate: string }) => {
+                return a === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'
+            }
+        },
+        {
+            title: '出价类型',
+            dataIndex: 'bidStrategy',
+            key: 'bidStrategy',
             align: 'center',
-            width: 130,
+            width: 80,
+            render: (a: string, b: { endDate: string }) => {
+                return BidStrategyEnum[a]
+            }
         },
         {
             title: '广告组日预算(分)',
             dataIndex: 'dailyBudget',
             key: 'dailyBudget',
             align: 'center',
-            width: 130,
+            width: 80,
         },
         {
             title: '是否开启自动版位功能',
             dataIndex: 'automaticSiteEnabled',
             key: 'automaticSiteEnabled',
             align: 'center',
-            width: 150,
+            width: 80,
             render: (a: any, b: any) => {
                 return a ? '开' : '关'
             }
@@ -141,7 +181,7 @@ function tableConfig(
             dataIndex: 'createdTime',
             key: 'createdTime',
             align: 'center',
-            width: 160,
+            width: 140,
         },
         // {
         //     title: '客户设置的状态',
@@ -158,7 +198,7 @@ function tableConfig(
             dataIndex: 'isDeleted',
             key: 'isDeleted',
             align: 'center',
-            width: 70,
+            width: 60,
             fixed: 'right',
             render: (a: any, b: any) => {
                 return <Badge status={!a ? "processing" : "error"} text={a ? '是' : '否'} />
@@ -169,21 +209,41 @@ function tableConfig(
             dataIndex: 'status',
             key: 'status',
             align: 'center',
-            width: 70,
+            width: 80,
             fixed: 'right',
             render: (a: string) => {
                 return AdStatusEnum[a]
             }
         },
+        {
+            title: '创意预览',
+            dataIndex: 'creativePreview',
+            key: 'creativePreview',
+            width: 70,
+            align: 'center',
+            fixed: 'right',
+            render: (a: any, b: any) => {
+                return <Box b={b} />
+            }
+        },
         // {
-        //     title: '预览',
-        //     dataIndex: 'preview',
-        //     key: 'preview',
+        //     title: '操作',
+        //     dataIndex: 'cz',
+        //     key: 'cz',
+        //     width: 65,
         //     align: 'center',
-        //     width: 40,
         //     fixed: 'right',
-        //     render: (a: string, b: any) => {
-        //         return <PreviewAd adgroupId={b?.adgroupId} accountId={b?.accountId}/>
+        //     render: (a: any, b: any) => {
+        //         return <Space>
+        //             {b?.status !== 'STATUS_DELETED' && <Popconfirm
+        //                 title="确定删除?"
+        //                 onConfirm={() => deleteHandle(0, b?.adgroupId)}
+        //                 okText="是"
+        //                 cancelText="否"
+        //             >
+        //                 <Button danger type='text' size='small'>删除</Button>
+        //             </Popconfirm>}
+        //         </Space>
         //     }
         // },
     ]

+ 71 - 0
src/pages/launchSystemNew/adq/ad/timeSeriesLook.tsx

@@ -0,0 +1,71 @@
+import { EyeOutlined } from "@ant-design/icons";
+import { Tooltip } from "antd";
+import React, { useEffect, useState } from "react"
+import { getTimeSeriesList, WEEK_SERIRES } from "./const"
+const totalHour = 48;
+
+interface Props {
+    timeSeries: string,  // 投放时间
+}
+const TimeSeriesLook: React.FC<Props> = (prosp) => {
+
+    const { timeSeries } = prosp
+    const [text, setText] = useState<string[]>([])
+
+
+    useEffect(() => {
+        if (timeSeries) {
+            if (timeSeries === getTimeSeriesList().join('')) {
+                setText([])
+            } else {
+                let newTimesText: string[] = []
+                WEEK_SERIRES.forEach((item, index) => {
+                    let data = timeSeries.slice(index * totalHour, totalHour + totalHour * index)
+                    let times: string[] = []
+                    let startTime = ''
+                    let endTime = ''
+                    for (let i = 0; i < data.length; i++) {
+                        if (data[i] === '1') {
+                            const hour = Math.floor(i / 2);
+                            const curHour = ('00' + hour).slice(-2);
+                            const curMinute = i % 2 ? '30' : '00'
+                            const nextHour = ('00' + (hour + 1)).slice(-2);
+                            if (!startTime) {
+                                startTime = `${curHour}:${curMinute}`
+                                endTime = i % 2 ? `${nextHour}:00` : `${curHour}:${curMinute}`
+                            } else {
+                                endTime = i % 2 ? `${nextHour}:00` : `${curHour}:${curMinute}`
+                            }
+                            if (data.length === i + 1) {
+                                if (startTime && endTime) {
+                                    times.push(`${startTime}-${endTime}`)
+                                }
+                            }
+                        } else {
+                            if (startTime && endTime) {
+                                times.push(`${startTime}-${endTime}`)
+                            }
+                            startTime = ''
+                            endTime = ''
+                        }
+
+                    }
+                    if (times.length > 0) {
+                        newTimesText.push(`${item}:${times.toString()}`)
+                    }
+                })
+                setText(newTimesText)
+            }
+        } else {
+            setText([])
+        }
+    }, [timeSeries])
+
+    return <>
+        {text.length === 0 ? '不限' : <Tooltip title={text.map((item, index) => <div key={index}>{item}</div>)}>
+            <a><EyeOutlined /></a>
+        </Tooltip>}
+    </>
+}
+
+export default React.memo(TimeSeriesLook)

+ 251 - 0
src/pages/launchSystemNew/adq/ad/updateAd.tsx

@@ -0,0 +1,251 @@
+import { Checkbox, DatePicker, Form, Input, message, Modal, notification, Radio, Select, Space, TimePicker, Tooltip } from "antd"
+import React, { useEffect, useState } from "react"
+import TimeInSelect from "../../components/timeInSelect"
+import moment from "moment"
+import { getTimeSeriesList } from "./const";
+import { editAdqAdgroupsDataApi } from "@/services/launchAdq/adq";
+import { useAjax } from "@/Hook/useAjax";
+import { BidStrategyEnum, GoalRoasEnum } from "@/services/launchAdq/enum";
+import { QuestionCircleOutlined } from "@ant-design/icons";
+import { RangePickerProps } from "antd/lib/date-picker";
+const { RangePicker } = DatePicker;
+let DatePickers: any = DatePicker
+
+interface Props {
+    title: string,
+    visible?: boolean,
+    onChange?: () => void,
+    onClose?: () => void,
+    selectedRows: any[]
+}
+/**
+ * 修改广告
+ * @returns 
+ */
+const UpdateAd: React.FC<Props> = ({ title = '修改广告', visible, onChange, onClose, selectedRows }) => {
+
+    /***************************/
+    const [form] = Form.useForm();
+    let dateType = Form.useWatch('dateType', form)
+    let field = Form.useWatch('field', form)
+
+    const [state, setState] = useState<any>({ isShowTime: [] })
+    const [isBidSType, setIsBidSType] = useState<boolean>(true)
+    const [timeSeriesType, setTimeSeriesType] = useState<'allDayLong' | 'timeInterValS'>('allDayLong')
+
+    const editAdqAdgroupsData = useAjax((params) => editAdqAdgroupsDataApi(params))
+    /***************************/
+
+    useEffect(() => {
+        setIsBidSType(selectedRows.every((item: { smartBidType: string }) => item.smartBidType === 'SMART_BID_TYPE_CUSTOM'))
+    }, [selectedRows])
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            console.log(values);
+            let newValues = JSON.parse(JSON.stringify(values))
+            let field = newValues.field
+            let adgroupsUpdateDatetimeDTO = {}  // 排期
+            let adgroupsUpdateBidAmountDTO = {} // 出价
+            let deepConversionSpec = {} // ROI
+            if (timeSeriesType === 'allDayLong') {
+                newValues.timeSeries = getTimeSeriesList()
+            }
+
+            if (title === '批量修改深度优化') {
+                deepConversionSpec['deepConversionType'] = newValues.deepConversionType
+                deepConversionSpec['deepConversionWorthSpec'] = {
+                    expectedRoi: newValues.deepBidAmount,
+                    goal: newValues.goal
+                }
+            } else {
+                field?.forEach((key: string) => {
+                    switch (key) {
+                        case 'dateType':
+                            if (newValues.dateType === '2') {
+                                adgroupsUpdateDatetimeDTO['beginDate'] = moment(newValues.date).format('YYYY-MM-DD')
+                                adgroupsUpdateDatetimeDTO['endDate'] = ''
+                            } else {
+                                adgroupsUpdateDatetimeDTO['beginDate'] = moment(newValues.date[0]).format('YYYY-MM-DD')
+                                adgroupsUpdateDatetimeDTO['endDate'] = moment(newValues.date[1]).format('YYYY-MM-DD')
+                            }
+                            break
+                        case 'timeType':
+                            // if (timeSeriesType === 'timeInterValS') {
+                            //     if (newValues.firstDayBeginTime && moment(newValues.firstDayBeginTime).format('HH:mm:ss') !== '00:00:00') {
+                            //         message.error('1111')
+                            //         return
+                            //     }
+                            // }
+                            if (newValues.timeSeries) {
+                                adgroupsUpdateDatetimeDTO['timeSeries'] = newValues.timeSeries.join('')
+                            }
+                            if (newValues.firstDayBeginTime) {
+                                adgroupsUpdateDatetimeDTO['firstDayBeginTime'] = moment(newValues.firstDayBeginTime).format('HH:mm:ss')
+                            }
+                            break
+                        case 'bidSType':
+                            adgroupsUpdateBidAmountDTO['bidStrategy'] = newValues.bidStrategy
+                            adgroupsUpdateBidAmountDTO['bidAmount'] = newValues.bidAmount * 100
+                            break
+                    }
+                })
+            }
+            // console.log('params--->', adgroupsUpdateDatetimeDTO);
+            editAdqAdgroupsData.run({ adgroupIds: selectedRows.map((item: { adgroupId: number }) => item.adgroupId), adgroupsUpdateDatetimeDTO, adgroupsUpdateBidAmountDTO, deepConversionSpec }).then(res => {
+                if (res) {
+                    message.success(`修改完成.成功: ${res.success},失败: ${res.fail}`)
+                    if (res?.fail) {
+                        notification.error({
+                            message: `修改失败`,
+                            description: `成功: ${res.success},修改失败${res.fail}条,失败的请到任务列表查看`,
+                            duration: 0
+                        });
+                    }
+                    onChange?.()
+                }
+
+            })
+        })
+    }
+
+    /** 禁止选择以前时间 */
+    const disabledDate: RangePickerProps['disabledDate'] = current => {
+        // Can not select days before today and today
+        return current && current < moment().startOf('day');
+    };
+
+    return <Modal
+        title={title}
+        visible={visible}
+        onOk={handleOk}
+        width={800}
+        onCancel={() => onClose && onClose()}
+        confirmLoading={editAdqAdgroupsData.loading}
+    >
+        <Form
+            form={form}
+            labelCol={{ span: 4 }}
+            className='ad_form_style'
+            colon={false}
+            initialValues={{
+                dateType: '2',
+                date: moment().startOf('day'),
+                timeSeries: getTimeSeriesList(),
+                firstDayBeginTime: moment('2023-02-24 00:00:00'),
+                bidStrategy: 'BID_STRATEGY_TARGET_COST',
+                bidAmount: 1000,
+                optimizationMode: 'DEEP_CONVERSION_TARGET',
+                deepConversionType: 'DEEP_CONVERSION_WORTH',
+                goal: 'GOAL_1DAY_PURCHASE_ROAS'
+            }}
+        >
+
+            {title === '批量修改' ? <>
+                <Form.Item label={<strong>选择修改字段</strong>} name='field' rules={[{ required: true, message: '选择修改字段' }]}>
+                    <Checkbox.Group>
+                        <Checkbox value="dateType">投放日期</Checkbox>
+                        <Checkbox value="timeType">投放时段</Checkbox>
+                        <Checkbox value="bidSType" disabled={!isBidSType}>
+                            <Space size={5}>
+                                出价
+                                {!isBidSType && <Tooltip title="选择的广告包含自动出价类型,不支持修改出价">
+                                    <QuestionCircleOutlined />
+                                </Tooltip>}
+                            </Space>
+                        </Checkbox>
+                    </Checkbox.Group>
+                </Form.Item>
+                {field?.includes('dateType') && <>
+                    <Form.Item label={<strong>投放日期</strong>} name='dateType'>
+                        <Radio.Group onChange={(e) => {
+                            if (e.target.value === "1") {
+                                form.setFieldsValue({ date: [moment().startOf('day'), moment().startOf('day').add(1, 'M')] })
+                            }
+                            if (e.target.value === "2") {
+                                form.setFieldsValue({ date: moment().startOf('day') })
+                            }
+                        }}>
+                            <Radio.Button value="1">选择开始与结束日期</Radio.Button>
+                            <Radio.Button value="2">长期投放</Radio.Button>
+                        </Radio.Group>
+                    </Form.Item>
+                    {/* 投放日期的不同展示不同的日期选择 */}
+                    {dateType === '1' ? <Form.Item name='date' rules={[{ required: true, message: '请选择日期' }]}>
+                        <RangePicker style={{ marginLeft: 125 }} disabledDate={disabledDate}></RangePicker>
+                    </Form.Item> : <Form.Item name='date' style={{ marginLeft: 125 }} rules={[{ required: true, message: '请选择日期' }]}>
+                        <DatePickers disabledDate={disabledDate} />
+                    </Form.Item>}
+                </>}
+                {field?.includes('timeType') && <>
+                    <Form.Item label={<strong>投放时段</strong>} style={{ marginBottom: 0 }}>
+                        <Space direction='vertical' style={{ width: '100%' }}>
+                            <Radio.Group
+                                name='timeSeriesType'
+                                value={timeSeriesType}
+                                onChange={(value) => {
+                                    setTimeSeriesType(value.target.value)
+                                    if (value.target.value === 'timeInterValS') {
+                                        setState({ isShowTime: ['1'] })
+                                    }
+                                }}>
+                                <Radio.Button value={'allDayLong'}>全天投放</Radio.Button>
+                                <Radio.Button value={'timeInterValS'}>指定多个时段</Radio.Button>
+                            </Radio.Group>
+                            {timeSeriesType === 'timeInterValS' && <Form.Item name='timeSeries' noStyle rules={[{ required: true, message: '请选择时段' }]}>
+                                <TimeInSelect />
+                            </Form.Item>}
+                        </Space>
+                    </Form.Item>
+                    <Form.Item label={<strong></strong>}>
+                        <Space>
+                            <Checkbox.Group options={[{ label: '指定首日开始投放时间', value: '1' }]} disabled={timeSeriesType === 'timeInterValS'} value={state.isShowTime} onChange={(checkedValue) => { setState({ ...state, isShowTime: checkedValue }) }} />
+                            {state?.isShowTime?.length > 0 && <Form.Item name='firstDayBeginTime' noStyle rules={[{ required: true, message: '请选择时间' }]}>
+                                <TimePicker />
+                            </Form.Item>}
+                        </Space>
+                    </Form.Item>
+                </>}
+                {field?.includes('bidSType') && <>
+                    <Form.Item label={<strong>出价策略</strong>} name='bidStrategy' rules={[{ required: true, message: '请选择出价策略' }]}>
+                        <Radio.Group>
+                            {Object.keys(BidStrategyEnum).map(key => {
+                                return <Radio.Button value={key} key={key} disabled={key === 'BID_STRATEGY_PRIORITY_CAP_COST'}>{BidStrategyEnum[key]}</Radio.Button>
+                            })}
+                        </Radio.Group>
+                    </Form.Item>
+                    <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
+                        <Input placeholder={`输入价格 元`} style={{ width: 300 }} />
+                    </Form.Item>
+                </>}
+            </> : <>
+                <Form.Item label={<strong>深度优化方式</strong>} name='optimizationMode' rules={[{ required: true, message: '请选择深度优化方式' }]}>
+                    <Radio.Group disabled>
+                        <Radio.Button value="DEEP_CONVERSION_TARGET">深度目标优化</Radio.Button>
+                    </Radio.Group>
+                </Form.Item>
+                <Form.Item label={<strong>深度优化类型</strong>} name='deepConversionType' rules={[{ required: true, message: '请选择深度优化类型' }]}>
+                    <Radio.Group disabled onChange={() => {
+                        form.setFieldsValue({
+                            goal: undefined,
+                            deepBidAmount: undefined
+                        })
+                    }}>
+                        <Radio.Button value="DEEP_CONVERSION_WORTH">优化 ROI</Radio.Button>
+                    </Radio.Group>
+                </Form.Item>
+                <Form.Item label={<strong>深度优化目标</strong>} name='goal' rules={[{ required: true, message: '请选择深度优化目标' }]}>
+                    <Select style={{ width: 380 }} placeholder='请选择'>
+                        {Object.keys(GoalRoasEnum).filter(key => ['GOAL_1DAY_PURCHASE_ROAS']?.includes(key)).map(key => <Select.Option value={key} key={key}>{GoalRoasEnum[key]}</Select.Option>)}
+                    </Select>
+                </Form.Item>
+                <Form.Item label={<strong>期望ROI</strong>} name='deepBidAmount' rules={[{ required: true, message: '请输入期望ROI' }]}>
+                    <Input style={{ width: 380 }} placeholder={`期望ROI目标范围0.001~1000,输入0.05,表示ROI目标为5%`} />
+                </Form.Item>
+            </>}
+        </Form>
+    </Modal>
+}
+
+
+export default React.memo(UpdateAd)

+ 1 - 0
src/pages/launchSystemNew/adq/adAccount/index.tsx

@@ -64,6 +64,7 @@ function AdAccount(props:Props) {
     }, [adAccountId, listAjax,selectedRowKeys])
     return <div>
         <TableData
+            isCard={false}
             columns={()=>tableConfig(tableIdClick)}
             ajax={listAjax}
             syncAjax={sync}

+ 35 - 8
src/pages/launchSystemNew/adq/campaign/index.tsx

@@ -31,15 +31,15 @@ type Props = {
 }
 
 function Campaign(props: Props) {
-    let { accountId, adAccountId, userId,queryParmas, tableIdClick } = props
+    let { accountId, adAccountId, userId, queryParmas, tableIdClick } = props
     // api方法
     const listAjax = useAjax((params) => getAdqCampaignList(params), { formatResult: true })
     const syncAjax = useAjax((adAccountId) => putAdqCampaignPage(adAccountId))
     const switchAjax = useAjax((params) => putAdqCampaignConfigStatus(params))
     console.log('创意=====》')
     useEffect(() => {
-        getList({ pageNum: 1, pageSize: 20,...queryParmas })
-    }, [accountId, userId,queryParmas])
+        getList({ pageNum: 1, pageSize: 20, accountId, ...queryParmas })
+    }, [accountId, userId, queryParmas])
     // 获取列表
     const getList = useCallback((params: {
         pageNum: number;
@@ -54,9 +54,9 @@ function Campaign(props: Props) {
     }) => {
         if (!params.campaignName || params.campaignName !== listAjax?.params[0]?.adcreativeName) {
             !params.campaignName && delete params.campaignName
-            listAjax.run({ ...params, userId, accountId })
+            listAjax.run({ ...params, userId })
         }
-    }, [accountId, userId, listAjax])
+    }, [userId, listAjax])
     // 同步 
     const sync = useCallback(() => {
         if (!adAccountId) {
@@ -81,6 +81,7 @@ function Campaign(props: Props) {
     }, [])
     return <div>
         <TableData
+            isCard={false}
             columns={() => tableConfig(switchHandle, tableIdClick)}
             ajax={listAjax}
             syncAjax={sync}
@@ -93,10 +94,35 @@ function Campaign(props: Props) {
             myKey={'campaignId'}
             leftChild={<>
                 <Row gutter={[10, 10]}>
+                    <Col>
+                        <Input
+                            placeholder='广告账号'
+                            allowClear
+                            style={{ width: 150 }}
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, accountId: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                        />
+                    </Col>
                     <Col>
                         <Input
                             placeholder='计划名称'
                             allowClear
+                            style={{ width: 150 }}
                             onBlur={(e) => {
                                 let value = e.target.value
                                 getList({ pageNum: 1, pageSize: 20, campaignName: value })
@@ -120,6 +146,7 @@ function Campaign(props: Props) {
                         <Input
                             placeholder='计划ID'
                             allowClear
+                            style={{ width: 150 }}
                             onBlur={(e) => {
                                 let value = e.target.value
                                 getList({ pageNum: 1, pageSize: 20, campaignId: value })
@@ -140,7 +167,7 @@ function Campaign(props: Props) {
                         />
                     </Col>
                     <Col>
-                        <Select placeholder='推广目标选择' style={{ minWidth: 200 }} showSearch filterOption={(input: any, option: any) =>
+                        <Select placeholder='推广目标选择' style={{ minWidth: 150 }} showSearch filterOption={(input: any, option: any) =>
                             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                         } allowClear onChange={(value: any) => {
                             getList({ pageNum: 1, pageSize: 20, promotedObjectType: value })
@@ -154,7 +181,7 @@ function Campaign(props: Props) {
                         </Select>
                     </Col>
                     <Col>
-                        <Select placeholder='计划状态' style={{ minWidth: 200 }} showSearch filterOption={(input: any, option: any) =>
+                        <Select placeholder='计划状态' style={{ minWidth: 150 }} showSearch filterOption={(input: any, option: any) =>
                             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                         } allowClear onChange={(value: any) => {
                             getList({ pageNum: 1, pageSize: 20, configuredStatus: value })
@@ -168,7 +195,7 @@ function Campaign(props: Props) {
                         </Select>
                     </Col>
                     <Col>
-                        <Select placeholder='计划类型' style={{ minWidth: 200 }} showSearch filterOption={(input: any, option: any) =>
+                        <Select placeholder='计划类型' style={{ minWidth: 150 }} showSearch filterOption={(input: any, option: any) =>
                             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                         } allowClear onChange={(value: any) => {
                             getList({ pageNum: 1, pageSize: 20, campaignType: value })

+ 36 - 9
src/pages/launchSystemNew/adq/creative/index.tsx

@@ -30,14 +30,14 @@ type Props = {
     }) => void
 }
 function Creative(props: Props) {
-    let { accountId, adAccountId,userId,tableIdClick ,queryParmas} = props
+    let { accountId, adAccountId, userId, tableIdClick, queryParmas } = props
     // api方法
     const listAjax = useAjax((params) => getAdqAdcreativeList(params), { formatResult: true })
     const syncAjax = useAjax((adAccountId) => putAdqTargetingSyncAll(adAccountId))
     console.log('创意=====》')
     useEffect(() => {
-        getList({ pageNum: 1, pageSize: 20,...queryParmas })
-    }, [accountId,userId,queryParmas])
+        getList({ pageNum: 1, pageSize: 20, accountId, ...queryParmas })
+    }, [accountId, userId, queryParmas])
     // 获取列表
     const getList = useCallback((params: {
         pageNum: number;
@@ -48,12 +48,12 @@ function Creative(props: Props) {
     }) => {
         if (!params.adcreativeName || params.adcreativeName !== listAjax?.params[0]?.adcreativeName) {
             !params.adcreativeName && delete params.adcreativeName
-            listAjax.run({ ...params,userId, accountId })
+            listAjax.run({ ...params, userId })
         }
-    }, [accountId,userId,listAjax])
+    }, [userId, listAjax])
     // 同步 
     const sync = useCallback(() => {
-        if(!adAccountId){
+        if (!adAccountId) {
             message.error('请先选择要同步的广点通账号!')
             return
         }
@@ -66,22 +66,48 @@ function Creative(props: Props) {
     }, [adAccountId, listAjax])
     return <div>
         <TableData
-            columns={()=>tableConfig(tableIdClick)}
+            isCard={false}
+            columns={() => tableConfig(tableIdClick)}
             ajax={listAjax}
             syncAjax={sync}
             dataSource={listAjax?.data?.data?.records}
             loading={listAjax?.loading || syncAjax?.loading}
-            scroll={{x:1200, y:550 }}
+            scroll={{ x: 1200, y: 550 }}
             total={listAjax?.data?.data?.total}
             page={listAjax?.data?.data?.current}
             pageSize={listAjax?.data?.data?.size}
             myKey={'adcreativeId'}
             leftChild={<>
                 <Row gutter={[10, 10]}>
+                    <Col>
+                        <Input
+                            placeholder='广告账号'
+                            allowClear
+                            style={{ width: 150 }}
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, accountId: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                        />
+                    </Col>
                     <Col>
                         <Input
                             placeholder='创意名称'
                             allowClear
+                            style={{ width: 150 }}
                             onBlur={(e) => {
                                 let value = e.target.value
                                 getList({ pageNum: 1, pageSize: 20, adcreativeName: value })
@@ -105,6 +131,7 @@ function Creative(props: Props) {
                         <Input
                             placeholder='创意ID'
                             allowClear
+                            style={{ width: 150 }}
                             onBlur={(e) => {
                                 let value = e.target.value
                                 getList({ pageNum: 1, pageSize: 20, adcreativeId: value })
@@ -125,7 +152,7 @@ function Creative(props: Props) {
                         />
                     </Col>
                     <Col>
-                        <Select placeholder='推广目标选择' style={{ minWidth: 200 }} showSearch filterOption={(input: any, option: any) =>
+                        <Select placeholder='推广目标选择' style={{ minWidth: 150 }} showSearch filterOption={(input: any, option: any) =>
                             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                         } allowClear onChange={(value: any) => {
                             getList({ pageNum: 1, pageSize: 20, accountId })

+ 28 - 0
src/pages/launchSystemNew/adq/index.less

@@ -0,0 +1,28 @@
+
+.adq {
+    display: flex;
+    justify-content: space-between;
+    position: relative;
+    width: 100%;
+    height: 100%;
+
+    .left {
+        height: 100%;
+        min-height: calc(100vh - 100px);
+        overflow: hidden;
+        width: 150px; 
+        background: #fff;
+    }
+
+    .right {
+        width: calc(100% - 150px);
+        position: relative;
+
+        .hiddenBtn {
+            position: absolute;
+            top: 0;
+            left: 0;
+            z-index: 10;
+        }
+    }
+}

+ 31 - 30
src/pages/launchSystemNew/adq/index.tsx

@@ -1,6 +1,6 @@
 import React, { useState, useEffect, useCallback } from 'react'
-import { Badge, Card, Col, Menu, Row, Select, Tabs, Tag } from 'antd';
-// import './index.less'
+import { Badge, Button, Card, Col, Menu, Row, Select, Tabs, Tag } from 'antd';
+import style from './index.less'
 import Campaign from './campaign';
 import AdAccount from './adAccount';
 import Ad from './ad';
@@ -9,23 +9,25 @@ import LandingPage from './landingPage';
 import { getAdAccountAllOfMember } from '@/services/launchAdq/adq';
 import Targeting from './targeting';
 import { useAjax } from '@/Hook/useAjax';
-import { IdcardFilled } from '@ant-design/icons';
+import { IdcardFilled, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
 import { useModel } from 'umi';
+import Log from './log';
 const { TabPane } = Tabs;
 let Menus: any = Menu
 const tabsConfig = [
     { key: '1', tab: '账户信息', jsx: (props: any) => <AdAccount {...props} /> },
-    { key: '2', tab: '计划', jsx: (props: any) => <Campaign {...props} /> },
     { key: '3', tab: '广告', jsx: (props: any) => <Ad {...props} /> },
+    { key: '2', tab: '计划', jsx: (props: any) => <Campaign {...props} /> },
     { key: '4', tab: '创意', jsx: (props: any) => <Creative {...props} /> },
     { key: '5', tab: '落地页', jsx: (props: any) => <LandingPage {...props} /> },
     { key: '6', tab: '定向', jsx: (props: any) => <Targeting {...props} /> },
+    { key: '7', tab: '操作记录', jsx: (props: any) => <Log {...props} /> },
 ]
 function Adq() {
     const userInfo = useModel('@@initialState', model => model.initialState?.currentUser)
     const allOfMember = useAjax(() => getAdAccountAllOfMember(), { formatResult: true })
     const [userAll, setUserAll] = useState([])
-    const [activeKey, setActiveKey] = useState('1')
+    const [activeKey, setActiveKey] = useState('3')
     const [userId, setUserId] = useState<any>(userInfo?.userId?.toString())
     const [selectedArr, setSelectedArr] = useState([])
     const [queryParmas, setQueryParmas] = useState<any>({
@@ -40,6 +42,7 @@ function Adq() {
         accountId: '',
         adAccountId: ''
     })
+    const [hide, setHide] = useState<boolean>(false)
     // 点击table中的ID添加对应条件
     const tableIdClick = useCallback((props: {
         activeKey: string,
@@ -55,8 +58,7 @@ function Adq() {
         // 暂时注释不开放
         let { activeKey, parma } = props
         setQueryParmas({ ...queryParmas, ...parma })
-        setActiveKey(activeKey)
-
+        // setActiveKey(activeKey)
     }, [queryParmas, activeKey])
     // 获取组员
     useEffect(() => {
@@ -109,12 +111,12 @@ function Adq() {
         }
     }, [userId, userAll])
     //判定是否从任务列表跳转过来
-    useEffect(()=>{
+    useEffect(() => {
         let data = sessionStorage.getItem('adqQuery')
-        if(data){
+        if (data) {
             let obj = JSON.parse(data)
-            Object.keys(obj).forEach(key=>{
-                switch(key){
+            Object.keys(obj).forEach(key => {
+                switch (key) {
                     case 'accountId':
                         setQueryParmas(obj)
                         setActiveKey('1')
@@ -135,27 +137,27 @@ function Adq() {
             })
             sessionStorage.removeItem('adqQuery')
         }
-    },[])
-    return <Row style={{ position: 'relative' }}>
-        <Col style={{ height: '100%', minHeight: 'calc(100vh - 100px)', overflowY: 'auto', position: 'absolute', left: 0, top: 0, bottom: 0, width: 150, background: '#fff' }}>
+    }, [])
+
+    return <div className={style.adq}>
+        {!hide && <div className={style.left}>
             <Select
                 placeholder='名称搜索'
                 style={{ width: '100%' }}
                 showSearch
                 filterOption={(input: any, option: any) => {
                     return (option!?.children as unknown as string)?.toLowerCase()?.includes(input?.toLowerCase())
-                }
-
-                } allowClear onChange={(value: any) => {
+                }}
+                allowClear
+                onChange={(value: any) => {
                     if (value) {
                         setUserId(value.toString())
                     }
-                }}>
-                {
-                    userAll.map((item: any) => {
-                        return <Select.Option value={item.key} key={item.key}>{item.label}</Select.Option>
-                    })
-                }
+                }}
+            >
+                {userAll.map((item: any) => {
+                    return <Select.Option value={item.key} key={item.key}>{item.label}</Select.Option>
+                })}
             </Select>
             <Menus
                 id='myMenus'
@@ -173,15 +175,16 @@ function Adq() {
                     overflowX: 'hidden'
                 }}
             />
-        </Col>
-        <Col span={21} style={{ marginLeft: 150 }}>
+        </div>}
+        <div className={style.right} style={!hide ? { width: 'calc(100% - 150px)' } : { width: '100%' }}>
+            <div className={style.hiddenBtn}><Button size='small' type="primary" onClick={() => setHide(!hide)} icon={!hide ? <MenuFoldOutlined /> : <MenuUnfoldOutlined />} /></div>
             <Card>
                 <Tabs activeKey={activeKey} type="card" onChange={(activeKey) => { setActiveKey(activeKey) }} tabBarExtraContent={
                     <Select
                         placeholder='广点通账号'
                         style={{ minWidth: 200 }}
                         showSearch
-                        allowClear 
+                        allowClear
                         onChange={(value: any) => {
                             let accountId = ''
                             let adAccountId = ''
@@ -205,7 +208,6 @@ function Adq() {
                         tabsConfig?.map(item => {
                             return <TabPane tab={item.tab} key={item.key} >
                                 <>
-                                    {/* 点击了列表中的ID默认展示并搜索对应ID条件 */}
                                     <Row >
                                         <Col>
                                             {
@@ -248,8 +250,7 @@ function Adq() {
                     }
                 </Tabs>
             </Card>
-        </Col>
-    </Row>
-
+        </div>
+    </div>
 }
 export default Adq

+ 34 - 8
src/pages/launchSystemNew/adq/landingPage/index.tsx

@@ -5,13 +5,13 @@ import { getAdqLandingPageList, putAdqLandingPage } from '@/services/launchAdq/a
 import TableData from '../../components/TableData'
 import tableConfig from './tableConfig'
 
-function LandingPage(props: { accountId: string, adAccountId: string, userId: string }) {
-    let { accountId, adAccountId, userId } = props
+function LandingPage(props: { accountId: string, adAccountId: string, userId: string, tableIdClick: any }) {
+    let { accountId, adAccountId, userId, tableIdClick } = props
     const listAjax = useAjax((params) => getAdqLandingPageList(params), { formatResult: true })
     const syncAjax = useAjax((adAccountId) => putAdqLandingPage(adAccountId))
     console.log('落地页=====》')
     useEffect(() => {
-        getList({ pageNum: 1, pageSize: 20 })
+        getList({ pageNum: 1, pageSize: 20, accountId })
     }, [accountId])
     // 获取列表
     const getList = useCallback((params: {
@@ -25,12 +25,12 @@ function LandingPage(props: { accountId: string, adAccountId: string, userId: st
     }) => {
         if (!params.pageName || params.pageName !== listAjax?.params[0]?.pageName) {
             !params.pageName && delete params.pageName
-            listAjax.run({ ...params, userId, accountId })
+            listAjax.run({ ...params, userId })
         }
-    }, [accountId, listAjax, userId])
+    }, [listAjax, userId])
     // 同步 
     const sync = useCallback(() => {
-        if(!adAccountId){
+        if (!adAccountId) {
             message.error('请先选择要同步的广点通账号!')
             return
         }
@@ -44,22 +44,48 @@ function LandingPage(props: { accountId: string, adAccountId: string, userId: st
 
     return <div>
         <TableData
-            columns={tableConfig}
+            isCard={false}
+            columns={() => tableConfig(tableIdClick)}
             ajax={listAjax}
             syncAjax={sync}
             dataSource={listAjax?.data?.data?.records}
             loading={listAjax?.loading || syncAjax?.loading}
-            scroll={{ y:550 }}
+            scroll={{ y: 550 }}
             total={listAjax?.data?.data?.total}
             page={listAjax?.data?.data?.current}
             pageSize={listAjax?.data?.data?.size}
             myKey={'pageId'}
             leftChild={<>
                 <Row gutter={[10, 10]}>
+                    <Col>
+                        <Input
+                            placeholder='广告账号'
+                            allowClear
+                            style={{ width: 150 }}
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, accountId: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                        />
+                    </Col>
                     <Col>
                         <Input
                             placeholder='落地页名称'
                             allowClear
+                            style={{ width: 150 }}
                             onBlur={(e) => {
                                 let value = e.target.value
                                 getList({ pageNum: 1, pageSize: 20, pageName: value })

+ 4 - 3
src/pages/launchSystemNew/adq/landingPage/tableConfig.tsx

@@ -1,7 +1,8 @@
 import {  PageStatusEnum, PageTypeEnum, SourceTypeEnum } from '@/services/launchAdq/enum'
 import React from 'react'
 import { Badge } from 'antd'
-function tableConfig(): any {
+import { copy } from '@/utils/utils'
+function tableConfig(tableIdClick: any): any {
     return [
         {
             title: '所属账号',
@@ -10,7 +11,7 @@ function tableConfig(): any {
             align: 'center',
             width:100,
             render:(a:string)=>{
-                return <a>{a}</a>
+                return <a onClick={() => tableIdClick({ activeKey: '1', parma: { accountId: a } })}>{a}</a>
             }
         },
         {
@@ -20,7 +21,7 @@ function tableConfig(): any {
             align: 'center',
             width:100,
             render:(a:string)=>{
-                return <a>{a}</a>
+                return <a onClick={() => copy(a)}>{a}</a>
             }
         },
         {

+ 139 - 0
src/pages/launchSystemNew/adq/log/index.tsx

@@ -0,0 +1,139 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getLogListApi } from "@/services/launchAdq/adq"
+import { Col, Input, Row } from "antd"
+import React, { useEffect, useState } from "react"
+import TableData from "../../components/TableData"
+import tableConfig from "./tableConfig"
+
+type Props = {
+    accountId: string,
+    adAccountId: string,
+    userId: string,
+    queryParmas: {
+        accountId?: string,//账户ID
+        adgroupId?: string,//广告ID
+    },
+    tableIdClick: (props: {
+        activeKey: string, parma: {
+            accountId?: string,//账户ID
+            campaignId?: string,//计划ID
+            adgroupId?: string,//广告ID
+            adcreativeId?: string,//创意ID
+            pageId?: string,//落地页ID
+            targetingId?: string,//定向ID
+        }
+    }) => void
+}
+/**
+ * 操作记录
+ */
+const Log: React.FC<Props> = (props) => {
+
+    /**************************/
+    let { tableIdClick, queryParmas, 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 })
+    /**************************/
+
+    useEffect(() => {
+        if (queryParmas?.accountId || queryParmas?.adgroupId || userId) {
+            setQueryparams({ ...queryparams, ...queryParmas, userId })
+        }
+    }, [queryParmas, userId])
+
+    useEffect(() => {
+        getLogList.run(queryparams)
+    }, [queryparams])
+
+    return <div>
+        <TableData
+            isCard={false}
+            columns={() => tableConfig(tableIdClick)}
+            ajax={getLogList}
+            dataSource={getLogList?.data?.data?.records}
+            loading={getLogList?.data?.loading}
+            scroll={{ y: 550 }}
+            total={getLogList?.data?.data?.total}
+            page={getLogList?.data?.data?.current}
+            pageSize={getLogList?.data?.data?.size}
+            leftChild={<>
+                <Row gutter={[10, 10]}>
+                    <Col>
+                        <Input
+                            placeholder='广告账号'
+                            allowClear
+                            style={{ width: 150 }}
+                            value={queryparams.accountId}
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                setQueryparams({...queryparams, pageNum: 1, pageSize: 20, accountId: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    setQueryparams({...queryparams, pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                setQueryparams({...queryparams, pageNum: 1, pageSize: 20, accountId: e.target.value })
+                            }}
+                        />
+                    </Col>
+                    <Col>
+                        <Input
+                            placeholder='广告组id'
+                            allowClear
+                            style={{ width: 150 }}
+                            value={queryparams.adgroupId}
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                setQueryparams({...queryparams, pageNum: 1, pageSize: 20, adgroupId: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    setQueryparams({...queryparams, pageNum: 1, pageSize: 20, adgroupId: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                setQueryparams({...queryparams, pageNum: 1, pageSize: 20, adgroupId: e.target.value })
+                            }}
+                        />
+                    </Col>
+                    <Col>
+                        <Input
+                            placeholder='定向名称'
+                            allowClear
+                            style={{ width: 150 }}
+                            value={queryparams.adgroupName}
+                            onBlur={(e) => {
+                                setQueryparams({...queryparams, pageNum: 1, pageSize: 20, adgroupName: e.target.value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    setQueryparams({...queryparams, pageNum: 1, pageSize: 20, adgroupName: e.target.value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                setQueryparams({...queryparams, pageNum: 1, pageSize: 20, adgroupName: e.target.value })
+                            }}
+                        />
+
+                    </Col>
+                </Row>
+            </>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                setQueryparams({ ...queryparams, pageNum: current, pageSize })
+            }}
+        />
+    </div>
+}
+
+
+export default React.memo(Log)

+ 142 - 0
src/pages/launchSystemNew/adq/log/tableConfig.tsx

@@ -0,0 +1,142 @@
+import { copy } from "@/utils/utils"
+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 {
+    return [
+        {
+            title: '所属账号',
+            dataIndex: 'accountId',
+            key: 'accountId',
+            align: 'center',
+            width: 70,
+            fixed: 'left',
+            ellipsis: true,
+            render: (a: string) => {
+                return <a onClick={() => {
+                    tableIdClick({ activeKey: '1', parma: { accountId: a } })
+                }}>{a}</a>
+            }
+        },
+        {
+            title: '广告ID',
+            dataIndex: 'adgroupId',
+            key: 'adgroupId',
+            align: 'center',
+            width: 85,
+            fixed: 'left',
+            ellipsis: true,
+            render: (a: string) => {
+                return <a onClick={() => {
+                    tableIdClick({ activeKey: '3', parma: { adgroupId: a } })
+                }}>{a}</a>
+            }
+        },
+        {
+            title: '推广计划ID',
+            dataIndex: 'campaignId',
+            key: 'campaignId',
+            align: 'center',
+            width: 85,
+            fixed: 'left',
+            ellipsis: true,
+            render: (a: string) => {
+                return <a onClick={() => {
+                    tableIdClick({ activeKey: '2', parma: { campaignId: a } })
+                }}>{a}</a>
+            }
+        },
+        {
+            title: '广告名称',
+            dataIndex: 'adgroupName',
+            key: 'adgroupName',
+            width: 280,
+            ellipsis: true,
+            render: (a: string) => {
+                return <a style={{ wordBreak: 'break-all' }} onClick={() => { copy(a) }}>{a}</a>
+            }
+        },
+        {
+            title: '执行时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            width: 140,
+            align: 'center'
+        },
+        {
+            title: '操作者名字',
+            dataIndex: 'operationByName',
+            key: 'operationByName',
+            width: 65,
+            align: 'center'
+        },
+        {
+            title: '操作类型名称',
+            dataIndex: 'operationName',
+            key: 'operationName',
+            width: 60,
+            align: 'center'
+        },
+        {
+            title: '操作数',
+            dataIndex: 'operationCount',
+            key: 'operationCount',
+            width: 60,
+            align: 'center'
+        },
+        {
+            title: '成功数',
+            dataIndex: 'successCount',
+            key: 'successCount',
+            width: 60,
+            align: 'center'
+        },
+        {
+            title: '失败数',
+            dataIndex: 'failCount',
+            key: 'failCount',
+            width: 60,
+            align: 'center',
+            render: (a: number) => {
+                return <span style={a > 0 ? { color: 'red' } : {}}>{a}</span>
+            }
+        },
+        {
+            title: '状态',
+            dataIndex: 'status',
+            key: 'status',
+            width: 70,
+            align: 'center',
+            render: (a: number, b: any) => {
+                return <span style={b?.failCount > 0 ? { color: 'red' } : { color: 'rgb(103, 194, 58)' }}>{b?.failCount > 0 ? '失败' : '成功'}</span>
+            }
+        },
+        {
+            title: '完成时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            width: 140,
+            align: 'center'
+        },
+        {
+            title: '错误消息',
+            dataIndex: 'errorMsg',
+            key: 'errorMsg',
+            width: 400,
+            ellipsis: true,
+        },
+    ]
+}
+
+
+export default tableConfig

+ 35 - 9
src/pages/launchSystemNew/adq/targeting/index.tsx

@@ -5,13 +5,13 @@ import { getAdqTargetingList, putAdqTargetingSyncAll } from '@/services/launchAd
 import TableData from '../../components/TableData'
 import tableConfig from './tableConfig'
 
-function Targeting(props: { adAccountId: any, userId: string, accountId: any }) {
-    let { accountId, adAccountId, userId } = props
+function Targeting(props: { adAccountId: any, userId: string, accountId: any, tableIdClick: any }) {
+    let { accountId, adAccountId, userId, tableIdClick } = props
     const listAjax = useAjax((params) => getAdqTargetingList(params), { formatResult: true })
     const syncAjax = useAjax((adAccountId) => putAdqTargetingSyncAll(adAccountId))
     console.log('定向=====》')
     useEffect(() => {
-        getList({ pageNum: 1, pageSize: 20 })
+        getList({ pageNum: 1, pageSize: 20, accountId })
     }, [userId, accountId])
     // 获取列表
     const getList = useCallback((params: {
@@ -23,12 +23,12 @@ function Targeting(props: { adAccountId: any, userId: string, accountId: any })
         console.log(accountId)
         if (!params.targetingName || params.targetingName !== listAjax?.params[0]?.targetingName) {
             !params.targetingName && delete params.targetingName
-            listAjax.run({ ...params, userId, accountId })
+            listAjax.run({ ...params, userId })
         }
-    }, [accountId,listAjax,userId])
+    }, [listAjax, userId])
     // 同步 
     const sync = useCallback(() => {
-        if(!adAccountId){
+        if (!adAccountId) {
             message.error('请先选择要同步的广点通账号!')
             return
         }
@@ -41,25 +41,51 @@ function Targeting(props: { adAccountId: any, userId: string, accountId: any })
     }, [adAccountId, listAjax])
     return <div>
         <TableData
-            columns={tableConfig}
+            isCard={false}
+            columns={() => tableConfig(tableIdClick)}
             ajax={listAjax}
             syncAjax={sync}
             dataSource={listAjax?.data?.data?.records}
             loading={listAjax?.loading || syncAjax?.loading}
-            scroll={{ x: 2000,y:550}}
+            scroll={{ x: 2000, y: 550 }}
             total={listAjax?.data?.data?.total}
             page={listAjax?.data?.data?.current}
             pageSize={listAjax?.data?.data?.size}
             myKey={'targetingId'}
             leftChild={<>
                 <Row gutter={[10, 10]}>
+                    <Col>
+                        <Input
+                            placeholder='广告账号'
+                            allowClear
+                            style={{ width: 150 }}
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, accountId: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, accountId: value })
+                                }
+                            }}
+                        />
+                    </Col>
                     <Col>
                         <Input
                             placeholder='定向名称'
                             allowClear
+                            style={{ width: 150 }}
                             onBlur={(e) => {
                                 let value = e.target.value
-                                getList({ pageNum: 1, pageSize: 20,  targetingName: value })
+                                getList({ pageNum: 1, pageSize: 20, targetingName: value })
                             }}
                             onKeyDownCapture={(e: any) => {
                                 let key = e.key

+ 11 - 10
src/pages/launchSystemNew/adq/targeting/tableConfig.tsx

@@ -1,16 +1,17 @@
-import {  TargetingSourceTypeEnum,  } from '@/services/launchAdq/enum'
+import { TargetingSourceTypeEnum, } from '@/services/launchAdq/enum'
 import React from 'react'
 import { Badge, Tooltip } from 'antd'
-function tableConfig(): any {
+import { copy } from '@/utils/utils'
+function tableConfig(tableIdClick: any): any {
     return [
         {
             title: '所属账号',
             dataIndex: 'accountId',
             key: 'accountId',
             align: 'center',
-            width:80,
-            render:(a:string)=>{
-                return <a>{a}</a>
+            width: 80,
+            render: (a: string) => {
+                return <a onClick={() => tableIdClick({ activeKey: '1', parma: { accountId: a } })}>{a}</a>
             }
         },
         {
@@ -19,8 +20,8 @@ function tableConfig(): any {
             key: 'targetingId',
             align: 'center',
             width: 100,
-            render:(a:string)=>{
-                return <a>{a}</a>
+            render: (a: string) => {
+                return <a onClick={() => copy(a)}>{a}</a>
             }
         },
         {
@@ -45,11 +46,11 @@ function tableConfig(): any {
             width: 600,
             render: (a: any) => {
                 return <Tooltip
-                    overlayInnerStyle={a?.length > 200 ? { width: 800 } :{}}
+                    overlayInnerStyle={a?.length > 200 ? { width: 800 } : {}}
                     title={
                         <div style={{ display: 'flex', flexFlow: 'column' }}>
                             {
-                                a?.split(';')?.filter((str: any) => !!str)?.map((str: string,index:number) => {
+                                a?.split(';')?.filter((str: any) => !!str)?.map((str: string, index: number) => {
                                     let arr = str?.split(':')
                                     return <span key={index}>{arr[0]}:<a>{arr[1]}</a></span>
                                 })
@@ -58,7 +59,7 @@ function tableConfig(): any {
                     }>
                     <div style={{ display: 'flex', flexFlow: 'column' }}>
                         {
-                            a?.split(';')?.filter((str: any) => !!str)?.map((str: string,index:number) => {
+                            a?.split(';')?.filter((str: any) => !!str)?.map((str: string, index: number) => {
                                 if (str.includes('地理位置')) {
                                     let arr = str?.split(':')
                                     return <span key={index}>{arr[0]}:<a>{arr[1]}....</a></span>

+ 7 - 3
src/pages/launchSystemNew/components/TableData/index.tsx

@@ -46,10 +46,11 @@ interface Prosp {
     },
     bodyStyle?: any
     gutter?: any
+    isCard?: boolean
 }
 
 function TableData(props: Prosp) {
-    const { isZj, scroll, columns, title, dataSource, expandedRowRender, className, 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, 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 { state: userState } = useModel('useOperating.useUser')
     const { isFell } = userState
     const [visible, setVisible] = useState<boolean>(false)
@@ -239,7 +240,7 @@ function TableData(props: Prosp) {
     const content = <Row gutter={[0, 20]} ref={ref} style={isFell ? { background: '#fff' } : {}}>
         {/**table */}
         <Col span={24}>
-            <Card
+            {isCard ? <Card
                 hoverable={hoverable}
                 title={title}
                 bodyStyle={props?.bodyStyle}
@@ -249,7 +250,10 @@ function TableData(props: Prosp) {
                     {header}
                     <Tab {...{ size, newColumns, handelResize, className, isZj, rowSelection, columns, loading, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
                 </Row>
-            </Card>
+            </Card> : <Row gutter={gutter}>
+                {header}
+                <Tab {...{ size, newColumns, handelResize, className, isZj, rowSelection, columns, loading, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
+            </Row>}
         </Col>
     </Row >
     return <>

+ 27 - 0
src/pages/launchSystemNew/components/timeInSelect/Header.tsx

@@ -0,0 +1,27 @@
+import React from 'react';
+import { Typography } from 'antd';
+import { HOUR_LIST } from './const';
+
+import './header.less';
+
+const Header: React.FC = () => {
+    return (
+        <div className="table_header_wrapper">
+            <div className="left">
+                <Typography.Text>星期 \ 时间</Typography.Text>
+            </div>
+            <div className="right">
+                <div className="top">
+                    <div className="before">00:00 - 12:00</div>
+                    <div className="after">12:00 - 24:00</div>
+                </div>
+                <div className="foot">
+                    {HOUR_LIST.map((item, index) => (
+                        <Typography key={index}>{item}</Typography>
+                    ))}
+                </div>
+            </div>
+        </div>
+    );
+}
+export default React.memo(Header)

+ 215 - 0
src/pages/launchSystemNew/components/timeInSelect/Thead.tsx

@@ -0,0 +1,215 @@
+import React, { useState, useRef } from 'react';
+import classnames from 'classnames';
+import { Space, Tag, Button, Tooltip, Typography } from 'antd';
+import {
+    WEEK_SERIRES,
+    initTimerPicker,
+    formatData,
+    getInitalFormatList,
+    Props,
+} from './const';
+
+import './index.less';
+
+const INIT_START_POINT = { rowNum: -1, colNum: -1, deselect: false };
+const INIT_END_POINT = { rowNum: -1, colNum: -1 };
+function getRealStartEndPoint(startPoint: { colNum: number, rowNum: number }, endPoint: { colNum: number, rowNum: number }) {
+    let startRowNum = startPoint.rowNum,
+        startColNum = startPoint.colNum,
+        endRowNum = endPoint.rowNum,
+        endColNum = endPoint.colNum;
+    if (startPoint.colNum > endPoint.colNum) {
+        startColNum = endPoint.colNum;
+        endColNum = startPoint.colNum;
+    }
+    if (startPoint.rowNum > endPoint.rowNum) {
+        startRowNum = endPoint.rowNum;
+        endRowNum = startPoint.rowNum;
+    }
+    return {
+        startRowNum,
+        startColNum,
+        endRowNum,
+        endColNum,
+    };
+}
+const getActiveStatus = (data: { each: any, startPoint: any, endPoint: any }) => {
+    const { each, startPoint, endPoint } = data;
+    if (startPoint.rowNum === -1 || endPoint.rowNum === -1) {
+        return each.selected;
+    }
+    const { startRowNum, startColNum, endRowNum, endColNum } = getRealStartEndPoint(startPoint, endPoint);
+    if (each.rowNum >= startRowNum && each.colNum >= startColNum && each.rowNum <= endRowNum && each.colNum <= endColNum) {
+        return !startPoint.deselect;
+    }
+    return each.selected;
+};
+
+const Thead: React.FC<Props> = ({ value = [], onChange }) => {
+    const [timerPickered, setTimerPickered] = useState(initTimerPicker(value));
+    const [startPoint, setStartPoint] = useState({ ...INIT_START_POINT });
+    const [endPoint, setEndPoint] = useState({ ...INIT_END_POINT });
+    const endPointRef = useRef(endPoint);
+
+    const updateEndPoint = (point: any) => {
+        endPointRef.current = { ...point };
+        setEndPoint(endPointRef.current);
+    };
+
+    const handleClear = () => {
+        const initData = getInitalFormatList();
+        setTimerPickered(initTimerPicker(initData));
+        setStartPoint({ ...INIT_START_POINT });
+        setEndPoint({ ...INIT_END_POINT });
+        onChange?.(initData);
+    };
+
+    const updateDataList = ({ startPoint, endPoint }: any) => {
+        const dataList = getInitalFormatList();
+        timerPickered.forEach((rows) => {
+            rows.forEach((each: any) => {
+                each.selected = getActiveStatus({
+                    each,
+                    startPoint,
+                    endPoint,
+                });
+                formatData(dataList, each);
+            });
+        });
+        setTimerPickered([...timerPickered]);
+        setStartPoint({ ...INIT_START_POINT });
+        updateEndPoint({ ...INIT_END_POINT });
+        return dataList;
+    };
+
+    const triggerChange = (changedValue: any) => {
+        const data = updateDataList({ startPoint, endPoint });
+        // onChange?.([...data, ...(value ?? []), ...changedValue]);
+        onChange?.(data);
+    };
+
+    const onMouseDown = ({ target: { dataset } }: any) => {
+        const { type, rowNum, colNum, selected } = dataset;
+        if ('time-boxzone' !== type) return;
+        setStartPoint({
+            rowNum: +rowNum,
+            colNum: +colNum,
+            deselect: selected === '1',
+        });
+        updateEndPoint({ rowNum: +rowNum, colNum: +colNum });
+    };
+
+    const onMouseUp = ({ target: { dataset } }: any) => {
+        const { type, rowNum, colNum } = dataset;
+        if (type !== 'time-boxzone') return;
+        if (+rowNum !== endPointRef.current.rowNum || +colNum !== endPointRef.current.colNum) {
+            updateEndPoint({ rowNum: +rowNum, colNum: +colNum });
+        }
+        requestAnimationFrame(() => {
+            const list = updateDataList({
+                startPoint,
+                endPoint: endPointRef.current,
+            });
+            triggerChange(list);
+        });
+    };
+
+    const onMouseOver = ({ target: { dataset } }: any) => {
+        const { type, rowNum, colNum } = dataset;
+        if ('time-boxzone' !== type) return;
+        if (startPoint.colNum === -1) return;
+        endPointRef.current = { rowNum: +rowNum, colNum: +colNum };
+        requestAnimationFrame(() => {
+            updateEndPoint(endPointRef.current);
+        });
+    };
+
+    const onMouseLeave = () => {
+        if (startPoint.rowNum === -1) return;
+        setTimeout(() => {
+            if (endPointRef.current.colNum === -1) {
+                updateEndPoint({
+                    colNum: startPoint.colNum,
+                    rowNum: startPoint.rowNum,
+                });
+            }
+            requestAnimationFrame(() => {
+                const list = updateDataList({
+                    startPoint,
+                    endPoint: endPointRef.current,
+                });
+                triggerChange(list);
+            });
+        });
+    };
+    const Footer = (
+        <div className="footer_wrapper">
+            <Space>
+                <Tag color="rgb(56,128,255)">已选</Tag>
+                <Tag>未选</Tag>
+                <Typography.Text type="secondary">可拖动鼠标选择时间段</Typography.Text>
+            </Space>
+            <Button type="link" onClick={handleClear} style={{ paddingLeft: 0, paddingRight: 0 }}>
+                清空
+            </Button>
+        </div>
+    );
+
+    return (
+        <div className="thead_wrapper">
+            <div className="thead_wrapper_content">
+                <div className="week_series">
+                    {WEEK_SERIRES.map((item, index) => (
+                        <div className="week_series__item" key={index}>
+                            {item?.name}
+                        </div>
+                    ))}
+                </div>
+                <div
+                    className="time_series"
+                    data-type="right-boxzones"
+                    onMouseDown={onMouseDown}
+                    onMouseUp={onMouseUp}
+                    onMouseOver={onMouseOver}
+                    onMouseLeave={onMouseLeave}
+                >
+                    {timerPickered.map((row, index) => (
+                        <div className="time_series__row" key={index}>
+                            {row.map((each: any, i: number) => (
+                                <Tooltip
+                                    destroyTooltipOnHide
+                                    mouseEnterDelay={1}
+                                    key={`${index}_${i}`}
+                                    title={each?.name}
+                                    placement="bottom"
+                                >
+                                    <div
+                                        key={each?.key}
+                                        draggable={false}
+                                        data-row-num={each?.rowNum}
+                                        data-col-num={each?.colNum}
+                                        data-selected={each?.selected ? '1' : '0'}
+                                        data-type="time-boxzone"
+                                        className={classnames(
+                                            {
+                                                isActive: getActiveStatus({
+                                                    each,
+                                                    startPoint,
+                                                    endPoint: endPointRef.current,
+                                                }),
+                                            },
+                                            'row_item',
+                                        )}
+                                    />
+                                </Tooltip>
+                            ))}
+                        </div>
+                    ))}
+                </div>
+            </div>
+            {Footer}
+        </div>
+    );
+}
+
+export default React.memo(Thead)

+ 67 - 0
src/pages/launchSystemNew/components/timeInSelect/const.ts

@@ -0,0 +1,67 @@
+import moment from 'moment';
+
+/**
+ * @param value string[]
+ * @param onChange (data: string[]) => void
+ */
+export interface Props {
+    value?: string[],
+    onChange?: (data: string[]) => void
+}
+
+const [_, ...rest] = moment.weekdays();
+export const WEEK_SERIRES = [...rest, _].map((item, index) => ({
+    name: item,
+    key: index,
+    value: index + 1,
+}));
+
+export const HOUR_LIST = Array.from({ length: 24 }, (v, i) => i);
+
+// export const TIME_PICKER = Array(24)
+//   .fill(0)
+//   .map((k, v) => `${v}:30`);
+
+const totalHour = 48;
+
+export const initTimerPicker = function (value: any[] = []) {
+    const result: any[] = [];
+    let index = 0;
+    WEEK_SERIRES.forEach((week, rowNum) => {
+        const weekList = [];
+        let hourSpan = 0;
+        while (hourSpan < totalHour) {
+            const hour = Math.floor(hourSpan / 2);
+            const curHour = ('00' + hour).slice(-2);
+            const nextHour = ('00' + (hour + 1)).slice(-2);
+            const timer = index % 2 === 0 ? `${curHour}:00-${curHour}:30` : `${curHour}:30-${nextHour}:00`;
+            const selected = value[index] === '1';
+            const timerBox = {
+                key: `${week.value}_${timer}`,
+                name: `${week.name} ${timer}`,
+                value: index,
+                rowNum: rowNum + 1,
+                colNum: (index % totalHour) + 1,
+                selected,
+            };
+            weekList.push(timerBox);
+            if (!selected) {
+                value[index] = '0';
+            }
+            index++;
+            hourSpan++;
+        }
+        result.push(weekList);
+    });
+    value.length = 7 * 24 * 2;
+    return result;
+};
+
+export function formatData(data: any, timeBox: any) {
+    data[timeBox.value] = timeBox.selected ? '1' : '0';
+    return data;
+}
+
+export function getInitalFormatList() {
+    return new Array(7 * 24 * 2).fill('0');
+}

+ 62 - 0
src/pages/launchSystemNew/components/timeInSelect/header.less

@@ -0,0 +1,62 @@
+.flexBase {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    background: #f9f9f9;
+}
+
+.table_header_wrapper {
+    width: 100%;
+    height: 60px;
+    display: flex;
+    border-top: 1px solid rgba(217, 218, 219, .8);
+
+    .left:extend(.flexBase) {
+        width: 73px;
+        height: 100%;
+        background: white;
+        box-shadow: 0px 0px 0px 1px rgba(217, 218, 219, .8);
+    }
+
+    .right {
+        width: calc(100% - 73px);
+        height: 100%;
+
+        .top {
+            display: flex;
+            width: 100%;
+            height: 40px;
+
+            .before:extend(.flexBase) {
+                width: 50%;
+                height: 100%;
+                border-right: 1px solid rgba(217, 218, 219, .8);
+                border-left: 1px solid rgba(217, 218, 219, .8);
+            }
+
+            .after :extend(.flexBase) {
+                width: 50%;
+                height: 100%;
+                border-right: 1px solid rgba(217, 218, 219, .8);
+            }
+        }
+
+        .foot {
+            width: 100%;
+            height: calc(100% - 40px);
+            display: grid;
+            grid-template-columns: repeat(24, 1fr);
+
+            .ant-typography {
+                &:extend(.flexBase);
+                &:first-child {
+                    border-left: 1px solid rgba(217, 218, 219, .8);
+                }
+                height: 19px;
+                border-bottom: 1px solid rgba(217, 218, 219, .8);
+                border-top: 1px solid rgba(217, 218, 219, .8);
+                border-right: 1px solid rgba(217, 218, 219, .8);
+            }
+        }
+    }
+}

+ 87 - 0
src/pages/launchSystemNew/components/timeInSelect/index.less

@@ -0,0 +1,87 @@
+.flexBase {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    background: #f9f9f9;
+    box-shadow: 0px 0px 0px 1px rgba(217, 218, 219, .8);
+}
+
+.thead_wrapper {
+    .thead_wrapper_content {
+        display: flex;
+
+        .week_series {
+            width: 73px;
+            height: 100%;
+
+            &__item:extend(.flexBase) {
+                width: 100%;
+                height: 40px;
+                background: white !important;
+            }
+        }
+
+        .time_series {
+            width: calc(100% - 73px);
+            border-right: 1px solid rgba(217, 218, 219, .8);
+            border-bottom: 1px solid rgba(217, 218, 219, .8);
+
+            &__row {
+                display: flex;
+                height: 39px;
+                width: 100%;
+                margin-top: 1px;
+
+                .row_item {
+                    width: 100%;
+                    height: 100%;
+                    margin-right: 1px;
+                    background: lightblue;
+                    box-sizing: border-box;
+                    // cursor: pointer;
+
+                    &__content {
+                        display: none;
+                    }
+
+                    &:first-child {
+                        margin-left: 1px;
+                    }
+
+                    &:nth-child(odd) {
+                        background: rgb(249, 249, 249);
+                    }
+
+                    &:nth-child(even) {
+                        background: rgb(240, 240, 240);
+                    }
+
+                    &:hover {
+                        background: rgba(@primary-color, 0.4);
+                        transition: all 0.5s;
+
+                        .row_item__content {
+                            display: block;
+                            transition: all 0.4s;
+                            color: white;
+                        }
+                    }
+
+                    &:last-child {
+                        margin-right: 0;
+                    }
+                }
+
+                .isActive {
+                    background: @primary-color !important;
+                    transition: all 0.5s;
+                }
+            }
+        }
+    }
+
+    .footer_wrapper {
+        display: flex;
+        justify-content: space-between;
+    }
+}

+ 16 - 0
src/pages/launchSystemNew/components/timeInSelect/index.tsx

@@ -0,0 +1,16 @@
+import React from "react"
+import { Props } from "./const";
+import Header from './Header';
+import Thead from './Thead';
+
+
+const TimeInSelect: React.FC<Props> = (props) => {
+
+    return <>
+        <Header />
+        <Thead {...props} />
+    </>
+}
+
+
+export default React.memo(TimeInSelect)

+ 80 - 0
src/pages/launchSystemNew/components/timeInSelect/thead.less

@@ -0,0 +1,80 @@
+.flexBase {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    background: #f9f9f9;
+    box-shadow: 0px 0px 0px 1px #efefef;
+}
+
+.thead_wrapper {
+    .thead_wrapper_content {
+        display: flex;
+
+        .week_series {
+            width: 73px;
+            height: 100%;
+
+            &__item:extend(.flexBase) {
+                width: 100%;
+                height: 40px;
+                background: white !important;
+            }
+        }
+
+        .time_series {
+            width: calc(100% - 73px);
+
+            &__row {
+                display: flex;
+                height: 39px;
+                width: 100%;
+                margin-top: 1px;
+
+                .row_item {
+                    width: 100%;
+                    height: 100%;
+                    margin-right: 1px;
+                    background: lightblue;
+                    cursor: pointer;
+
+                    &__content {
+                        display: none;
+                    }
+
+                    &:first-child {
+                        margin-left: 1px;
+                    }
+
+                    &:nth-child(odd) {
+                        background: rgb(249, 249, 249);
+                    }
+
+                    &:nth-child(even) {
+                        background: rgb(240, 240, 240);
+                    }
+
+                    &:hover {
+                        background: rgba(@primary-color, 0.4);
+                        transition: all 0.5s;
+
+                        .row_item__content {
+                            display: block;
+                            transition: all 0.4s;
+                            color: white;
+                        }
+                    }
+                }
+
+                .isActive {
+                    background: @primary-color !important;
+                    transition: all 0.5s;
+                }
+            }
+        }
+    }
+
+    .footer_wrapper {
+        display: flex;
+        justify-content: space-between;
+    }
+}

+ 0 - 2
src/pages/launchSystemNew/launchManage/createAd/ad/modal/adPositionList.tsx

@@ -1,5 +1,3 @@
-import { SiteSetEnum } from "@/services/launchAdq/enum"
-import { ColumnHeightOutlined } from "@ant-design/icons"
 import { useDrag } from "ahooks"
 import { Button, Modal } from "antd"
 import React, { useEffect, useState } from "react"

+ 2 - 3
src/pages/launchSystemNew/launchManage/createAd/ad/modal/leadAd.tsx

@@ -24,7 +24,7 @@ interface Props {
     ajax: any
 }
 
-const siteSetData = [
+export const siteSetData = [
     { siteSet: 'SITE_SET_MOMENTS', type: 1 },
     { siteSet: 'SITE_SET_MOBILE_UNION', type: 3 },
     { siteSet: 'SITE_SET_TENCENT_NEWS', type: 2 },
@@ -32,6 +32,7 @@ const siteSetData = [
     { siteSet: 'SITE_SET_KANDIAN', type: 2 },
     { siteSet: 'SITE_SET_QQ_MUSIC_GAME', type: 2 },
     { siteSet: 'SITE_SET_WECHAT', type: 4 },
+    { siteSet: 'SITE_SET_WECHAT_PLUGIN', type: 4 },
 ]
 
 export interface SiteSetPackageDataProps {
@@ -364,8 +365,6 @@ function LeadAdModal(props: Props) {
         })
     }
 
-    console.log('siteSetPackage--->', siteSetPackage, bidAdjustmentEnabled, siteSet);
-
     return <Modal
         visible={visible}
         title={type === 'add' ? '新建广告' : type === 'look' ? '广告详情' : '编辑广告'}

+ 38 - 2
src/pages/launchSystemNew/launchManage/createAd/ad/modal/wechat.tsx

@@ -9,7 +9,7 @@ import { ExclamationCircleOutlined } from '@ant-design/icons';
 import { CreateAdProps } from '@/services/launchAdq/createAd';
 import { createSysAdgroups } from '@/services/launchAdq/localAd';
 import AdPositionList from './adPositionList';
-import { SiteSetPackageDataProps } from './leadAd';
+import { siteSetData, SiteSetPackageDataProps } from './leadAd';
 import BidAdjustment from './bidAdjustment';
 import { useModel } from 'umi';
 const { RangePicker }: { RangePicker: any } = DatePicker;
@@ -301,7 +301,43 @@ function WeChatAdModal(props: Props) {
     const setSiteSetHandle = (siteSet: string[]) => {
         let data: SiteSetPackageDataProps[] = []
         if (siteSet && siteSet?.length > 0) {
-            data = siteSet?.map((item: string) => ({ siteSet: [item], bidCoefficient: 1, deepBidCoefficient: 1 }))
+            let data1: string[] = []
+            let data2: string[] = []
+            let data3: string[] = []
+            let data4: string[] = []
+            siteSet.forEach((item: string) => {
+                let siteData = siteSetData?.find(item1 => item1.siteSet === item)
+                if (siteData) {
+                    switch (siteData.type) {
+                        case 1:
+                            data1.push(item)
+                            break
+                        case 2:
+                            data2.push(item)
+                            break
+                        case 3:
+                            data3.push(item)
+                            break
+                        case 4:
+                            data4.push(item)
+                            break
+                    }
+                }
+            });
+            let newSiteSetPackageData: SiteSetPackageDataProps[] = []
+            if (data1?.length > 0) {
+                newSiteSetPackageData.push({ siteSet: data1, bidCoefficient: 1, deepBidCoefficient: 1 })
+            }
+            if (data2?.length > 0) {
+                newSiteSetPackageData.push({ siteSet: data2, bidCoefficient: 1, deepBidCoefficient: 1 })
+            }
+            if (data3?.length > 0) {
+                newSiteSetPackageData.push({ siteSet: data3, bidCoefficient: 1, deepBidCoefficient: 1 })
+            }
+            if (data4?.length > 0) {
+                newSiteSetPackageData.push({ siteSet: data4, bidCoefficient: 1, deepBidCoefficient: 1 })
+            }
+            data = newSiteSetPackageData
         } else {
             data = []
         }

+ 17 - 0
src/services/launchAdq/adAuthorize.ts

@@ -1,6 +1,23 @@
 import { request } from 'umi';
 import { api } from '../api'
 
+/**
+ * 分页获取账号列表
+ * @returns 
+ */
+export interface GetAdAccountParams {
+    pageNum: number,
+    pageSize: number,
+    groupId?: number,
+    accountIds?: number[]
+}
+export async function getAdAccountListApi(data: GetAdAccountParams) {
+    return request(api + '/adq/adAccount/accountList', {
+        method: 'POST',
+        data
+    });
+}
+
 /**
  * 获取账号列表
  * @returns 

+ 109 - 20
src/services/launchAdq/adq.ts

@@ -23,10 +23,10 @@ export async function getAdAccountAllOfMember() {
  * @param accountId 账户ID
  * @param adgroupName 广告名称
  */
- export async function getAdqAdAccountList(params: {
+export async function getAdqAdAccountList(params: {
   pageNum: number;
   pageSize: number;
-  userId?:string;//用户ID
+  userId?: string;//用户ID
   accountIds?: string[];//账号本地ID
 }) {
   return request(api + '/adq/adAccount/list', {
@@ -38,7 +38,7 @@ export async function getAdAccountAllOfMember() {
  * 按账号同步计划
  * @param adAccountId 本地ID
  */
- export async function putAdqAdAccountSyncByIds(data:any) {
+export async function putAdqAdAccountSyncByIds(data: any) {
   return request(api + `/adq/adAccount/syncByIds`, {
     method: 'PUT',
     data
@@ -51,15 +51,15 @@ export async function getAdAccountAllOfMember() {
  * @param accountId 账户ID
  * @param adgroupName 广告名称
  */
- export async function getAdqCampaignList(params: {
+export async function getAdqCampaignList(params: {
   pageNum: number;
   pageSize: number;
-  userId?:string;//用户ID
+  userId?: string;//用户ID
   accountId?: string;//账号本地ID
-  campaignName?:string;//计划名称
-  configuredStatus?:string;//计划状态
-  campaignType?:string;//计划类型
-  promotedObjectType?:string;//推广目标类型
+  campaignName?: string;//计划名称
+  configuredStatus?: string;//计划状态
+  campaignType?: string;//计划类型
+  promotedObjectType?: string;//推广目标类型
 }) {
   return request(api + '/adq/campaign/list', {
     method: 'POST',
@@ -70,7 +70,7 @@ export async function getAdAccountAllOfMember() {
  * 按账号同步计划
  * @param adAccountId 本地ID
  */
- export async function putAdqCampaignPage(adAccountId: any) {
+export async function putAdqCampaignPage(adAccountId: any) {
   return request(api + `/adq/campaign/syncAll/${adAccountId} `, {
     method: 'PUT',
   });
@@ -82,11 +82,11 @@ export async function getAdAccountAllOfMember() {
  * @param accountId 账户ID
  * @param adgroupName 广告名称
  */
- export async function getAdqAdgroupsList(params: {
+export async function getAdqAdgroupsList(params: {
   pageNum: number;
   pageSize: number;
   accountId: string;
-  adgroupName?:string
+  adgroupName?: string
 }) {
   return request(api + '/adq/adgroups/list', {
     method: 'POST',
@@ -97,12 +97,89 @@ export async function getAdAccountAllOfMember() {
  * 同步ADQ广告列表
  *@param adAccountId 本地ID
  */
- export async function putAdqAdgroupsSync(data: any) {
+export async function putAdqAdgroupsSync(data: any) {
   return request(api + `/adq/adgroups/sync`, {
     method: 'PUT',
     data
   });
 }
+
+/**
+ * 多选删除广告
+ * @param data 
+ * @returns 
+ */
+export async function delListAdqAdgroupsApi(data: { adgroupIds: number[] }) {
+  return request(api + `/adq/adgroups/delete/batch`, {
+    method: 'POST',
+    data
+  });
+}
+
+/**
+ * 删除广告
+ * @param data 
+ * @returns
+ */
+export async function delAdqAdgroupsApi({ adAccountId, adgroupId }: { adAccountId: number, adgroupId: number }) {
+  return request(api + `/adq/adgroups/${adAccountId}/${adgroupId}`, {
+    method: 'DELETE'
+  });
+}
+
+export interface EditAdqAdgroupsProps {
+  adgroupIds: number[],   // 广告组id列表
+  adgroupsUpdateBidAmountDTO?: { // 出价
+    bidAmount: number,  // 出价
+    bidMode: string,    // 出价方式
+    bidStrategy: string,// 出价策略
+    optimizationGoal: string,  // 出价目标
+  },
+  adgroupsUpdateDatetimeDTO?: { // 排期
+    beginDate: string,
+    endDate?: string,
+    firstDayBeginTime?: string,
+    timeSeries?: string
+  },
+  deepConversionSpec?: { // 深度优化
+    deepConversionType: string,
+    deepConversionBehaviorSpec?: {
+      bidAmount: number,
+      goal: string
+    },
+    deepConversionWorthAdvancedSpec?: {
+      expectedRoi: number,
+      goal: string
+    },
+    deepConversionWorthSpec?: {
+      expectedRoi: number,
+      goal: string
+    }
+  },
+  configuredStatus?: string
+}
+export async function editAdqAdgroupsDataApi(data: EditAdqAdgroupsProps) {
+  return request(api + `/adq/adgroups/update/batch`, {
+    method: 'POST',
+    data
+  });
+}
+
+export interface CopyAdProps{
+  adgroupIds: number[],   // 广告组id列表
+  beginDate: string,
+  endDate: string,
+  copyCount: number,  // 复制数量
+  firstDayBeginTime?: string
+  timeSeries?: string
+}
+export async function copyAdAdApi(data: CopyAdProps) {
+  return request(api + `/adq/adgroups/copy/batch`, {
+    method: 'POST',
+    data
+  });
+}
+
 /**
  * 获取ADQ创意列表
  * @param adgroupName 广告名称
@@ -114,7 +191,7 @@ export async function getAdqAdcreativeList(params: {
   pageNum: number;
   pageSize: number;
   accountId: string;
-  adcreativeName?:string
+  adcreativeName?: string
 
 }) {
   return request(api + '/adq/adcreative/list', {
@@ -148,7 +225,7 @@ export async function getAdqLandingPageList(params: {
  * 按账号同步落地页
  * @param adAccountId 本地ID
  */
- export async function putAdqLandingPage(adAccountId: any) {
+export async function putAdqLandingPage(adAccountId: any) {
   return request(api + `/adq/landingPageWechat/syncAll/${adAccountId} `, {
     method: 'PUT',
   });
@@ -178,7 +255,7 @@ export async function getAdqTargetingList(params: {
  * 按账号同步ADQ定向列表
  * @param adAccountId 本地ID
  */
- export async function putAdqTargetingSyncAll(adAccountId: any) {
+export async function putAdqTargetingSyncAll(adAccountId: any) {
   return request(api + `/adq/targeting/syncAll/${adAccountId} `, {
     method: 'PUT',
   });
@@ -188,8 +265,8 @@ export async function getAdqTargetingList(params: {
  * 计划启停
  * @param adAccountId 本地ID
  */
- export async function putAdqCampaignConfigStatus(params: any) {
-   let {accountId,campaignId,configuredStatus}=params
+export async function putAdqCampaignConfigStatus(params: any) {
+  let { accountId, campaignId, configuredStatus } = params
   return request(api + `/adq/campaign/configStatus/${accountId}/${campaignId}/${configuredStatus}`, {
     method: 'PUT',
   });
@@ -198,9 +275,21 @@ export async function getAdqTargetingList(params: {
  * 广告启停
  * @param adAccountId 本地ID
  */
- export async function putAdqAdgroupsConfigStatus(params: any) {
-   let {accountId,adgroupId,configuredStatus}=params
+export async function putAdqAdgroupsConfigStatus(params: any) {
+  let { accountId, adgroupId, configuredStatus } = params
   return request(api + `/adq/adgroups/configStatus/${accountId}/${adgroupId}/${configuredStatus}`, {
     method: 'PUT',
   });
 }
+
+/**
+ * log
+ * @param data 
+ * @returns 
+ */
+export async function getLogListApi(data: { accountId?: number, adgroupId?: number, adgroupName?: string, pageNum: number, pageSize: number }) {
+  return request(api + `/adq/adgroups/operate/log`, {
+    method: 'POST',
+    data
+  });
+}

+ 1 - 0
src/services/launchAdq/enum.ts

@@ -211,6 +211,7 @@ export enum SiteSetEnum {
   'SITE_SET_TENCENT_NEWS' = '腾讯新闻',
   'SITE_SET_TENCENT_VIDEO' = '腾讯视频',
   'SITE_SET_WECHAT' = '微信公众号与小程序',
+  'SITE_SET_WECHAT_PLUGIN' = '微信新闻插件'
 }
 /***/
 /**出价方式*/

+ 95 - 0
src/services/launchAdq/subgroup.ts

@@ -0,0 +1,95 @@
+import { request } from 'umi';
+import { api } from '../api'
+
+/**
+ * 添加分组API
+ * @returns 
+ */
+export async function addGroupApi(data: { groupName: string }) {
+    return request(api + '/adq/group/add', {
+        method: 'POST',
+        data
+    });
+}
+
+
+/**
+ * 删除分组
+ * @param data 
+ * @returns 
+ */
+export async function delGroupApi(data: { groupId: number }) {
+    return request(api + `/adq/group/delete/${data.groupId}`, {
+        method: 'POST'
+    });
+}
+
+/**
+ * 获取分组列表
+ * @returns 
+ */
+export async function getGroupListApi() {
+    return request(api + `/adq/group/list`)
+}
+
+/**
+ * 修改分组
+ * @param data 
+ * @returns 
+ */
+export async function editGroupApi(data: { groupName: string, groupId: number }) {
+    return request(api + '/adq/group/update', {
+        method: 'POST',
+        data
+    });
+}
+
+/**
+ * 根据分组ID获取账号列表
+ * @param groupId 
+ * @returns 
+ */
+export async function getAccountListApi(groupId: number) {
+    return request(api + `/adq/group/account/${groupId}`)
+}
+
+
+/**
+ * 添加账号进入分组
+ * @param AddAccountToGroupProps
+ * @returns 
+ */
+export interface AddAccountToGroupProps {
+    accountIds: number[],
+    currGroupId?: number,
+}
+export async function addAccountToGroupApi(data: AddAccountToGroupProps) {
+    return request(api + `/adq/group/account/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 移动账号到新分组
+ * @param data 
+ * @returns 
+ */
+export async function moveAccountToGroupApi(data: AddAccountToGroupProps) {
+    return request(api + `/adq/group/account/modify`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 账号移出分组
+ * @param data 
+ * @returns 
+ */
+export async function delAccountToGroupApi(data: AddAccountToGroupProps) {
+    return request(api + `/adq/group/account/move`, {
+        method: 'POST',
+        data
+    })
+}