Ver código fonte

Merge branch 'develop' of http://git.zanxiangnet.com/wjx/ad-manage

wjx 5 meses atrás
pai
commit
062618aaf6
25 arquivos alterados com 1174 adições e 278 exclusões
  1. 98 0
      src/components/SelectAdAccount/index.less
  2. 343 0
      src/components/SelectAdAccount/index.tsx
  3. 1 0
      src/pages/launchSystemNew/launchManage/createAd/index.less
  4. 2 2
      src/pages/launchSystemNew/launchManage/createAd/selector.tsx
  5. 15 12
      src/pages/launchSystemV3/adqv3/ad/index.tsx
  6. 4 4
      src/pages/launchSystemV3/adqv3/ad/tableConfig.tsx
  7. 1 1
      src/pages/launchSystemV3/adqv3/typings.d.ts
  8. 5 0
      src/pages/launchSystemV3/material/cloudNew/index.less
  9. 25 10
      src/pages/launchSystemV3/material/cloudNew/selectCloudNew.tsx
  10. 28 18
      src/pages/launchSystemV3/material/cloudNew/selectGroupCloudNew.tsx
  11. 4 0
      src/pages/launchSystemV3/material/typings.d.ts
  12. 54 4
      src/pages/launchSystemV3/tencenTasset/copyWriting/index.tsx
  13. 13 29
      src/pages/launchSystemV3/tencenTasset/wechatCanvasPage/index.tsx
  14. 3 2
      src/pages/launchSystemV3/tencentAdPutIn/create/Material/addMaterial.tsx
  15. 2 1
      src/pages/launchSystemV3/tencentAdPutIn/create/Material/index.tsx
  16. 2 1
      src/pages/launchSystemV3/tencentAdPutIn/create/SelectAccount/index.less
  17. 143 143
      src/pages/launchSystemV3/tencentAdPutIn/create/SelectAccount/index.tsx
  18. 41 18
      src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx
  19. 109 15
      src/pages/launchSystemV3/tencentAdPutIn/create/submitModal.tsx
  20. 74 0
      src/pages/launchSystemV3/tencentAdPutIn/taskList/executeLog.tsx
  21. 74 5
      src/pages/launchSystemV3/tencentAdPutIn/taskList/index.tsx
  22. 78 12
      src/pages/launchSystemV3/tencentAdPutIn/taskList/tableConfig.tsx
  23. 12 0
      src/services/adqV3/global.ts
  24. 32 1
      src/services/adqV3/index.ts
  25. 11 0
      src/services/launchAdq/adAuthorize.ts

+ 98 - 0
src/components/SelectAdAccount/index.less

@@ -0,0 +1,98 @@
+.selectAccount {
+    position: relative;
+}
+
+.selectAccount_row {
+    position: relative;
+    z-index: 1;
+    background-color: #FFF;
+    border-radius: 6px;
+}
+
+.selectAccount_select {
+    height: 32px;
+    padding: 1px 24px 1px 4px;
+    position: relative;
+
+    .clear {
+        position: absolute;
+        font-size: 12px;
+        right: 10px;
+        top: 50%;
+        transform: translateY(-50%);
+        color: #c3c3c3;
+        display: none;
+    }
+
+    &:hover .clear {
+        display: block;
+    }
+}
+
+.selectAccount_list {
+    position: absolute;
+    width: 900px;
+    height: 400px;
+    background-color: #FFF;
+    margin-top: 10px;
+    border-radius: 6px;
+    display: flex;
+    flex-direction: column;
+    padding: 10px 0;
+
+    .selectAccount_list_header {
+        padding: 2px 10px;
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        margin-bottom: 10px;
+    }
+
+    .selectAccount_list_table {
+        min-height: 308px;
+        height: 308px;
+        padding: 0 10px;
+    }
+
+
+    .selectAccount_list_footer {
+        padding: 4px 10px;
+        text-align: right;
+        background: transparent;
+        border-top: 1px solid #f0f0f0;
+        box-sizing: border-box;
+
+        .resetCss {
+            height: 28px;
+            line-height: normal;
+            border-radius: 6px !important;
+            padding: 3px 11px;
+        }
+    }
+}
+
+.selectAccount_select_content {
+    height: 100%;
+    min-width: 180px;
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+}
+
+.selectAccount_mask {
+    width: 100vw;
+    height: 100vh;
+    position: fixed;
+    background-color: rgba(0, 0, 0, .4);
+    z-index: 999;
+    top: 0;
+    left: 0;
+}
+
+.content_tag {
+    color: rgba(0, 0, 0, 0.85);
+
+    >span {
+        color: rgba(151, 151, 151, 0.85) !important;
+    }
+}

+ 343 - 0
src/components/SelectAdAccount/index.tsx

@@ -0,0 +1,343 @@
+import Selector from "@/pages/launchSystemNew/launchManage/createAd/selector"
+import React, { useEffect, useState } from "react"
+import style from './index.less'
+import { Button, Col, Form, Input, Row, Select, Space, Table, Tag, Tooltip, Typography } from "antd";
+import { CloseCircleFilled } from "@ant-design/icons";
+import { useAjax } from "@/Hook/useAjax";
+import { getUserAccountListApi } from "@/services/launchAdq/adAuthorize";
+import { arraysHaveSameValues } from "@/utils/utils";
+import '@/pages/launchSystemV3/tencentAdPutIn/index.less'
+const { Text } = Typography;
+
+interface Props {
+    value?: number[]
+    onChange?: (value?: number[] | number) => void
+    type?: 'checkbox' | 'radio'
+    /**  是否第一次获取的时候返回值 */
+    isReturnFirstValue?: boolean
+    allowClear?: boolean
+}
+
+
+
+const SelectAdAccount: React.FC<Props> = ({ value, onChange, isReturnFirstValue, type = 'checkbox', allowClear = true }) => {
+
+    value = value && typeof value !== 'object' ? [value] : value 
+    /***********************************/
+    const [form] = Form.useForm()
+    const [visible, setVisible] = useState<boolean>(false)
+    const [selectedRows, setSelectedRows] = useState<any[]>([])
+    const [queryForm, setQueryForm] = useState<{ accountIdList?: number[], adUnitTypeList?: string[], groupId?: number, remark?: string, putInType?: 'NOVEL' | 'GAME', sysGroupId?: number, pageNum: number, pageSize: number }>({ pageNum: 1, pageSize: 50 })
+    const [isFirstReturn, setIsFirstReturn] = useState<boolean>(false)
+
+    const getUserAccountList = useAjax((params) => getUserAccountListApi(params))
+    /***********************************/
+
+    const onFinish = (data: any) => {
+        let oldQueryFrom = JSON.parse(JSON.stringify(queryForm))
+        let params = { ...oldQueryFrom, ...data, pageNum: 1 }
+        if (params?.accountIdList) {
+            params.accountIdList = params?.accountIdList.split(/[,,\n\s]+/ig).filter((item: any) => item)
+        } else {
+            delete params?.accountIdList
+        }
+        setQueryForm(params)
+    }
+
+
+    useEffect(() => {
+        if (visible) {
+            setSelectedRows(value?.map(accountId => ({ accountId })) || [])
+        }
+    }, [value, visible])
+
+    useEffect(() => {
+        if (visible || isReturnFirstValue) {
+            let params = { ...queryForm }
+            if (queryForm?.putInType) {
+                params.adUnitTypeList = queryForm?.putInType === 'NOVEL' ? ['NOVEL', 'NOVEL_IAA', 'SKIT_IAA'] : ['GAME', 'GAME_IAA']
+                delete queryForm.putInType
+            } else {
+                delete params?.adUnitTypeList
+            }
+            getUserAccountList.run(params).then(res => {
+                if (isReturnFirstValue && !isFirstReturn) {
+                    setIsFirstReturn(() => true)
+                    if (res?.records?.length) {
+                        onChange?.(res.records[0].accountId)
+                    }
+                }
+            })
+        }
+    }, [queryForm, visible, isFirstReturn])
+
+    useEffect(() => {
+        return () => {
+            document.body.style.overflow = 'auto';
+        }
+    }, [])
+
+    const handleCancel = () => {
+        document.body.style.overflow = 'auto';
+        setSelectedRows([])
+        setVisible(false)
+    }
+
+
+    return <div className={style.selectAccount}>
+        <div className={style.selectAccount_row} style={{ zIndex: visible ? 1000 : 1 }}>
+            <Selector
+                label={visible ? '选择账户' : '媒体账户'}
+                style={visible ? { borderColor: '#1890ff' } : {}}
+                titleStyle={visible ? { borderColor: '#1890ff', color: '#1890ff' } : {}}
+            >
+                <div
+                    className={style.selectAccount_select}
+                    onClick={() => {
+                        document.body.style.overflow = 'hidden';
+                        setVisible(true)
+                    }}
+                >
+                    <div className={style.selectAccount_select_content}>
+                        {(visible && selectedRows.length > 0) ? <>
+                            <Tag
+                                closable={type === 'checkbox'}
+                                color="#F5F5F5"
+                                key={selectedRows[0].accountId}
+                                className={style.content_tag}
+                                onClose={() => {
+                                    setSelectedRows(selectedRows.slice(1))
+                                }}
+                            >{selectedRows[0].accountId}</Tag>
+                            {selectedRows?.length > 1 && <Tooltip
+                                color="#FFF"
+                                title={<span style={{ color: '#000' }}>
+                                    {selectedRows?.filter((_, index) => index !== 0)?.map((item) => <Tag
+                                        color="#F5F5F5"
+                                        className={style.content_tag}
+                                        key={item.accountId}
+                                        closable
+                                        onClose={() => {
+                                            setSelectedRows(selectedRows?.filter(item1 => item1.accountId !== item.accountId))
+                                        }}
+                                    >{item.accountId}</Tag>)}</span>
+                                }
+                            >
+                                <Tag color="#F5F5F5" className={style.content_tag}>+{selectedRows.length - 1}</Tag>
+                            </Tooltip>}
+                        </> : (!visible && value && value?.length > 0) ? <>
+                            <Tag
+                                closable={type === 'checkbox'}
+                                color="#F5F5F5"
+                                className={style.content_tag}
+                                key={value[0]}
+                                onClose={() => {
+                                    onChange?.(value.slice(1))
+                                }}
+                            >{value[0]}</Tag>
+                            {value?.length > 1 && <Tooltip
+                                color="#FFF"
+                                title={<span style={{ color: '#000' }}>
+                                    {value?.filter((_, index) => index !== 0)?.map((accountId) => <Tag
+                                        className={style.content_tag}
+                                        color="#F5F5F5"
+                                        key={accountId}
+                                        closable
+                                        onClose={() => {
+                                            onChange?.(value?.filter(accountId1 => accountId1 !== accountId))
+                                        }}
+                                    >{accountId}</Tag>)}</span>
+                                }
+                            >
+                                <Tag color="#F5F5F5" className={style.content_tag}>+{value.length - 1}</Tag>
+                            </Tooltip>}
+                        </> : <Text type="secondary">请选择媒体账户</Text>}
+                    </div>
+                    {visible}
+                    {((visible && selectedRows.length > 0) || (!visible && value && value?.length > 0)) && allowClear && <a className={style.clear} onClick={(e) => {
+                        e.stopPropagation()
+                        if (visible) {
+                            setSelectedRows([])
+                        } else {
+                            onChange?.(type === 'checkbox' ? [] : undefined)
+                        }
+                    }}><CloseCircleFilled /></a>}
+                </div>
+            </Selector>
+            {visible && <div className={style.selectAccount_list}>
+                <div className={style.selectAccount_list_header}>
+                    <Form layout="inline" className='queryForm' name="basicSelectAcc" form={form} onFinish={onFinish}>
+                        <Row gutter={[0, 6]}>
+                            <Col><Form.Item name={'putInType'}>
+                                <Select
+                                    style={{ width: 160 }}
+                                    placeholder="选择账户类型"
+                                    allowClear
+                                    filterOption={(input: any, option: any) => {
+                                        return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
+                                    }}
+                                >
+                                    <Select.Option value='NOVEL'>小说/短剧</Select.Option>
+                                    <Select.Option value='GAME'>游戏</Select.Option>
+                                </Select>
+                            </Form.Item></Col>
+                            <Col><Form.Item name='accountIdList'>
+                                <Input.TextArea
+                                    rows={1}
+                                    autoSize={{ minRows: 1, maxRows: 1 }}
+                                    placeholder="账户(多个,,空格换行)"
+                                    style={{ width: 180 }}
+                                    allowClear
+                                />
+                            </Form.Item></Col>
+                            <Col><Form.Item name='remark'>
+                                <Input
+                                    placeholder="备注"
+                                    style={{ width: 120 }}
+                                    allowClear
+                                />
+                            </Form.Item></Col>
+                            <Col>
+                                <Space>
+                                    <Button type="primary" htmlType="submit">搜索</Button>
+                                    <Button onClick={() => form.resetFields()}>重置</Button>
+                                </Space>
+                            </Col>
+                        </Row>
+                    </Form>
+
+                    {/* 
+                 */}
+                </div>
+                <div className={style.selectAccount_list_table}>
+                    <Table
+                        size={'small'}
+                        bordered
+                        dataSource={getUserAccountList?.data?.records}
+                        rowKey={'accountId'}
+                        scroll={{ y: 220 }}
+                        pagination={{
+                            pageSize: getUserAccountList?.data?.size || 50,
+                            current: getUserAccountList?.data?.current || 1,
+                            showTotal: total => `总共 ${total} 账户`,
+                            total: getUserAccountList?.data?.total,
+                            showSizeChanger: true,
+                            showLessItems: true,
+                            defaultCurrent: 1,
+                            defaultPageSize: 50,//默认初始的每页条数
+                            onChange: (page, pageSize) => {
+                                setQueryForm({ ...queryForm, pageNum: page, pageSize })
+                            }
+                        }}
+                        loading={getUserAccountList.loading}
+                        columns={[
+                            {
+                                title: '账号',
+                                dataIndex: 'accountId',
+                                key: 'accountId',
+                                width: 80,
+                                align: 'center',
+                                render(value) {
+                                    return <span style={{ fontSize: 12 }}>{value}</span>
+                                }
+                            },
+                            {
+                                title: '企业',
+                                dataIndex: 'corporationName',
+                                key: 'corporationName',
+                                width: 180,
+                                ellipsis: true,
+                                render(value) {
+                                    return <span style={{ fontSize: 12 }}>{value}</span>
+                                }
+                            },
+                            {
+                                title: '企业标识',
+                                dataIndex: 'corporationLicence',
+                                key: 'corporationLicence',
+                                width: 150,
+                                ellipsis: true,
+                                render(value) {
+                                    return <span style={{ fontSize: 12 }}>{value}</span>
+                                }
+                            },
+                            {
+                                title: '备注',
+                                dataIndex: 'remark',
+                                key: 'remark',
+                                ellipsis: true,
+                                render(value) {
+                                    return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                                }
+                            },
+                        ]}
+                        rowSelection={type === 'checkbox' ? {
+                            selectedRowKeys: selectedRows.map(item => item.accountId),
+                            type,
+                            onSelect: (record: { accountId: number }, selected: boolean) => {
+                                if (selected) {
+                                    selectedRows.push({ ...record })
+                                    setSelectedRows([...selectedRows])
+                                } else {
+                                    let newSelectAccData = selectedRows.filter((item: { accountId: number }) => item.accountId !== record.accountId)
+                                    setSelectedRows([...newSelectAccData])
+                                }
+                            },
+                            onSelectAll: (selected: boolean, selectedRowss: { accountId: number }[], changeRows: { accountId: number }[]) => {
+                                if (selected) {
+                                    let newSelectAccData = [...selectedRows]
+                                    changeRows.forEach((item: { accountId: number }) => {
+                                        let index = newSelectAccData.findIndex((ite: { accountId: number }) => ite.accountId === item.accountId)
+                                        if (index === -1) {
+                                            let data: any = { ...item }
+                                            newSelectAccData.push(data)
+                                        }
+                                    })
+                                    setSelectedRows([...newSelectAccData])
+                                } else {
+                                    let newSelectAccData = selectedRows.filter((item: { accountId: number }) => {
+                                        let index = changeRows.findIndex((ite: { accountId: number }) => ite.accountId === item.accountId)
+                                        if (index !== -1) {
+                                            return false
+                                        } else {
+                                            return true
+                                        }
+                                    })
+                                    setSelectedRows([...newSelectAccData])
+                                }
+                            }
+                        } : {
+                            selectedRowKeys: selectedRows.map(item => item.accountId),
+                            type,
+                            onChange(selectedRowKeys, selectedRows, info) {
+                                setSelectedRows(selectedRows)
+                            },
+                        }}
+                    />
+                </div>
+                <div className={style.selectAccount_list_footer}>
+                    <Button className={style.resetCss} onClick={handleCancel}>取消</Button>
+                    <Button
+                        className={style.resetCss}
+                        type="primary"
+                        style={{ marginLeft: 8 }}
+                        onClick={() => {
+                            if (value?.length && arraysHaveSameValues(value, selectedRows.map(item => item.accountId))) {
+                                handleCancel()
+                                return
+                            }
+                            document.body.style.overflow = 'auto';
+                            onChange?.(type === 'checkbox' ? selectedRows.map(item => item.accountId) : selectedRows?.[0]?.accountId)
+                            setSelectedRows([])
+                            setVisible(false)
+                        }}
+                    >确定</Button>
+                </div>
+            </div>}
+        </div>
+        {visible && <div className={style.selectAccount_mask}></div>}
+    </div>
+}
+
+
+export default React.memo(SelectAdAccount)

+ 1 - 0
src/pages/launchSystemNew/launchManage/createAd/index.less

@@ -19,6 +19,7 @@
   justify-content: flex-start;
   align-items: center;
   transition: all .3s;
+  font-size: 14px;
 
   &:hover {
     border: 1px solid #1890ff;

+ 2 - 2
src/pages/launchSystemNew/launchManage/createAd/selector.tsx

@@ -2,8 +2,8 @@ import React from "react"
 import './index.less'
 
 interface Props {
-    label: string,
     children: React.ReactNode;
+    label?: string,
     style?: React.CSSProperties
     titleStyle?: React.CSSProperties
 }
@@ -14,7 +14,7 @@ const Selector: React.FC<Props> = (props) => {
     /****************/
 
     return <div className="selector" style={style}>
-        <span className="selectorLabel" style={titleStyle}>{label}</span>{children}
+        {label && <span className="selectorLabel" style={titleStyle}>{label}</span>}{children}
     </div>
 }
 

+ 15 - 12
src/pages/launchSystemV3/adqv3/ad/index.tsx

@@ -22,9 +22,9 @@ const { Text } = Typography;
 const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
 
     /*****************************************/
-    const [useType, setUseType] = useLocalStorageState<1 | 2>('AD_USETYPE', 1);
+    const [useType, setUseType] = useLocalStorageState<string>('AD_UNIT_TYPE', 'NOVEL');
     const [pageSize, setPageSize] = useLocalStorageState<number>('AD_PAGESIZE', 20);
-    const [queryFrom, set_queryFrom] = useState<ADQV3.GetAdListProps>({ pageNum: 1, pageSize: pageSize || 20, useType: useType || 1 })
+    const [queryFrom, set_queryFrom] = useState<ADQV3.GetAdListProps>({ pageNum: 1, pageSize: pageSize || 20, adUnitType: 'NOVEL' })
     const [isClearSelect, setIsClearSelect] = useState(true)
     const [selectedRows, setSelectedRows] = useState<any[]>([])
     const [tactics, setTactics] = useState<any>()
@@ -41,7 +41,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
     /*****************************************/
 
     useEffect(() => {
-        getList({ pageNum: 1, pageSize: pageSize || 20, useType: useType || 1 })
+        getList({ pageNum: 1, pageSize: pageSize || 20, adUnitType: 'NOVEL' })
     }, [userId, pageSize])
 
     // 获取列表
@@ -92,7 +92,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
         <Row gutter={[6, 6]} align='middle' style={{ marginBottom: 15 }}>
             <Col>
                 <Select
-                    placeholder='应用类型'
+                    placeholder='业务单元类型'
                     style={{ width: 90 }}
                     showSearch
                     filterOption={(input: any, option: any) =>
@@ -100,14 +100,17 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                     }
                     value={useType}
                     onChange={(value: any) => {
-                        let params = { ...queryFrom, useType: value, pageNum: 1 }
+                        let params = { ...queryFrom, adUnitType: value, pageNum: 1 }
                         setUseType(value)
                         set_queryFrom(params)
                         getList(params)
                     }}
                 >
-                    <Select.Option value={1}>小说</Select.Option>
-                    <Select.Option value={2}>游戏</Select.Option>
+                    <Select.Option value={'NOVEL'}>小说</Select.Option>
+                    <Select.Option value={'NOVEL_IAA'}>小说IAA</Select.Option>
+                    <Select.Option value={'SKIT_IAA'}>短剧IAA</Select.Option>
+                    <Select.Option value={'GAME'}>游戏</Select.Option>
+                    <Select.Option value={'GAME_IAA'}>游戏IAA</Select.Option>
                 </Select>
             </Col>
             <Col>
@@ -240,7 +243,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
         </Row>
         <TableData
             isCard={false}
-            columns={() => tableConfig(() => getAdqV3AdList.refresh(), creativeHandle, useType)}
+            columns={() => tableConfig(() => getAdqV3AdList.refresh(), useType, creativeHandle)}
             ajax={getAdqV3AdList}
             syncAjax={sync}
             fixed={{ left: 2, right: 5 }}
@@ -334,7 +337,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                                 addDynamic()
                             }}
                             userId={userId}
-                            putInType={useType === 1 ? 'NOVEL' : 'GAME'}
+                            putInType={['GAME', 'GAME_IAA'].includes(useType) ? 'GAME' : 'NOVEL'}
                         /></Col>
                         <Col><Button type='primary' icon={<PlusOutlined />} disabled={selectedRows.length === 0} onClick={addDynamic}>添加创意</Button></Col>
                         <Col>
@@ -345,8 +348,8 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                                 {selectedRows?.length > 0 && <div style={{ maxWidth: '380px' }}>
                                     <Text type="danger" ellipsis={{ tooltip: true }} strong style={{ fontSize: 12 }}>
                                         {`当前广告选择:
-                                        营销目的:${useType === 2 ? MARKETING_SUB_GOAL_ENUM[selectedRows?.[0]?.marketingSubGoal as keyof typeof MARKETING_SUB_GOAL_ENUM] : MARKETING_GOAL_ENUM[selectedRows?.[0]?.marketingGoal as keyof typeof MARKETING_GOAL_ENUM]},
-                                        推广产品类型:${useType === 2 ? MARKETING_TARGET_TYPE_GAME_ENUM[selectedRows?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM] : MARKETING_TARGET_TYPE_ENUM[selectedRows?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]},
+                                        营销目的:${['GAME', 'GAME_IAA'].includes(useType) ? MARKETING_SUB_GOAL_ENUM[selectedRows?.[0]?.marketingSubGoal as keyof typeof MARKETING_SUB_GOAL_ENUM] : MARKETING_GOAL_ENUM[selectedRows?.[0]?.marketingGoal as keyof typeof MARKETING_GOAL_ENUM]},
+                                        推广产品类型:${['GAME', 'GAME_IAA'].includes(useType) ? MARKETING_TARGET_TYPE_GAME_ENUM[selectedRows?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM] : MARKETING_TARGET_TYPE_ENUM[selectedRows?.[0]?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]},
                                         营销载体类型:${MARKETING_CARRIER_TYPE_ENUM[selectedRows?.[0]?.marketingCarrierType as keyof typeof MARKETING_CARRIER_TYPE_ENUM]},
                                         版位选择:${selectedRows?.[0]?.automaticSiteEnabled ? '自动版位' : '选择特定版位'},
                                         ${!selectedRows?.[0]?.automaticSiteEnabled && `广告版位:${selectedRows?.[0]?.siteSet.map((item: string | number) => SITE_SET_ENUM[item as keyof typeof SITE_SET_ENUM]).toString()}`}
@@ -475,7 +478,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
         {/* 新增创意 */}
         {addDynamicVisible && <AddDynamic
             adData={selectedRows}
-            putInType={useType === 1 ? 'NOVEL' : 'GAME'}
+            putInType={['GAME', 'GAME_IAA'].includes(useType) ? 'GAME' : 'NOVEL'}
             visible={addDynamicVisible}
             onClose={() => {
                 setAddDynamicVisible(false)

+ 4 - 4
src/pages/launchSystemV3/adqv3/ad/tableConfig.tsx

@@ -8,7 +8,7 @@ import SwitchStatus from './switchStatus'
 import TimeSeriesLook from '@/pages/launchSystemNew/adq/ad/timeSeriesLook'
 import CreativePreview from '../../adMonitorListV3/CreativePreview'
 import { BID_MODE_ENUM, BID_SCENE_NORMAL_ENUM, MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, MARKETING_TARGET_TYPE_GAME_ENUM, OPTIMIZATIONGOAL_ENUM, SITE_SET_ENUM } from '../../tencentAdPutIn/const'
-function tableConfig(onChange: () => void, creativeHandle?: (id: number) => void, useType?: 1 | 2): any {
+function tableConfig(onChange: () => void, useType: string, creativeHandle?: (id: number) => void): any {
     return [
         {
             title: '启停',
@@ -151,14 +151,14 @@ function tableConfig(onChange: () => void, creativeHandle?: (id: number) => void
             }
         },
         {
-            title: useType === 2 ? '出价场景' : '出价策略',
+            title: ['GAME', 'GAME_IAA'].includes(useType) ? '出价场景' : '出价策略',
             dataIndex: 'bidStrategy',
             key: 'bidStrategy',
             align: 'center',
             width: 70,
             ellipsis: true,
             render: (a: string, b: { bidScene: string }) => {
-                if (useType === 2) {
+                if (['GAME', 'GAME_IAA'].includes(useType)) {
                     return BID_SCENE_NORMAL_ENUM[b?.bidScene as keyof typeof BID_SCENE_NORMAL_ENUM] || '--'
                 }
                 return BidStrategyEnum[a as keyof typeof BidStrategyEnum]
@@ -216,7 +216,7 @@ function tableConfig(onChange: () => void, creativeHandle?: (id: number) => void
             width: 80,
             ellipsis: true,
             render: (a: any) => {
-                if (useType === 2) {
+                if (['GAME', 'GAME_IAA'].includes(useType)) {
                     return MARKETING_TARGET_TYPE_GAME_ENUM[a as keyof typeof MARKETING_TARGET_TYPE_GAME_ENUM] || '--'
                 }
                 return MARKETING_TARGET_TYPE_ENUM[a as keyof typeof MARKETING_TARGET_TYPE_ENUM]

+ 1 - 1
src/pages/launchSystemV3/adqv3/typings.d.ts

@@ -11,7 +11,7 @@ declare namespace ADQV3 {
     interface GetAdListProps {
         pageNum: number;
         pageSize: number;
-        useType: 1 | 2,   // 1:小说(默认值)2:游戏
+        adUnitType: string
         accountMemo?: string,  // 腾讯备注
         accountRemark?: string, // 本地备注
         accountIdList?: any[], // 广告账号

+ 5 - 0
src/pages/launchSystemV3/material/cloudNew/index.less

@@ -380,6 +380,11 @@
         justify-content: center;
         overflow: hidden;
         position: relative;
+        border: 1px solid transparent;
+
+        &.active {
+            border-color: red;
+        }
 
         >img {
             max-width: 100%;

+ 25 - 10
src/pages/launchSystemV3/material/cloudNew/selectCloudNew.tsx

@@ -26,7 +26,7 @@ const { Text, Paragraph } = Typography;
  * @param param0 
  * @returns 
  */
-const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defaultParams, num, sliderImgContent, isGroup, onChange, onClose, accountCreateLogs }) => {
+const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defaultParams, num, sliderImgContent, isGroup, onChange, onClose, accountCreateLogs, putInType }) => {
 
     /************************************/
     const { initialState } = useModel('@@initialState');
@@ -43,6 +43,7 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
     const [uploadVisible, setUploadVisible] = useState<boolean>(false)
     const [showField, setShowField] = useLocalStorageState<string[]>('show-field', ['material.create_time', 'material_data_day.cost', 'material_data_day.ctr', 'material_data_day.conversions_rate', 'material_data_day.dynamic_creative_count']);
     const [sortData, setSortData] = useLocalStorageState<{ sortField: string | undefined, sortType: boolean }>('sort-data', { sortField: undefined, sortType: false });
+    const [active, setActive] = useState<number>()
 
     const getMaterialDataList = useAjax((params) => getMaterialDataListApi(params))
     /************************************/
@@ -100,10 +101,19 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
     // 选择
     const onCheckboxChange = (checked: boolean, item: any) => {
         let newCheckedFolderList: any[] = JSON.parse(JSON.stringify(checkedFolderList))
-        if (checked) { // 选中
-            newCheckedFolderList.push({ ...item, materialType: 0 })
-        } else { // 取消
-            newCheckedFolderList = newCheckedFolderList.filter(i => i.id !== item.id)
+        if (active || active === 0) {
+            if (checked) { // 选中
+                newCheckedFolderList[active] = { ...item, materialType: 0 }
+            } else { // 取消
+                message.warning('请选择其他图片替换')
+                return
+            }
+        } else {
+            if (checked) { // 选中
+                newCheckedFolderList.push({ ...item, materialType: 0 })
+            } else { // 取消
+                newCheckedFolderList = newCheckedFolderList.filter(i => i.id !== item.id)
+            }
         }
         setCheckedFolderList(newCheckedFolderList)
     };
@@ -137,7 +147,7 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
             {checkedFolderList.length > 0 ? <>
                 <div className={style.selectedCloud}>
                     <div style={{ width: checkedFolderList.length * 58 + (checkedFolderList.length - 1) * 8 }}>
-                        {checkedFolderList.map(item => <div key={item.id} className={style.selectedCloud_col}>
+                        {checkedFolderList.map((item, index) => <div key={item.id + '_' + index} className={`${style.selectedCloud_col} ${active === index ? style.active : ''}`} onClick={() => isGroup && setActive(index)}>
                             <div className={style.cz}>
                                 {item.material_type === 'video' && <PlayVideo videoUrl={item.oss_url}>{(onPlay) => <a onClick={onPlay}><PlayCircleOutlined /></a>}</PlayVideo>}
                                 <a style={{ color: 'red' }} onClick={() => setCheckedFolderList(data => data.filter(i => i.id !== item.id))}><DeleteOutlined /></a>
@@ -146,6 +156,7 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
                         </div>)}
                     </div>
                 </div>
+                {!!active && <a style={{ color: 'red' }} onClick={() => setActive(undefined)}>清除单独修改</a>}
                 <a style={{ color: 'red' }} onClick={() => setCheckedFolderList([])}>清除所有</a>
             </> : <div></div>}
             <Space>
@@ -196,7 +207,7 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
                                         }
                                         setCheckedFolderList(newCheckedFolderList)
                                     }}
-                                    disabled={checkedFolderList?.length >= num}
+                                    disabled={checkedFolderList?.length >= num || !!active || active === 0}
                                     indeterminate={indeterminateFolder}
                                     checked={checkFolderAll}
                                 >全选</Checkbox>
@@ -272,11 +283,11 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
                             <Spin spinning={getMaterialDataList.loading}>
                                 <div className={style.content_scroll} ref={ref} {...dropAreaProps}>
                                     {isDragOver && <div className={`${style.placeholder} ${isDragOver ? style.dragOver : ''}`}><span>拖动文件到这里</span></div>}
-                                    {getMaterialDataList?.data?.records?.length > 0 ? <Checkbox.Group value={checkedFolderList?.map(item => item.id)} style={{ width: '100%' }}>
+                                    {getMaterialDataList?.data?.records?.length > 0 ? <Checkbox.Group value={checkedFolderList?.filter((_, index) => (active || active === 0) ? index === active : true)?.map(item => item.id)} style={{ width: '100%' }}>
                                         <div className={style.content_scroll_div}>
                                             {getMaterialDataList?.data?.records.map((item: any) => {
-                                                const isSelect = checkedFolderList?.map(item => item.id).includes(item.id)
-                                                const disabled = checkedFolderList.length >= num && !isSelect
+                                                const isSelect = checkedFolderList?.filter((_, index) => (active || active === 0) ? index === active : true)?.map(item => item.id).includes(item.id)
+                                                const disabled = (active || active === 0) ? false : (checkedFolderList.length >= num && !isSelect)
                                                 return <div key={item.id} className={style.content_row} style={{ width: rowNum ? (1 / rowNum * 100) + '%' : 200 }}>
                                                     <Card
                                                         hoverable
@@ -348,6 +359,7 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
                                 onChange={(page: number, pageSize: number) => {
                                     setQueryParams({ ...queryParams, pageNum: page, pageSize })
                                 }}
+                                pageSizeOptions={[10, 15, 20, 50, 100]}
                             />
                         </div>
                     </div>
@@ -359,6 +371,9 @@ const SelectCloudNew: React.FC<CLOUDNEW.SelectCloudNewProps> = ({ visible, defau
             checkedFolderList={checkedFolderList}
             setCheckedFolderList={setCheckedFolderList}
             accountCreateLogs={accountCreateLogs}
+            putInType={putInType}
+            active={active}
+            setActive={setActive}
         />}
 
 

+ 28 - 18
src/pages/launchSystemV3/material/cloudNew/selectGroupCloudNew.tsx

@@ -1,9 +1,8 @@
-import React, { useEffect, useRef, useState } from "react"
+import React, { useEffect, useState } from "react"
 import style from './index.less'
 import { Button, Card, Checkbox, Divider, Empty, Form, message, Pagination, Popover, Radio, Result, Select, Space, Spin, Typography, Image } from "antd"
 import { useAjax } from "@/Hook/useAjax"
 import { getPageRemoteImageDataListApi } from "@/services/adqV3/cloudNew"
-import { useModel } from "umi"
 import { formatBytes, formatSecondsToTime, groupBy } from "@/utils/utils"
 import SelectGroupSearch from "./selectGroupSearch"
 import './global.less'
@@ -14,6 +13,7 @@ import PlayVideo from "./playVideo"
 import Lazyimg from "react-lazyimg-component"
 import { useLocalStorageState } from "ahooks"
 import SyncCloudSc from "./syncCloudSc"
+import { getUserAccountListApi } from "@/services/launchAdq/adAuthorize"
 
 const { Text, Paragraph } = Typography;
 
@@ -22,10 +22,9 @@ const { Text, Paragraph } = Typography;
  * @param param0 
  * @returns 
  */
-const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num, defaultParams, checkedFolderList, setCheckedFolderList, accountCreateLogs }) => {
+const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num, defaultParams, checkedFolderList, setCheckedFolderList, accountCreateLogs, putInType, active, setActive }) => {
 
     /*****************************************/
-    const { getAllUserAccount } = useModel('useLaunchAdq.useAdAuthorize')
     const [rowNum, setRowNum] = useState<number>(0)
 
     const [checkFolderAll, setCheckFolderAll] = useState<boolean>(false);
@@ -39,6 +38,7 @@ const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num,
     const [showField, setShowField] = useLocalStorageState<string[]>('show-field-group', ['material.create_time', 'material_data_day.cost', 'material_data_day.ctr', 'material_data_day.conversions_rate', 'material_data_day.dynamic_creative_count']);
 
     const getPageRemoteImageDataList = useAjax((params) => getPageRemoteImageDataListApi(params))
+    const getUserAccountList = useAjax((params) => getUserAccountListApi(params))
     /*****************************************/
 
     // 根据内容宽度计算列数
@@ -69,10 +69,10 @@ const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num,
 
     useEffect(() => {
         if (accountCreateLogs?.length) {
-            getAllUserAccount.run().then(res => {
-                if (res?.data?.length) {
+            getUserAccountList.run({ adUnitTypeList: putInType === 'NOVEL' ? ['NOVEL', 'NOVEL_IAA', 'SKIT_IAA'] : ['GAME', 'GAME_IAA'], pageNum: 1, pageSize: 20000, accountIdList: accountCreateLogs.map(item => item.accountId) }).then(res => {
+                if (res?.records?.length) {
                     const list = accountCreateLogs.map(item => item.accountId)
-                    const selectAccountList = res.data.filter((item: { accountId: any }) => list.includes(item.accountId))
+                    const selectAccountList = res.records.filter((item: { accountId: any }) => list.includes(item.accountId))
                     const groupAccount = groupBy(selectAccountList, (item) => item.groupId, true)
                     const GrounpArray = Object.keys(groupAccount).map(function (group) {
                         return groupAccount[group];
@@ -115,16 +115,25 @@ const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num,
     // 选择
     const onCheckboxChange = (checked: boolean, item: any) => {
         let newCheckedFolderList: any[] = JSON.parse(JSON.stringify(checkedFolderList))
-        if (checked) { // 选中
-            newCheckedFolderList.push({ ...item, id: item.image_id || item.video_id, material_type: defaultParams.materialType, oss_url: item.preview_url, materialType: 1 })
-        } else { // 取消
-            let id = item.image_id || item.video_id
-            newCheckedFolderList = newCheckedFolderList.filter(i => i.id !== id)
+        if (active || active === 0) {
+            if (checked) { // 选中
+                newCheckedFolderList[active] = { ...item, id: item.image_id || item.video_id, material_type: defaultParams.materialType, oss_url: item.preview_url, materialType: 1 }
+            } else { // 取消
+                message.warning('请选择其他图片替换')
+                return
+            }
+        } else {
+            if (checked) { // 选中
+                newCheckedFolderList.push({ ...item, id: item.image_id || item.video_id, material_type: defaultParams.materialType, oss_url: item.preview_url, materialType: 1 })
+            } else { // 取消
+                let id = item.image_id || item.video_id
+                newCheckedFolderList = newCheckedFolderList.filter(i => i.id !== id)
+            }
         }
         setCheckedFolderList(newCheckedFolderList)
     };
 
-    return <Spin spinning={getAllUserAccount.loading} wrapperClassName={'select_group_spin'}>
+    return <Spin spinning={getUserAccountList.loading} wrapperClassName={'select_group_spin'}>
         <div className={style.select_cloudNew_layout}>
             {ownerAccountId && ownerAccountId !== -1 ? <>
                 {/* 搜索 */}
@@ -167,7 +176,7 @@ const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num,
                                             }
                                             setCheckedFolderList(newCheckedFolderList)
                                         }}
-                                        disabled={checkedFolderList?.length >= num}
+                                        disabled={checkedFolderList?.length >= num || !!active || active === 0}
                                         indeterminate={indeterminateFolder}
                                         checked={checkFolderAll}
                                     >全选</Checkbox>
@@ -175,7 +184,7 @@ const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num,
                                     {checkedFolderList.length > 0 && <a style={{ color: 'red' }} onClick={() => setCheckedFolderList([])}>清除所有</a>}
                                     {sortData?.sortField && <Text>「排序-{showField1List.find(item => item.value === sortData.sortField)?.label}-{sortData.sortType ? '正序' : '倒序'}」</Text>}
                                     {/* 同步素材 */}
-                                    <SyncCloudSc 
+                                    <SyncCloudSc
                                         accountId={ownerAccountId}
                                     />
                                 </div>
@@ -246,12 +255,12 @@ const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num,
                             <div className={`${style.content} content_global`}>
                                 <Spin spinning={getPageRemoteImageDataList.loading}>
                                     <div className={`${style.content_scroll} scroll`}>
-                                        {getPageRemoteImageDataList?.data?.records?.length > 0 ? <Checkbox.Group value={checkedFolderList?.map(item => item.id)} style={{ width: '100%' }}>
+                                        {getPageRemoteImageDataList?.data?.records?.length > 0 ? <Checkbox.Group value={checkedFolderList?.filter((_, index) => (active || active === 0) ? index === active : true)?.map(item => item.id)} style={{ width: '100%' }}>
                                             <div className={style.content_scroll_div}>
                                                 {getPageRemoteImageDataList?.data?.records.map((item: any) => {
                                                     let id = defaultParams.materialType === 'image' ? item.image_id : item.video_id
-                                                    const isSelect = checkedFolderList?.map(item => item.id).includes(id)
-                                                    const disabled = checkedFolderList.length >= num && !isSelect
+                                                    const isSelect = checkedFolderList?.filter((_, index) => (active || active === 0) ? index === active : true)?.map(item => item.id).includes(id)
+                                                    const disabled = (active || active === 0) ? false : (checkedFolderList.length >= num && !isSelect)
                                                     return <div key={id} className={style.content_row} style={{ width: rowNum ? (1 / rowNum * 100) + '%' : 220 }}>
                                                         <Card
                                                             hoverable
@@ -333,6 +342,7 @@ const SelectGroupCloudNew: React.FC<CLOUDNEW.SelectGroupCloudNewProps> = ({ num,
                                     onChange={(page: number, pageSize: number) => {
                                         setQueryParams({ ...queryParams, pageNum: page, pageSize })
                                     }}
+                                    pageSizeOptions={[10, 15, 20, 50, 100]}
                                 />
                             </div>
                         </div>

+ 4 - 0
src/pages/launchSystemV3/material/typings.d.ts

@@ -153,13 +153,17 @@ declare namespace CLOUDNEW {
         onClose?: () => void
         onChange?: (data: any[]) => void
         accountCreateLogs?: PULLIN.AccountCreateLogsProps[]
+        putInType?: 'NOVEL' | 'GAME'
     }
     interface SelectGroupCloudNewProps {
         num: number
         defaultParams: DefaultParams
         checkedFolderList: any[]
         setCheckedFolderList: React.Dispatch<React.SetStateAction<any[]>>
+        active: number | undefined
+        setActive: React.Dispatch<React.SetStateAction<number | undefined>>
         accountCreateLogs?: PULLIN.AccountCreateLogsProps[]
+        putInType?: 'NOVEL' | 'GAME'
     }
     interface GetRemoteMaterialListProps {
         pageNum: number,

+ 54 - 4
src/pages/launchSystemV3/tencenTasset/copyWriting/index.tsx

@@ -1,9 +1,9 @@
 import React, { useEffect, useState } from "react"
 import '../../tencentAdPutIn/index.less'
-import { Button, Card, Input, message, Select, Table } from "antd"
-import { PlusOutlined, SearchOutlined } from "@ant-design/icons"
+import { Button, Card, Input, message, Popconfirm, Select, Table } from "antd"
+import { DeleteOutlined, PlusOutlined, SearchOutlined } from "@ant-design/icons"
 import { useAjax } from "@/Hook/useAjax"
-import { delCopyWritingApi, getCopyWritingListApi } from "@/services/adqV3/global"
+import { delCopyWritingApi, delsCopyWritingApi, getCopyWritingListApi } from "@/services/adqV3/global"
 import ModifyCopyWriting from "./modifyCopyWriting"
 import columns from "./tableConfig"
 import { getErpUserAll } from "@/services/launchAdq/adq"
@@ -19,10 +19,11 @@ const CopyWriting: React.FC = () => {
     const [queryFormNew, setQueryFormNew] = useState<{ category?: string, content?: string, createBy?: number, pageNum: number, pageSize: number }>({ pageNum: 1, pageSize: 20 })
     const [initialValues, setInitialValues] = useState<any>()
     const [visible, setVisible] = useState<boolean>(false)
-
+    const [selectedRows, setSelectedRows] = useState<any[]>([])
 
     const getCopyWritingList = useAjax((params) => getCopyWritingListApi(params))
     const delCopyWriting = useAjax((params) => delCopyWritingApi(params))
+    const delsCopyWriting = useAjax((params) => delsCopyWritingApi(params))
     const allOfMember = useAjax(() => getErpUserAll())
     /*************************************/
 
@@ -70,6 +71,20 @@ const CopyWriting: React.FC = () => {
 
             <Button type="primary" icon={<SearchOutlined />} onClick={() => setQueryFormNew({ ...queryForm })}>搜索</Button>
             <Button type="primary" icon={<PlusOutlined />} onClick={() => { setVisible(true) }}>新增文案</Button>
+            <Popconfirm
+                title="确定删除?"
+                onConfirm={() => {
+                    delsCopyWriting.run(selectedRows.map(item => item.id)).then(res => {
+                        if (res) {
+                            message.success('删除成功')
+                            getCopyWritingList.refresh()
+                        }
+                    })
+                }}
+                disabled={selectedRows.length === 0}
+            >
+                <Button type="primary" loading={delsCopyWriting.loading} icon={<DeleteOutlined />} danger disabled={selectedRows.length === 0}>删除</Button>
+            </Popconfirm>
         </div>}
     >
 
@@ -92,6 +107,41 @@ const CopyWriting: React.FC = () => {
                 setQueryForm({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
                 setQueryFormNew({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
             }}
+            rowSelection={{
+                selectedRowKeys: selectedRows.map(item => item.id.toString()),
+                onSelect: (record: { id: number, mpName: string }, selected: boolean) => {
+                    if (selected) {
+                        selectedRows.push({ ...record })
+                        setSelectedRows([...selectedRows])
+                    } else {
+                        let newSelectAccData = selectedRows.filter((item: { id: number }) => item.id !== record.id)
+                        setSelectedRows([...newSelectAccData])
+                    }
+                },
+                onSelectAll: (selected: boolean, selectedRowss: { id: number }[], changeRows: { id: number }[]) => {
+                    if (selected) {
+                        let newSelectAccData = [...selectedRows]
+                        changeRows.forEach((item: { id: number }) => {
+                            let index = newSelectAccData.findIndex((ite: { id: number }) => ite.id === item.id)
+                            if (index === -1) {
+                                let data: any = { ...item }
+                                newSelectAccData.push(data)
+                            }
+                        })
+                        setSelectedRows([...newSelectAccData])
+                    } else {
+                        let newSelectAccData = selectedRows.filter((item: { id: number }) => {
+                            let index = changeRows.findIndex((ite: { id: number }) => ite.id === item.id)
+                            if (index !== -1) {
+                                return false
+                            } else {
+                                return true
+                            }
+                        })
+                        setSelectedRows([...newSelectAccData])
+                    }
+                }
+            }}
         />
 
 

+ 13 - 29
src/pages/launchSystemV3/tencenTasset/wechatCanvasPage/index.tsx

@@ -3,10 +3,10 @@ import React, { useEffect, useState } from "react"
 import '../../tencentAdPutIn/index.less'
 import { delPageApi, getAdqLandingPageListApi, getWXDownPageAuthInfoListApi } from "@/services/adqV3/global";
 import { useAjax } from "@/Hook/useAjax";
-import { useModel } from "umi";
 import { SearchOutlined } from "@ant-design/icons";
 import columns from "./tableConfig";
 import CopyPage from "./copyPage";
+import SelectAdAccount from "@/components/SelectAdAccount";
 
 interface AjaxProps {
     pageNum: number;
@@ -24,7 +24,6 @@ interface AjaxProps {
 const WechatCanvasPage: React.FC = () => {
 
     /********************************/
-    const { getAllUserAccount } = useModel('useLaunchAdq.useAdAuthorize')
     const [queryParamsNew, setQueryParamsNew] = useState<AjaxProps>({ pageNum: 1, pageSize: 20 })
     const [visible, setVisible] = useState<boolean>(false)
     const [pageData, setPageData] = useState<any>()
@@ -35,13 +34,6 @@ const WechatCanvasPage: React.FC = () => {
     const delPage = useAjax((params) => delPageApi(params))
     /********************************/
 
-    useEffect(() => {
-        // 获取账户列表
-        getAllUserAccount.run().then(res => {
-            setQueryParamsNew({ ...queryParamsNew, accountId: res?.data?.[0]?.accountId })
-        })
-    }, [])
-
     // 落地页列表
     useEffect(() => {
         if (queryParamsNew?.accountId) {
@@ -74,24 +66,17 @@ const WechatCanvasPage: React.FC = () => {
 
     return <Card
         className="cardResetCss"
-        title={<div className="flexStart" style={{ gap: 8 }}>
-            <Select
-                placeholder="请先选择媒体账户"
-                maxTagCount={1}
-                style={{ width: 250 }}
-                autoClearSearchValue={false}
-                filterOption={(input: any, option: any) => {
-                    let newInput: string[] = input ? input?.split(/[,,\n\s]+/ig).filter((item: any) => item) : []
-                    return newInput?.some(val => option!.children?.toString().toLowerCase()?.includes(val))
-                }}
-                value={queryParamsNew.accountId}
-                onChange={(e) => {
-                    setQueryParamsNew({ ...queryParamsNew, accountId: e, ownerUid: undefined })
+    >
+        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
+            <SelectAdAccount
+                isReturnFirstValue
+                value={queryParamsNew?.accountId ? [queryParamsNew.accountId] : undefined}
+                type="radio"
+                onChange={(value) => {
+                    setQueryParamsNew({ ...queryParamsNew, accountId: value as number, ownerUid: undefined })
                 }}
-                showSearch
-            >
-                {getAllUserAccount?.data?.data?.map((item: any) => <Select.Option value={item.accountId} key={item.id}>{item.remark ? item.accountId + '_' + item.remark : item.accountId}</Select.Option>)}
-            </Select>
+                allowClear={false}
+            />
             {queryParamsNew?.accountId && <>
                 <Select
                     placeholder='选择原生页授权方信息'
@@ -122,13 +107,12 @@ const WechatCanvasPage: React.FC = () => {
             >
                 <Button danger loading={delPage.loading} disabled={selectedRows.length === 0 || !!queryParamsNew?.ownerUid}>删除</Button>
             </Popconfirm>
-        </div>}
-    >
+        </div>
         <Table
             columns={columns(handleCopy, () => getAdqLandingPageList.refresh(), queryParamsNew.accountId)}
             dataSource={getAdqLandingPageList.data?.records}
             size="small"
-            loading={getAdqLandingPageList?.loading || getAllUserAccount.loading}
+            loading={getAdqLandingPageList?.loading}
             scroll={{ y: 600 }}
             bordered
             rowKey={'pageId'}

+ 3 - 2
src/pages/launchSystemV3/tencentAdPutIn/create/Material/addMaterial.tsx

@@ -13,15 +13,15 @@ interface Props {
     creativeTemplateId: number
     materialData: any
     deliveryMode: string,
-    isSelectRemote?: boolean
     value?: any,
     visible?: boolean
     onClose?: () => void
     onChange?: (value: any) => void
     accountCreateLogs?: PULLIN.AccountCreateLogsProps[]
+    putInType?: 'NOVEL' | 'GAME'
 }
 
-const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, deliveryMode, visible, isSelectRemote, value, onChange, onClose, adLength, accountCreateLogs }) => {
+const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, deliveryMode, visible, value, onChange, onClose, adLength, accountCreateLogs, putInType }) => {
 
     /*********************************/
     const [form] = Form.useForm();
@@ -629,6 +629,7 @@ const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, delive
                 setSelectVideoVisible(false)
                 setSelectCloudData(undefined)
             }}
+            putInType={putInType}
             onChange={(content: any) => {
                 if (content.length > 0) {
                     if (deliveryMode === 'DELIVERY_MODE_COMPONENT') { // 组件化创意

+ 2 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/Material/index.tsx

@@ -6,7 +6,7 @@ import { Button, Empty, message, Tooltip, Typography } from "antd";
 import { FolderOpenOutlined, RedoOutlined, SyncOutlined } from "@ant-design/icons";
 import AddMaterial from "./addMaterial";
 import VideoNews from "@/pages/launchSystemNew/components/newsModal/videoNews";
-import { getRandomElements, shuffleArray } from "@/utils/utils";
+import { shuffleArray } from "@/utils/utils";
 import SaveUseImg from "../Save/saveUseImg";
 const { Title } = Typography;
 
@@ -220,6 +220,7 @@ const Material: React.FC<{ adData?: any[] }> = ({ adData }) => {
             onClose={() => {
                 setNewVisible(false)
             }}
+            putInType={putInType}
             accountCreateLogs={accountCreateLogs}
             onChange={({ dynamicMaterialDTos, mediaType }) => {
                 let newAddelivery = { ...addelivery, dynamicMaterialDTos, mediaType }

+ 2 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/SelectAccount/index.less

@@ -31,7 +31,7 @@
 
 .selectAccount_list {
     position: absolute;
-    width: 800px;
+    width: 900px;
     height: 400px;
     background-color: #FFF;
     margin-top: 10px;
@@ -49,6 +49,7 @@
     }
 
     .selectAccount_list_table {
+        min-height: 308px;
         height: 308px;
         padding: 0 10px;
     }

+ 143 - 143
src/pages/launchSystemV3/tencentAdPutIn/create/SelectAccount/index.tsx

@@ -1,12 +1,12 @@
 import Selector from "@/pages/launchSystemNew/launchManage/createAd/selector"
 import React, { useEffect, useState } from "react"
 import style from './index.less'
-import { Button, Input, message, Modal, Select, Table, Tag, Tooltip, Typography } from "antd";
+import { Button, Col, Form, Input, message, Modal, Row, Select, Space, Table, Tag, Tooltip, Typography } from "antd";
 import { CloseCircleFilled } from "@ant-design/icons";
-import { getGroupListApi, getAccountListApi } from "@/services/launchAdq/subgroup";
+import { getGroupListApi } from "@/services/launchAdq/subgroup";
 import { useAjax } from "@/Hook/useAjax";
 import { getAccountAssetsGroupListAllApi } from "@/services/adqV3/global";
-import { getAllUserAccountApi } from "@/services/launchAdq/adAuthorize";
+import { getUserAccountListApi } from "@/services/launchAdq/adAuthorize";
 import { arraysHaveSameValues, groupBy } from "@/utils/utils";
 import { DELIVERY_MODE_ENUM } from "../../const";
 import '../../index.less'
@@ -30,119 +30,103 @@ interface Props {
 const SelectAccount: React.FC<Props> = ({ putInType, accountCreateLogs, setAccountCreateLogs, onChange, dynamicGroup, deliveryMode, mType }) => {
 
     /***********************************/
-
+    const [form] = Form.useForm()
     const [visible, setVisible] = useState<boolean>(false)
-    const [assetSharingDeta, setAssetSharingDeta] = useState<{ authMainAccountId: number, groupId: number }>() // 资产共享组
-    const [dataSource, setDataSource] = useState<any[]>([])
-    const [options, setOptions] = useState<any[]>([])
     const [selectedRows, setSelectedRows] = useState<any[]>([])
-    const [mediaTypeGroupIds, setMediaTypeGroupIds] = useState<number[]>([])
-    const [loading, setLoading] = useState<boolean>(false)
-    const [inputAccountList, setInputAccountList] = useState<string[]>([])
     const [tipsVisible, setTipsVisible] = useState<boolean>(false)
+    const [loading, setLoading] = useState<boolean>(false)
+    const [queryForm, setQueryForm] = useState<{ accountIdList?: number[], adUnitTypeList?: string[], groupId?: number, remark?: string, sysGroupId?: number, pageNum: number, pageSize: number }>({ pageNum: 1, pageSize: 50 })
 
     const getGroupList = useAjax(() => getGroupListApi())
     const getAccountAssetsGroupListAll = useAjax((params) => getAccountAssetsGroupListAllApi(params))
-    const getAllUserAccount = useAjax(() => getAllUserAccountApi())
+    const getUserAccountList = useAjax((params) => getUserAccountListApi(params))
     /***********************************/
 
+    const onFinish = (data: any) => {
+        let oldQueryFrom = JSON.parse(JSON.stringify(queryForm))
+        let params = { ...oldQueryFrom, ...data, pageNum: 1 }
+        if (params?.accountIdList) {
+            params.accountIdList = params?.accountIdList.split(/[,,\n\s]+/ig).filter((item: any) => item)
+        } else {
+            delete params?.accountIdList
+        }
+        setQueryForm(params)
+    }
+
     // 判断选择的账号是否在同一个资产组,允许选择云端素材的关键
-    const getauthMainAccountData = (accountCreateLogs: PULLIN.AccountCreateLogsProps[]) => {
-        console.log('---->', getAllUserAccount?.data)
-        if (getAllUserAccount?.data?.length && accountCreateLogs?.length) {
-            const list = accountCreateLogs.map(item => item.accountId)
-            const selectAccountList = getAllUserAccount.data.filter((item: { accountId: any }) => list.includes(item.accountId))
-            const groupAccount = groupBy(selectAccountList, (item) => item.groupId, true)
-            const GrounpArray = Object.keys(groupAccount).map(function (group) {
-                return groupAccount[group];
-            })
-            if (GrounpArray.length === 1 && !groupAccount['undefined']) {
-                return { authMainAccountId: GrounpArray?.[0]?.[0]?.authMainAccountId || GrounpArray?.[0]?.[0]?.accountId, isSelectRemote: true }
-            } else if (GrounpArray.length === 2 && groupAccount?.['undefined']?.length === 1) {
-                const undefinedAccount = groupAccount?.['undefined'][0].accountId
-                let authMainAccountId: any
-                Object.keys(groupAccount).forEach(key => {
-                    if (key !== 'undefined') {
-                        authMainAccountId = groupAccount[key][0].authMainAccountId
+    const getauthMainAccountData = (accountCreateLogs: PULLIN.AccountCreateLogsProps[]): Promise<{ authMainAccountId?: number, isSelectRemote: boolean }> => {
+        return new Promise((resolve) => {
+            if (accountCreateLogs?.length) {
+                setLoading(true)
+                getUserAccountListApi({ adUnitTypeList: putInType === 'NOVEL' ? ['NOVEL', 'NOVEL_IAA', 'SKIT_IAA'] : ['GAME', 'GAME_IAA'], pageNum: 1, pageSize: 20000, accountIdList: accountCreateLogs.map(item => item.accountId) }).then(res => {
+                    setLoading(false)
+                    console.log('ppppp', res)
+                    if (res?.data?.records?.length) {
+                        const list = accountCreateLogs.map(item => item.accountId)
+                        const selectAccountList = res.data.records.filter((item: { accountId: any }) => list.includes(item.accountId))
+                        const groupAccount = groupBy(selectAccountList, (item) => item.groupId, true)
+                        const GrounpArray = Object.keys(groupAccount).map(function (group) {
+                            return groupAccount[group];
+                        })
+                        if (GrounpArray.length === 1 && !groupAccount['undefined']) {
+                            resolve({ authMainAccountId: GrounpArray?.[0]?.[0]?.authMainAccountId || GrounpArray?.[0]?.[0]?.accountId, isSelectRemote: true })
+                        } else if (GrounpArray.length === 2 && groupAccount?.['undefined']?.length === 1) {
+                            const undefinedAccount = groupAccount?.['undefined'][0].accountId
+                            let authMainAccountId: any
+                            Object.keys(groupAccount).forEach(key => {
+                                if (key !== 'undefined') {
+                                    authMainAccountId = groupAccount[key][0].authMainAccountId
+                                }
+                            })
+                            if (undefinedAccount === authMainAccountId) {
+                                resolve({ authMainAccountId: authMainAccountId, isSelectRemote: true })
+                            } else {
+                                resolve({ authMainAccountId: -1, isSelectRemote: false })
+                            }
+                        } else if (groupAccount?.['undefined']?.length === 1 && groupAccount?.['undefined']?.[0]?.isGroupMainAccount) {
+                            resolve({ authMainAccountId: groupAccount?.['undefined']?.[0]?.accountId, isSelectRemote: true })
+                        } else {
+                            resolve({ authMainAccountId: -1, isSelectRemote: false })
+                        }
                     }
+                }).catch(() => {
+                    setVisible(false)
+                    resolve({ authMainAccountId: undefined, isSelectRemote: false })
                 })
-                if (undefinedAccount === authMainAccountId) {
-                    return { authMainAccountId: authMainAccountId, isSelectRemote: true }
-                } else {
-                    return { authMainAccountId: -1, isSelectRemote: false }
-                }
-            }  else if (groupAccount?.['undefined']?.length === 1 && groupAccount?.['undefined']?.[0]?.isGroupMainAccount) {
-                return { authMainAccountId: groupAccount?.['undefined']?.[0]?.accountId, isSelectRemote: true }
             } else {
-                return { authMainAccountId: -1, isSelectRemote: false }
+                resolve({ authMainAccountId: undefined, isSelectRemote: false })
             }
-        } else {
-            return { authMainAccountId: undefined, isSelectRemote: false }
-        }
+        })
     }
 
     useEffect(() => {
         if (visible && accountCreateLogs?.length) {
-            setSelectedRows(accountCreateLogs)
+            setSelectedRows(JSON.parse(JSON.stringify(accountCreateLogs)))
         }
     }, [accountCreateLogs, visible])
 
+    useEffect(() => {
+        if (putInType && visible) {
+            getUserAccountList.run({ adUnitTypeList: putInType === 'NOVEL' ? ['NOVEL', 'NOVEL_IAA', 'SKIT_IAA'] : ['GAME', 'GAME_IAA'], ...queryForm })
+        }
+    }, [putInType, queryForm, visible])
+
     useEffect(() => {
         if (putInType) {
             // 获取账户组
             getGroupList.run()
             // 获取资产共享组
             getAccountAssetsGroupListAll.run({})
-            // 账户
-            getAllUserAccount.run().then(res => {
-                if (res) {
-                    let options = res?.filter((item: any) => putInType === 'NOVEL' ? ['NOVEL', 'NOVEL_IAA', 'SKIT_IAA'].includes(item.adUnitType) : putInType === 'GAME' ? ['GAME', 'GAME_IAA'].includes(item.adUnitType) : false)
-                    setOptions(options || [])
-                    setDataSource(options || [])
-                }
-            })
         }
         return () => {
             document.body.style.overflow = 'auto';
         }
     }, [putInType])
 
-    /** 获取分组里账号 */
-    const getGroupAccountList = (ids: number[]) => {
-        setMediaTypeGroupIds(ids)
-        setAssetSharingDeta(undefined)
-        if (ids.length > 0) {
-            setLoading(true)
-            let data = ids.map(id => getAccountListApi(id))
-            Promise.all(data).then(res => {
-                setLoading(false)
-                if (res?.length > 0 && res.every((item: { code: number }) => item.code === 200)) {
-                    let userArr: any[] = []
-                    res.forEach((item: { data: { adAccountList: { accountId: number, id: number }[] } }) => {
-                        item.data.adAccountList.forEach(acc => {
-                            let obj = userArr.find((item: { accountId: number }) => item.accountId === acc.accountId)
-                            if (!obj) {
-                                userArr.push(acc.accountId)
-                            }
-                        })
-                    })
-                    setDataSource(options.filter(item => userArr.includes(item.accountId)))
-                } else {
-                    message.error('操作异常')
-                }
-            })
-        } else {
-            setDataSource(options)
-        }
-    }
-
     const handleOk = (isClear: boolean) => {
         document.body.style.overflow = 'auto';
         onChange?.(selectedRows, isClear)
         setSelectedRows([])
-        setInputAccountList([])
-        setMediaTypeGroupIds([])
-        setAssetSharingDeta(undefined)
         setVisible(false)
         setTipsVisible(false)
     }
@@ -150,9 +134,6 @@ const SelectAccount: React.FC<Props> = ({ putInType, accountCreateLogs, setAccou
     const handleCancel = () => {
         document.body.style.overflow = 'auto';
         setSelectedRows([])
-        setInputAccountList([])
-        setMediaTypeGroupIds([])
-        setAssetSharingDeta(undefined)
         setVisible(false)
     }
 
@@ -238,68 +219,84 @@ const SelectAccount: React.FC<Props> = ({ putInType, accountCreateLogs, setAccou
             </Selector>
             {visible && <div className={style.selectAccount_list}>
                 <div className={style.selectAccount_list_header}>
-                    <Input.TextArea
-                        rows={1}
-                        autoSize={{ minRows: 1, maxRows: 1 }}
-                        placeholder="账户/备注(多个,,空格换行)"
-                        style={{ width: 200 }}
-                        allowClear
-                        onChange={e => {
-                            let input = e.target.value
-                            let newInput: string[] = input ? input?.split(/[,,\n\s]+/ig).filter((item: any) => item) : []
-                            setInputAccountList(newInput)
-                        }}
-                    />
-                    <Select
-                        mode="multiple"
-                        style={{ minWidth: 200 }}
-                        placeholder="快捷选择媒体账户组"
-                        maxTagCount={1}
-                        allowClear
-                        filterOption={(input: any, option: any) => {
-                            return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
-                        }}
-                        value={mediaTypeGroupIds}
-                        onChange={(e) => { getGroupAccountList(e) }}
-                    >
-                        {getGroupList?.data && getGroupList?.data?.map((item: any) => <Select.Option value={item.groupId} key={item.groupId}>{item.groupName}</Select.Option>)}
-                    </Select>
-                    <Select
-                        allowClear
-                        showSearch
-                        placeholder="账户资产共享组"
-                        filterOption={(input: any, option: any) => {
-                            return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
-                        }}
-                        style={{ width: 145 }}
-                        value={assetSharingDeta?.groupId}
-                        onChange={(_, option) => {
-                            setMediaTypeGroupIds([])
-                            if (option) {
-                                let assetSharingDeta = { groupId: option.value, authMainAccountId: option.authMainAccountId }
-                                setAssetSharingDeta({ groupId: option.value, authMainAccountId: option.authMainAccountId })
-                                setDataSource(options?.filter((item: { accountId: number; groupId: number }) => assetSharingDeta?.groupId ? (item.accountId?.toString() === assetSharingDeta?.authMainAccountId?.toString() || item?.groupId?.toString() === assetSharingDeta?.groupId?.toString()) : true))
-                            } else {
-                                setAssetSharingDeta(undefined)
-                                setDataSource(options)
-                            }
-                        }}
-                    >
-                        {getAccountAssetsGroupListAll?.data?.map((item: { accountId: any; authMainAccountId: number; id: number; accountGroupName: string }) => <Select.Option value={item.accountId} authMainAccountId={item.authMainAccountId} key={item.id}>{item.accountGroupName}({item.authMainAccountId})</Select.Option>)}
-                    </Select>
+                    <Form layout="inline" className='queryForm' name="basicSelectAcc" form={form} onFinish={onFinish}>
+                        <Row gutter={[0, 6]}>
+                            <Col><Form.Item name='accountIdList'>
+                                <Input.TextArea
+                                    rows={1}
+                                    autoSize={{ minRows: 1, maxRows: 1 }}
+                                    placeholder="账户(多个,,空格换行)"
+                                    style={{ width: 180 }}
+                                    allowClear
+                                />
+                            </Form.Item></Col>
+                            <Col><Form.Item name='remark'>
+                                <Input
+                                    placeholder="备注"
+                                    style={{ width: 120 }}
+                                    allowClear
+                                />
+                            </Form.Item></Col>
+                            <Col><Form.Item name='sysGroupId'>
+                                <Select
+                                    mode="multiple"
+                                    style={{ minWidth: 180 }}
+                                    placeholder="快捷选择媒体账户组"
+                                    maxTagCount={1}
+                                    allowClear
+                                    filterOption={(input: any, option: any) => {
+                                        return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
+                                    }}
+                                >
+                                    {getGroupList?.data && getGroupList?.data?.map((item: any) => <Select.Option value={item.groupId} key={item.groupId}>{item.groupName}</Select.Option>)}
+                                </Select>
+                            </Form.Item></Col>
+                            <Col><Form.Item name='groupId'>
+                                <Select
+                                    allowClear
+                                    showSearch
+                                    placeholder="账户资产共享组"
+                                    filterOption={(input: any, option: any) => {
+                                        return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
+                                    }}
+                                    style={{ width: 145 }}
+                                >
+                                    {getAccountAssetsGroupListAll?.data?.map((item: { accountId: any; authMainAccountId: number; id: number; accountGroupName: string }) => <Select.Option value={item.id} key={item.id}>{item.accountGroupName}({item.authMainAccountId})</Select.Option>)}
+                                </Select>
+                            </Form.Item></Col>
+                            <Col>
+                                <Space>
+                                    <Button type="primary" htmlType="submit">搜索</Button>
+                                    {/* <Button onClick={() => form.resetFields()}>重置</Button> */}
+                                </Space>
+                            </Col>
+                        </Row>
+                    </Form>
+
+                    {/* 
+                     */}
                 </div>
                 <div className={style.selectAccount_list_table}>
                     <Table
                         size={'small'}
                         bordered
-                        dataSource={dataSource.filter(item => inputAccountList?.length > 0 ? inputAccountList?.some(val => item.accountId?.toString().toLowerCase()?.includes(val) || item?.remark?.toString().toLowerCase()?.includes(val)) : true)}
+                        dataSource={getUserAccountList?.data?.records}
                         rowKey={'accountId'}
                         scroll={{ y: 220 }}
                         pagination={{
-                            pageSize: 50,
-                            showTotal: total => `总共 ${total} 账户`
+                            pageSize: getUserAccountList?.data?.size || 50,
+                            current: getUserAccountList?.data?.current || 1,
+                            showTotal: total => `总共 ${total} 账户`,
+                            total: getUserAccountList?.data?.total,
+                            showSizeChanger: true,
+                            showLessItems: true,
+                            defaultCurrent: 1,
+                            defaultPageSize: 50,//默认初始的每页条数
+                            onChange: (page, pageSize) => {
+                                setQueryForm({ ...queryForm, pageNum: page, pageSize })
+                            }
                         }}
-                        loading={getAllUserAccount.loading || loading}
+                        loading={getUserAccountList.loading || loading}
                         columns={[
                             {
                                 title: '账号',
@@ -383,6 +380,7 @@ const SelectAccount: React.FC<Props> = ({ putInType, accountCreateLogs, setAccou
                     <Button
                         className={style.resetCss}
                         type="primary"
+                        loading={loading}
                         style={{ marginLeft: 8 }}
                         onClick={() => {
                             if (accountCreateLogs?.length && arraysHaveSameValues(accountCreateLogs.map(item => item.accountId), selectedRows.map(item => item.accountId))) {
@@ -453,15 +451,17 @@ const SelectAccount: React.FC<Props> = ({ putInType, accountCreateLogs, setAccou
                                     message.error('请联系管理员')
                                 }
                             }
-
                             if (isY && authMainAccountId) {
                                 // 2.有的话判断 判断账户是否为一组
-                                const authMainData = getauthMainAccountData(selectedRows)
-                                if (authMainData.isSelectRemote && authMainData.authMainAccountId === authMainAccountId) {
-                                    handleOk(false)
-                                } else {
-                                    setTipsVisible(true)
-                                }
+                                getauthMainAccountData(selectedRows).then(authMainData => {
+                                    console.log('authMainData---->', authMainData)
+                                    if (authMainData.isSelectRemote && authMainData.authMainAccountId === authMainAccountId) {
+                                        handleOk(false)
+                                    } else {
+                                        setTipsVisible(true)
+                                    }
+                                })
+
                                 // 3.是的话 判断是以前那组吗
                             } else {
                                 handleOk(false)

+ 41 - 18
src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx

@@ -17,7 +17,7 @@ import PageList from "./PageList"
 import { cartesianProduct, distributeArray, processData, randomString, splitArrayIntoRandomChunks } from "@/utils/utils"
 import columns from "./tableConfig"
 import SubmitModal from "./submitModal"
-import { createAdgroupTaskApi, getSelectTaskDetailApi } from "@/services/adqV3"
+import { createAdgroupTaskApi, createAdgroupTaskV2Api, getSelectTaskDetailApi } from "@/services/adqV3"
 import WechatAccount from "../../components/WechatAccount"
 import Title from "antd/lib/typography/Title"
 import { getCreativeDetailsApi } from "@/services/adqV3/global"
@@ -62,6 +62,7 @@ const Create: React.FC = () => {
     const [ownerAccountId, setOwnerAccountId] = useState<number>()
 
     const createAdgroupTask = useAjax((params) => createAdgroupTaskApi(params))
+    const createAdgroupTaskV2 = useAjax((params) => createAdgroupTaskV2Api(params))
     const getSelectTaskDetail = useAjax((params) => getSelectTaskDetailApi(params))
     const getCreativeDetails = useAjax((params) => getCreativeDetailsApi(params))
     /*******************************************/
@@ -806,23 +807,45 @@ const Create: React.FC = () => {
             taskType: putInType,
             ...copyTask
         }
-        createAdgroupTask.run(params).then(res => {
-            if (res) {
-                Modal.success({
-                    content: '任务提交成功',
-                    bodyStyle: { fontWeight: 700 },
-                    okText: '跳转任务列表',
-                    closable: true,
-                    onOk: () => {
-                        sessionStorage.setItem('CAMPV3', values?.taskName)
-                        window.location.href = '/#/launchSystemV3/tencentAdPutIn/taskList'
-                    },
-                    onCancel: () => {
-                        setSubVisible(false)
-                    }
-                })
-            }
-        })
+
+        if (values?.submitRule !== 0) {
+            createAdgroupTaskV2.run(params).then(res => {
+                if (res) {
+                    Modal.success({
+                        content: '任务提交成功',
+                        bodyStyle: { fontWeight: 700 },
+                        okText: '跳转任务列表',
+                        closable: true,
+                        onOk: () => {
+                            sessionStorage.setItem('CAMPV3', values?.taskName)
+                            window.location.href = '/#/launchSystemV3/tencentAdPutIn/taskList'
+                        },
+                        onCancel: () => {
+                            setSubVisible(false)
+                        }
+                    })
+                }
+            })
+        } else {
+            delete params?.submitRule
+            createAdgroupTask.run(params).then(res => {
+                if (res) {
+                    Modal.success({
+                        content: '任务提交成功',
+                        bodyStyle: { fontWeight: 700 },
+                        okText: '跳转任务列表',
+                        closable: true,
+                        onOk: () => {
+                            sessionStorage.setItem('CAMPV3', values?.taskName)
+                            window.location.href = '/#/launchSystemV3/tencentAdPutIn/taskList'
+                        },
+                        onCancel: () => {
+                            setSubVisible(false)
+                        }
+                    })
+                }
+            })
+        }
     }
 
     const clearData = () => {

+ 109 - 15
src/pages/launchSystemV3/tencentAdPutIn/create/submitModal.tsx

@@ -1,7 +1,8 @@
-import { Form, Input, Modal } from "antd"
+import { Button, Card, Checkbox, DatePicker, Form, Input, InputNumber, message, Modal, Radio, Space } from "antd"
 import React, { useState } from "react"
 import moment from "moment"
 import { randomString } from "@/utils/utils"
+import { RangePickerProps } from "antd/lib/date-picker"
 
 /**
  * 设置名称
@@ -18,33 +19,126 @@ const SubmitModal: React.FC<Props> = (props) => {
     /********************/
     const { visible, onClose, onChange, ajax } = props
     const [form] = Form.useForm()
-    const [initialValues] = useState<{ taskName: string }>({ taskName: '任务' + moment().format('MM_DD_HH_mm_ss') + '_' + randomString(true, 3, 5) })
+    const submitRule = Form.useWatch('submitRule', form)
 
-    const handleOk = async () => {
-        form.submit()
-        let data = await form.validateFields()
-        onChange && onChange(data)
+    const handleOk = (values: any) => {
+        let params = JSON.parse(JSON.stringify(values))
+        if (params?.submitStartTime) {
+            params.submitStartTime = moment(params.submitStartTime).format('YYYY-MM-DD HH:mm:ss')
+        }
+        if (params?.submitInterval) {
+            params.submitInterval = params.submitInterval * 60
+        }
+        onChange?.(params)
     }
 
+
+    const disabledDate: RangePickerProps['disabledDate'] = current => {
+        // Can not select days before today and today
+        return current && current <= moment().startOf('day');
+    };
+
     return <Modal
-        title="提交任务"
+        title={<strong style={{ fontSize: 20 }}>提交</strong>}
         className="modalResetCss"
         open={visible}
         confirmLoading={ajax?.loading}
-        onOk={handleOk}
-        onCancel={() => { onClose && onClose() }}
+        footer={null}
+        onCancel={onClose}
+        bodyStyle={{ padding: '0 0 40px', position: 'relative', borderRadius: '0 0 8px 8px' }}
+        maskClosable={false}
+        width={700}
     >
         <Form
             name="basicSubmit"
             form={form}
-            labelCol={{ span: 4 }}
-            wrapperCol={{ span: 20 }}
+            labelCol={{ span: 6 }}
+            wrapperCol={{ span: 18 }}
             autoComplete="off"
-            initialValues={{ ...initialValues }}
-
+            style={{ backgroundColor: '#f1f4fc', maxHeight: 650, overflow: 'hidden', overflowY: 'auto', padding: '10px', borderRadius: '0 0 8px 8px' }}
+            scrollToFirstError={{
+                behavior: 'smooth',
+                block: 'center'
+            }}
+            onFinishFailed={({ errorFields }) => {
+                message.error(errorFields?.[0]?.errors?.[0])
+            }}
+            onFinish={handleOk}
+            colon={false}
+            labelAlign="left"
+            initialValues={{
+                taskName: '任务' + moment().format('MMDDHHmmss') + '_' + randomString(true, 3, 5),
+                submitRule: 0,
+                submitType: 1,
+                submitByMultiMediaAccount: false
+            }}
         >
-            <Form.Item label={<strong>任务名称</strong>} name="taskName">
-                <Input placeholder="请输入任务名称" />
+            <Card
+                title={<strong style={{ fontSize: 18 }}>提交配置</strong>}
+                className="cardResetCss"
+            >
+                <Form.Item label={<strong>提交规则</strong>} name="submitRule" rules={[{ required: true, message: '请选择提交规则!' }]}>
+                    <Radio.Group buttonStyle="solid">
+                        <Radio.Button value={0}>默认(立即提交)</Radio.Button>
+                        <Radio.Button value={1}>立即提交</Radio.Button>
+                        <Radio.Button value={2}>定时提交</Radio.Button>
+                        <Radio.Button value={3}>分批次提交</Radio.Button>
+                    </Radio.Group>
+                </Form.Item>
+
+                {[2, 3].includes(submitRule) && <>
+                    <Form.Item
+                        label={<strong>开始提交时间</strong>}
+                        name="submitStartTime"
+                        rules={[
+                            { required: true, message: '请选择开始提交时间!' },
+                            () => ({
+                                validator(_, value) {
+                                    if (value && moment().add(10, 'minutes') > value) {
+                                        return Promise.reject(new Error('开始提交时间必须大于10分钟后'));
+                                    }
+                                    return Promise.resolve();
+                                },
+                            })
+                        ]}
+                    >
+                        <DatePicker showTime disabledDate={disabledDate} />
+                    </Form.Item>
+                </>}
+                {submitRule === 3 ? <>
+                    <Form.Item label={<strong>分批提交类型</strong>} name="submitType" rules={[{ required: true, message: '请选择分批提交类型!' }]}>
+                        <Radio.Group buttonStyle="solid">
+                            <Radio.Button value={1}>分批提交广告</Radio.Button>
+                            {/* <Radio.Button value={2}>分批提交创意</Radio.Button> */}
+                        </Radio.Group>
+                    </Form.Item>
+                    <Form.Item label={<strong>每次提交时间间隔</strong>} required tooltip="是否多媒体账户提交(true false) 多媒体账户提交- 如果是true,5个账户10条广告中,每个账户的第一条广告都要执行 如果是false ,5个账户10个广告,10个广告依次按照时间间隔执行">
+                        <Space>
+                            <Form.Item name="submitInterval" rules={[{ required: true, message: '请输入每次提交时间间隔!' }]} noStyle>
+                                <InputNumber addonAfter={'分钟'} min={10} max={1440} style={{ width: 250 }} placeholder="请输入每次提交时间间隔" />
+                            </Form.Item>
+                            <Form.Item name="submitByMultiMediaAccount" noStyle valuePropName="checked">
+                                <Checkbox>是否多媒体账户提交</Checkbox>
+                            </Form.Item>
+                        </Space>
+                    </Form.Item>
+                    <Form.Item label={<strong>每批次提交数</strong>} name="submitAdNum" rules={[{ required: true, message: '请输入每批次提交数!' }]}>
+                        <InputNumber min={1} style={{ width: 250 }} placeholder="请输入每批次提交数" />
+                    </Form.Item>
+                </> : null}
+
+                <Form.Item label={<strong>任务名称</strong>} name="taskName" rules={[{ required: true, message: '请输入任务名称!' }]}>
+                    <Input placeholder="请输入任务名称" />
+                </Form.Item>
+            </Card>
+
+            <Form.Item className="submit_pull" wrapperCol={{ span: 24 }}>
+                <Space>
+                    <Button onClick={onClose}>取消</Button>
+                    <Button type="primary" htmlType="submit" className="modalResetCss">
+                        确定
+                    </Button>
+                </Space>
             </Form.Item>
         </Form>
     </Modal>

+ 74 - 0
src/pages/launchSystemV3/tencentAdPutIn/taskList/executeLog.tsx

@@ -0,0 +1,74 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getSelectAdTaskLogListApi } from "@/services/adqV3"
+import { Button, Modal, Space, Table, Tag } from "antd"
+import React, { useEffect, useState } from "react"
+import { columnsExecuteLog } from "./tableConfig"
+
+interface Props {
+    data: any
+    visible?: boolean,
+    onClose?: () => void
+}
+
+/**
+ * 任务执行记录
+ * @param param0 
+ * @returns 
+ */
+const ExecuteLog: React.FC<Props> = ({ data, visible, onClose }) => {
+
+    /*************************************/
+    const { id, taskName } = data
+    const [queryForm, setQueryForm] = useState<PULLIN.GetTaskV3LogProps>({ pageNum: 1, pageSize: 20 })
+
+    const getSelectAdTaskLogList = useAjax((params) => getSelectAdTaskLogListApi(params), { formatResult: true })
+    /*************************************/
+
+    useEffect(() => {
+        getList()
+    }, [queryForm, id])
+
+    /** 获取列表 */
+    const getList = () => {
+        if (id) {
+            getSelectAdTaskLogList.run({ ...queryForm, taskId: id })
+        }
+    }
+
+    return <Modal
+        title={<Space>
+            <strong>{taskName + ' 执行记录'}</strong>
+            <Button type="link" loading={getSelectAdTaskLogList.loading} onClick={() => getSelectAdTaskLogList.refresh()}>刷新</Button>
+        </Space>}
+        className="modalResetCss"
+        open={visible}
+        width={750}
+        onCancel={onClose}
+        footer={null}
+    >
+        <Table
+            columns={columnsExecuteLog()}
+            dataSource={getSelectAdTaskLogList?.data?.data?.records}
+            size="small"
+            loading={getSelectAdTaskLogList?.loading}
+            scroll={{ y: 600 }}
+            bordered
+            rowKey={'id'}
+            pagination={{
+                pageSize: queryForm.pageSize,
+                current: queryForm.pageNum,
+                total: getSelectAdTaskLogList?.data?.data?.total || 0,
+                showTotal: (total) => <Tag color="cyan">总共{total}数据</Tag>
+            }}
+            onChange={(pagination) => {
+                let { current, pageSize } = pagination
+                let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                newQueryForm.pageNum = current
+                newQueryForm.pageSize = pageSize
+                setQueryForm(newQueryForm)
+            }}
+        />
+    </Modal>
+}
+
+export default React.memo(ExecuteLog)

+ 74 - 5
src/pages/launchSystemV3/tencentAdPutIn/taskList/index.tsx

@@ -1,13 +1,14 @@
-import { Button, Card, DatePicker, Input, Table, Tag } from "antd"
+import { Button, Card, DatePicker, Input, message, Popconfirm, Table, Tag } from "antd"
 import React, { useEffect, useState } from "react"
 import '../index.less'
 import { SearchOutlined } from "@ant-design/icons"
 import { useAjax } from "@/Hook/useAjax"
-import { getTaskV3ListApi } from "@/services/adqV3"
+import { getTaskV3ListApi, stopAdgroupTaskApi } from "@/services/adqV3"
 import moment from "moment"
 import { useModel } from "umi"
 import columns from "./tableConfig"
 import Log from "./log"
+import ExecuteLog from "./executeLog"
 
 const TaskList: React.FC = () => {
 
@@ -17,9 +18,12 @@ const TaskList: React.FC = () => {
     const [queryParams, setQueryParams] = useState<PULLIN.GetTaskV3Props>({ pageNum: 1, pageSize: 20 })
     const [queryParamsNew, setQueryParamsNew] = useState<PULLIN.GetTaskV3Props>({ pageNum: 1, pageSize: 20 })
     const [logVisible, setLogVisible] = useState<boolean>(false)
+    const [executeLogVisible, setExecuteLogVisible] = useState<boolean>(false)
     const [logData, setLogData] = useState<any>({})
+    const [selectedRows, setSelectedRows] = useState<any[]>([])
 
     const getTaskV3List = useAjax((params) => getTaskV3ListApi(params), { formatResult: true })
+    const stopAdgroupTask = useAjax((params) => stopAdgroupTaskApi(params))
     /****************************************/
 
     useEffect(() => {
@@ -37,7 +41,7 @@ const TaskList: React.FC = () => {
     }, [queryParamsNew])
 
 
-    const callback = (data: any, type: 'log' | 'page' | 'copy', allData?: any) => {
+    const callback = (data: any, type: 'log' | 'page' | 'copy' | 'zxLog', allData?: any) => {
         switch (type) {
             case 'log':
                 setLogData({ ...data })
@@ -47,15 +51,35 @@ const TaskList: React.FC = () => {
                 sessionStorage.setItem('TASKID3.0', data.id)
                 window.location.href = '/#/launchSystemV3/tencentAdPutIn/create'
                 break
+            case 'zxLog':
+                setLogData({ ...data })
+                setExecuteLogVisible(true)
+                break
         }
     }
 
+    const handleSuspend = () => {
+        stopAdgroupTask.run(selectedRows.map(item => item.id)).then(res => {
+            if (res) {
+                message.success('终止成功')
+                getTaskV3List.refresh()
+            }
+        })
+    }
+
     return <Card
         className="cardResetCss"
         title={<div className="flexStart" style={{ gap: 8 }}>
             <Input style={{ width: 180 }} placeholder="请输入任务名称" value={queryParams?.taskName} allowClear onChange={(e) => setQueryParams({ ...queryParams, taskName: e.target.value, pageNum: 1 })} />
             <DatePicker.RangePicker style={{ width: 250 }} placeholder={['创建开始时间', '创建结束时间']} value={(queryParams?.createTimeMin && queryParams?.createTimeMax) ? [moment(queryParams?.createTimeMin), moment(queryParams?.createTimeMax)] as any : undefined} onChange={(_, option) => setQueryParams({ ...queryParams, createTimeMin: option[0], createTimeMax: option[1], pageNum: 1 })} />
-            <Button type="primary" icon={<SearchOutlined />} onClick={() => setQueryParamsNew({ ...queryParams })}>搜索</Button>
+            <Button type="primary" icon={<SearchOutlined />} loading={getTaskV3List.loading} onClick={() => setQueryParamsNew({ ...queryParams })}>搜索</Button>
+            <Popconfirm
+                title="确定中止任务?"
+                onConfirm={handleSuspend}
+                disabled={selectedRows.length === 0}
+            >
+                <Button type="primary"danger loading={stopAdgroupTask.loading} disabled={selectedRows.length === 0}>终止任务</Button>
+            </Popconfirm>
         </div>}
     >
         <Table
@@ -80,17 +104,62 @@ const TaskList: React.FC = () => {
                 setQueryParamsNew(newQueryForm)
                 setQueryParams(newQueryForm)
             }}
+            rowSelection={{
+                selectedRowKeys: selectedRows.map(item => item.id),
+                onSelect: (record: { id: number, mpName: string }, selected: boolean) => {
+                    if (selected) {
+                        selectedRows.push({ ...record })
+                        setSelectedRows([...selectedRows])
+                    } else {
+                        let newSelectAccData = selectedRows.filter((item: { id: number }) => item.id !== record.id)
+                        setSelectedRows([...newSelectAccData])
+                    }
+                },
+                onSelectAll: (selected: boolean, selectedRowss: { id: number }[], changeRows: { id: number }[]) => {
+                    if (selected) {
+                        let newSelectAccData = [...selectedRows]
+                        changeRows.forEach((item: { id: number }) => {
+                            let index = newSelectAccData.findIndex((ite: { id: number }) => ite.id === item.id)
+                            if (index === -1) {
+                                let data: any = { ...item }
+                                newSelectAccData.push(data)
+                            }
+                        })
+                        setSelectedRows([...newSelectAccData])
+                    } else {
+                        let newSelectAccData = selectedRows.filter((item: { id: number }) => {
+                            let index = changeRows.findIndex((ite: { id: number }) => ite.id === item.id)
+                            if (index !== -1) {
+                                return false
+                            } else {
+                                return true
+                            }
+                        })
+                        setSelectedRows([...newSelectAccData])
+                    }
+                }
+            }}
         />
 
         {/* 任务日志 */}
         {logVisible && <Log
             visible={logVisible}
             data={logData}
-            onClose={() =>{
+            onClose={() => {
                 setLogData({})
                 setLogVisible(false)
             }}
         />}
+
+        {/* 执行记录 */}
+        {executeLogVisible && <ExecuteLog
+            visible={executeLogVisible}
+            data={logData}
+            onClose={() => {
+                setLogData({})
+                setExecuteLogVisible(false)
+            }}
+        />}
     </Card>
 }
 

+ 78 - 12
src/pages/launchSystemV3/tencentAdPutIn/taskList/tableConfig.tsx

@@ -1,5 +1,5 @@
 import React from "react"
-import { Badge, Popover, Space, TableProps, Typography } from "antd"
+import { Badge, Popover, Space, TableProps, Tag, Typography } from "antd"
 import { AD_STATUS_ENUM, DELIVERY_MODE_ENUM, DYNAMIC_CREATIVE_TYPE_ENUM, MARKETING_GOAL_ENUM } from "../const"
 import TargetingTooltip from "../../components/TargetingTooltip"
 import { QuestionCircleFilled } from "@ant-design/icons"
@@ -8,20 +8,22 @@ import DynamicTooltip from "../../components/DynamicTooltip"
 import { copy } from "@/utils/utils"
 
 
-const columns = (geoLocationList: any, modelList: any, callback: (data: any, type: 'log' | 'page' | 'copy', allData?: any) => void): TableProps<any>['columns'] => {
+const columns = (geoLocationList: any, modelList: any, callback: (data: any, type: 'log' | 'page' | 'copy' | 'zxLog', allData?: any) => void): TableProps<any>['columns'] => {
 
     return [
         {
             title: '操作',
             dataIndex: 'cz',
             key: 'cz',
-            width: 80,
-            align: 'center',
+            width: 130,
             fixed: 'left',
             render: (_, records) => {
                 return <Space>
                     <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback(records, 'log', records) }}>日志</a>
                     <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback(records, 'copy') }}>复制</a>
+                    {records?.submitRule && <>
+                        <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback(records, 'zxLog') }}>执行记录</a>
+                    </>}
                 </Space>
             }
         },
@@ -29,7 +31,7 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
             title: '任务类型',
             dataIndex: 'taskType',
             key: 'taskType',
-            width: 70,
+            width: 50,
             align: 'center',
             fixed: 'left',
             render(value) {
@@ -40,11 +42,17 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
             title: '任务广告状态',
             dataIndex: 'status',
             key: 'status',
-            width: 100,
+            width: 80,
             align: 'center',
             fixed: 'left',
             render(value) {
-                let a = { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }} >执行中</span>} />, 1: <Badge style={{ fontSize: 12 }} status="error" text={<span style={{ fontSize: 12 }} >全部失败</span>} />, 2: <Badge style={{ fontSize: 12 }} status="warning" text={<span style={{ fontSize: 12 }} >部分成功</span>} />, 100: <Badge style={{ fontSize: 12 }} status="success" text={<span style={{ fontSize: 12 }} >全部成功</span>} /> }
+                let a = { 
+                    0: <Badge status="processing" text={<span style={{ fontSize: 12 }} >执行中</span>} />, 
+                    1: <Badge style={{ fontSize: 12 }} status="error" text={<span style={{ fontSize: 12 }} >全部失败</span>} />, 
+                    2: <Badge style={{ fontSize: 12 }} status="warning" text={<span style={{ fontSize: 12 }} >部分成功</span>} />, 
+                    100: <Badge style={{ fontSize: 12 }} status="success" text={<span style={{ fontSize: 12 }} >全部成功</span>} />,
+                    '-2': <Badge style={{ fontSize: 12 }} status="error" text={<span style={{ fontSize: 12 }} >任务终止</span>} />,
+                }
                 return a[value as keyof typeof a]
             },
         },
@@ -59,12 +67,23 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
                 return <span style={{ fontSize: 12 }}>{value}</span>
             }
         },
+        {
+            title: '任务提交规则',
+            dataIndex: 'submitRule',
+            key: 'submitRule',
+            width: 80,
+            align: 'center',
+            render(value) {
+                let a = { 0: <Tag color="#108ee9"><span style={{ fontSize: 12 }} >默认</span></Tag>, 1: <Tag color="#f50"><span style={{ fontSize: 12 }} >立即提交</span></Tag>, 2: <Tag color="#2db7f5"><span style={{ fontSize: 12 }} >定时提交</span></Tag>, 3: <Tag color="#87d068"><span style={{ fontSize: 12 }} >分批提交</span></Tag> }
+                return a[value as keyof typeof a] || '--'
+            },
+        },
         {
             title: 'ID',
             dataIndex: 'id',
             key: 'id',
             align: 'center',
-            width: 60,
+            width: 70,
             ellipsis: true,
             render(value) {
                 return <span style={{ fontSize: 12 }}>{value}</span>
@@ -75,7 +94,7 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
             dataIndex: 'configuredStatus',
             key: 'configuredStatus',
             align: 'center',
-            width: 80,
+            width: 50,
             render: (_, b) => {
                 let configuredStatus = b?.adgroupDTO?.configuredStatus
                 if (configuredStatus) {
@@ -159,7 +178,8 @@ const columns = (geoLocationList: any, modelList: any, callback: (data: any, typ
             dataIndex: 'createTime',
             key: 'createTime',
             align: 'center',
-            width: 140,
+            width: 125,
+            ellipsis: true,
             render: (value) => {
                 return <span style={{ fontSize: "12px" }}>{value || '--'}</span>
             }
@@ -224,7 +244,7 @@ export const columnsLog = (sync: (value: any) => void): TableProps<any>['columns
             ellipsis: true,
             align: 'center',
             render(value) {
-                return <span style={{ fontSize: 12 }}>{MARKETING_GOAL_ENUM[value  as keyof typeof MARKETING_GOAL_ENUM]}</span>
+                return <span style={{ fontSize: 12 }}>{MARKETING_GOAL_ENUM[value as keyof typeof MARKETING_GOAL_ENUM]}</span>
             },
         },
         {
@@ -244,7 +264,7 @@ export const columnsLog = (sync: (value: any) => void): TableProps<any>['columns
             width: 90,
             align: 'center',
             render(value) {
-                const a = { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }}>执行中</span>} />, 1: <Badge status="error" style={{ fontSize: 12 }} text={<span>失败</span>} />, 101: <Badge status="warning" style={{ fontSize: 12 }} text={<span>同步异常</span>} />, 100: <Badge style={{ fontSize: 12 }} status="success" text={<span>成功</span>} /> }
+                const a = { 0: <Badge status="processing" text={<span style={{ fontSize: 12 }}>执行中</span>} />, 1: <Badge status="error" text={<span style={{ fontSize: 12 }}>失败</span>} />, 101: <Badge status="warning" text={<span style={{ fontSize: 12 }}>同步异常</span>} />, 100: <Badge status="success" text={<span style={{ fontSize: 12 }}>成功</span>} /> }
                 return a[value as keyof typeof a]
             },
         },
@@ -382,4 +402,50 @@ export const columnsDynamicLog = (): TableProps<any>['columns'] => {
             }
         }
     ]
+}
+
+
+export const columnsExecuteLog = (): TableProps<any>['columns'] => {
+    return [
+        {
+            title: '执行时间',
+            dataIndex: 'nextTime',
+            key: 'nextTime',
+            width: 145,
+            ellipsis: true,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            }
+        },
+        {
+            title: '提交广告数量',
+            dataIndex: 'submitAdCount',
+            key: 'submitAdCount',
+            width: 110,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            }
+        },
+        {
+            title: '提交创意总数',
+            dataIndex: 'submitCreativeCount',
+            key: 'submitCreativeCount',
+            width: 110,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            }
+        },
+        {
+            title: '提交状态',
+            dataIndex: 'submitStatus',
+            key: 'submitStatus',
+            render(value) {
+                const a = { 2: <Badge status="processing" text={<span style={{ fontSize: 12 }}>执行中</span>} />, 0: <Badge status="error" text={<span style={{ fontSize: 12 }}>已终止</span>} />, 1: <Badge status="default" text={<span style={{ fontSize: 12 }}>待执行</span>} />, 3: <Badge status="success" text={<span style={{ fontSize: 12 }}>完成</span>} /> }
+                return a[value as keyof typeof a]
+            },
+        }
+    ]
 }

+ 12 - 0
src/services/adqV3/global.ts

@@ -864,4 +864,16 @@ export async function delCopyWritingApi(id: number) {
     return request(api + `/adq/copyWriting/delById/${id}`, {
         method: 'DELETE',
     })
+}
+
+/**
+ * 批量删除
+ * @param ids 
+ * @returns 
+ */
+export async function delsCopyWritingApi(ids: number[]) {
+    return request(api + `/adq/copyWriting/batchDelByIds`, {
+        method: 'DELETE',
+        data: ids
+    })
 }

+ 32 - 1
src/services/adqV3/index.ts

@@ -89,10 +89,17 @@ export async function createAdgroupTaskApi(data: any) {
 }
 
 /**
- * 任务列表
+ * 多类型创建任务
  * @param data 
  * @returns 
  */
+export async function createAdgroupTaskV2Api(data: any) {
+    return request(api + `/adq/v3/adgroup/createAdgroupTaskV2`, {
+        method: 'POST',
+        data
+    })
+}
+
 export async function getTaskV3ListApi(data: PULLIN.GetTaskV3Props) {
     return request(api + `/adq/v3/adgroup/selectByPage`, {
         method: 'POST',
@@ -100,6 +107,18 @@ export async function getTaskV3ListApi(data: PULLIN.GetTaskV3Props) {
     })
 }
 
+/**
+ * 中止任务
+ * @param data 
+ * @returns 
+ */
+export async function stopAdgroupTaskApi(data: number[]) {
+    return request(api + `/adq/v3/adgroup/stopAdgroupTask`, {
+        method: 'POST',
+        data
+    })
+}
+
 /**
  * 获取详情
  * @param id 
@@ -123,6 +142,18 @@ export async function getTaskV3LogListApi(data: PULLIN.GetTaskV3LogProps) {
     })
 }
 
+/**
+ * 任务执行记录
+ * @param data 
+ * @returns 
+ */
+export async function getSelectAdTaskLogListApi(data: PULLIN.GetTaskV3LogProps) {
+    return request(api + `/adq/v3/adgroup/selectAdTaskLog`, {
+        method: 'POST',
+        data
+    })
+}
+
 /**
  * 查询创意创建日志
  * @param data 

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

@@ -51,6 +51,17 @@ export async function getAllUserAccountApi() {
     });
 }
 
+/**
+ * 分页查询账户列表
+ * @returns 
+ */
+export async function getUserAccountListApi(data: { accountIdList?: number[], adUnitTypeList?: string[], groupId?: number, remark?: string, sysGroupId?: number, pageNum: number, pageSize: number }) {
+    return request(api + '/adq/adAccount/pageOfOwnerUser', {
+        method: 'POST',
+        data
+    });
+}
+
 /**
  * 获取所有账户
  * @returns