Browse Source

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

wjx 2 years ago
parent
commit
874e51b8e2
20 changed files with 2677 additions and 117 deletions
  1. 10 2
      src/pages/launchSystemNew/components/goodsModal/index.tsx
  2. 1 1
      src/pages/launchSystemNew/components/textAideInput/index.tsx
  3. 2 2
      src/pages/launchSystemNew/launchManage/createAd/creative/index.tsx
  4. 166 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/index.tsx
  5. 196 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/brandImage.tsx
  6. 80 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/config.ts
  7. 89 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/copywriting.tsx
  8. 150 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/headNickJump.tsx
  9. 104 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/index.less
  10. 1011 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/index.tsx
  11. 313 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/material.tsx
  12. 152 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/tableConfig.tsx
  13. 49 3
      src/pages/launchSystemNew/launchManage/createAd/index.less
  14. 321 101
      src/pages/launchSystemNew/launchManage/createAd/index.tsx
  15. 5 1
      src/pages/launchSystemNew/launchManage/createAd/tableConfig.tsx
  16. 10 3
      src/pages/launchSystemNew/launchManage/createAd/targeting/index.tsx
  17. 6 1
      src/pages/launchSystemNew/launchManage/taskList/index.tsx
  18. 3 2
      src/pages/launchSystemNew/launchManage/taskList/tableConfig.tsx
  19. 4 0
      src/services/launchAdq/createAd.ts
  20. 5 1
      src/services/launchAdq/taskList.ts

+ 10 - 2
src/pages/launchSystemNew/components/goodsModal/index.tsx

@@ -75,6 +75,13 @@ const GoodsModal: React.FC<Props> = (props) => {
         setData([...newData])
     }
 
+    // 清空已选
+    const clearGoods = () => {
+        let newData = JSON.parse(JSON.stringify(data))
+        newData[selectAdz - 1]['productList'] = []
+        setData([...newData])
+    }
+
     return <Modal
         title={<Space>
             <span>商品库</span>
@@ -97,8 +104,9 @@ const GoodsModal: React.FC<Props> = (props) => {
                     </div>))}
             </div>
             <div className={style.right}>
-                <Space style={{ marginBottom: 10 }} align="end">
-                    <Button icon={<SyncOutlined />} type='link' loading={getGoods?.loading} onClick={() => { getList([data[selectAdz - 1].adAccountId]) }}></Button>
+                <Space style={{ marginBottom: 10 }} align="end" size={2}>
+                    <Button icon={<SyncOutlined />} type='link' loading={getGoods?.loading} onClick={() => { getList([data[selectAdz - 1].adAccountId]) }}>刷新</Button>
+                    {data[selectAdz - 1]?.productList?.length > 0 && <Button type='link' onClick={() => { clearGoods() }}>清空</Button>}
                 </Space>
                 <Tables
                     columns={columns()}

+ 1 - 1
src/pages/launchSystemNew/components/textAideInput/index.tsx

@@ -58,7 +58,7 @@ const TextAideInput: React.FC<Props> = (props) => {
                         textList(value)
                     }}
                     onBlur={() => {
-                        // setTimeout(() => { setDescriptionshow(false) }, 100)
+                        setTimeout(() => { setDescriptionshow(false) }, 500)
                     }}
                     onChange={(e) => {
                         let value = e.target.value

+ 2 - 2
src/pages/launchSystemNew/launchManage/createAd/creative/index.tsx

@@ -1,5 +1,5 @@
 import React, { useCallback, useState } from 'react'
-import { Button, Col, Space, Spin, Tabs, Tooltip, } from 'antd'
+import { Col, Space, Spin, Tabs, Tooltip, } from 'antd'
 import style from '../index.less'
 import CreativeModal from "../../../components/creativeModal"
 import { CreateAdProps } from '@/services/launchAdq/createAd'
@@ -19,7 +19,7 @@ type Props = {
     set_targetKey: (key: string) => void
 }
 function Creative(props: Props) {
-    let { queryForm, getSysAdgroups, getSysAdcreative, setQueryForm, clearData, targetKey, set_targetKey, page_checked } = props
+    let { queryForm, getSysAdgroups, getSysAdcreative, setQueryForm, clearData, targetKey, set_targetKey } = props
     const [adModalConfig, setAdModalConfig] = useState<ModalConfig>({//新建广告弹窗
         visible: false,
         type: 'add'

+ 166 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/index.tsx

@@ -0,0 +1,166 @@
+import { CreateAdProps } from "@/services/launchAdq/createAd"
+import { BaseResult } from "@ahooksjs/use-request/lib/types"
+import { Col, Space, Spin, Tooltip } from "antd"
+import React, { useCallback, useState } from "react"
+import { ModalConfig } from "../ad"
+import style from '../index.less'
+import AdcreativeCol from "../adcreativeCol"
+import CreativePup from "./modal"
+import Material from "./modal/material"
+import Copywriting from "./modal/copywriting"
+
+
+type Props = {
+    queryForm: Partial<CreateAdProps>,
+    setQueryForm: React.Dispatch<React.SetStateAction<Partial<CreateAdProps>>>,
+    getSysAdcreative: BaseResult<any, any>,
+    clearData: () => void,
+    targetKey: string,
+}
+const CreativeCL: React.FC<Props> = (props) => {
+
+    /********************************/
+    const { queryForm, getSysAdcreative, setQueryForm, clearData, targetKey } = props
+
+    const [adModalConfig, setAdModalConfig] = useState<ModalConfig>({//新建广告弹窗
+        visible: false,
+        type: 'add'
+    })
+    /********************************/
+
+    // 设置变量
+    const handleAdModalConfig = useCallback((arg: ModalConfig) => {
+        setAdModalConfig({ ...adModalConfig, ...arg })
+    }, [adModalConfig])
+
+    return <>
+        <Col className={style.conRightBorder}>
+            <div className={style.top}>创意基本信息</div>
+            <div className={style.center}>
+                {
+                    queryForm?.taskMediaMaps?.filter((item, index) => index === 0)?.map((item, index) => {
+                        return <Spin spinning={getSysAdcreative.loading} key={index}>
+                            <div className={style.centerContent}>
+                                {item.sysAdcreative && <AdcreativeCol data={item.sysAdcreative} />}
+                            </div>
+                        </Spin>
+                    })
+                }
+            </div>
+            <div className={style.bottom}>
+                <Space size={20}>
+                    {queryForm?.sysAdgroup ? <>
+                        <span onClick={() => { handleAdModalConfig(queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative ? { visible: true, type: 'edit' } : { visible: true, type: 'add' }) }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative ? '编辑' : '添加'}</span>
+                    </> : <Tooltip title="请先设置广告">
+                        <span>添加</span>
+                    </Tooltip>}
+                </Space>
+            </div>
+
+            {/* 创建创意 */}
+            {adModalConfig.visible && <CreativePup visible={adModalConfig.visible} type={adModalConfig.type} PupFn={handleAdModalConfig} callback={(values: any, material: any, textData: any[]) => {
+                let arr = queryForm.taskMediaMaps || []
+                let adqPageArr: any = queryForm.adqPageList || []
+                let pageArr: any = queryForm.pageList || []
+                adqPageArr[targetKey as string] = null//清除adq落地页
+                pageArr[targetKey as string] = null//清除本地落地页
+                arr[targetKey] = { sysAdcreative: values }
+                setQueryForm({ ...queryForm, taskMediaMaps: arr, adqPageList: adqPageArr, pageList: pageArr, materialData: material, materials: [], textData, texts: [] }); clearData();
+                handleAdModalConfig({ visible: false, dataInfo: null, type: 'add' })
+            }} dataInfo={queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative} queryForm={queryForm} />}
+        </Col>
+        <Col className={style.conRightBorder}>
+            <div className={style.top}>
+                <strong style={{ fontSize: 15 }}>创意素材</strong>
+                <span>已选:{queryForm?.materials?.length || 0}</span>
+            </div>
+            <div className={style.center}>
+                <div className={queryForm?.materialData && queryForm?.materialData[0]?.name === 'image_list' && queryForm?.materialData[0]?.arrayProperty?.minNumber > 1 ? style.imgListGroup : style.otherGroup}>
+                    {queryForm?.materials?.map((item: any, index: number) => {
+                        let keys = Object.keys(item)
+                        if (keys?.includes('imageUrlList')) {
+                            return <div key={index} className={style.group}>
+                                {item?.imageUrlList?.map((url: string, index: number) => <div key={index}><img src={url} /></div>)}
+                            </div>
+                        } else {
+                            if (keys?.length > 1) {
+                                return <div key={index} className={style.otherGroup}>
+                                    {keys?.includes('imageUrl') && <div className={style.group}><img src={item.imageUrl} /></div>}
+                                    {keys?.includes('videoUrl') && <div className={style.group}><video src={item.videoUrl} /></div>}
+                                    {keys?.includes('shortVideo1Url') && <div className={style.group}><video src={item.shortVideo1Url} /></div>}
+                                </div>
+                            } else {
+                                if (keys?.includes('imageUrl')) {
+                                    return <div key={index} className={style.group}><img src={item.imageUrl} /></div>
+                                } else if (keys?.includes('videoUrl')) {
+                                    return <div key={index} className={style.group}><video src={item.videoUrl} /></div>
+                                } else if (keys?.includes('shortVideo1Url')) {
+                                    return <div key={index} className={style.group}><video src={item.shortVideo1Url} /></div>
+                                }
+                            }
+                            return null
+                        }
+                    })}
+                </div>
+            </div>
+            <div className={style.bottom}>
+                <Space size={20}>
+                    {queryForm?.taskMediaMaps && queryForm?.taskMediaMaps?.length > 0 ? <>
+                        <Material
+                            onChange={(data) => {
+                                setQueryForm({ ...queryForm, materials: data })
+                            }}
+                            material={queryForm?.materialData}
+                            value={queryForm?.materials}
+                            sysAdcreative={queryForm?.taskMediaMaps[0]?.sysAdcreative}
+                        />
+                    </> : <Tooltip title="请先设置创意">
+                        <span>添加</span>
+                    </Tooltip>}
+                </Space>
+            </div>
+        </Col>
+        <Col className={style.conRightBorder} style={{ maxWidth: '25%' }}>
+            <div className={style.top}>
+                <strong style={{ fontSize: 15 }}>创意文案</strong>
+                <span>已选:{queryForm?.texts?.length || 0}</span>
+            </div>
+            <div className={style.center}>
+                {queryForm?.texts?.some((item: any) => item?.title) && <div className={style.accName} style={{ fontWeight: 800 }}>标题</div>}
+                {queryForm?.texts?.map((item: any, index: number) => {
+                    if (item?.title) {
+                        return <div className={style.accCon} key={index}><span className={style.title}>{item?.title}</span> </div>
+                    } else {
+                        return null
+                    }
+                })}
+                {queryForm?.texts?.some((item: any) => item?.description) && <div className={style.accName} style={{ fontWeight: 800 }}>广告详情</div>}
+                {queryForm?.texts?.map((item: any, index: number) => {
+                    if (item?.description) {
+                        return <div className={style.accCon} key={index}><span className={style.title}>{item?.description}</span></div>
+                    } else {
+                        return null
+                    }
+                })}
+            </div>
+            <div className={style.bottom}>
+                <Space size={20}>
+                    {queryForm?.taskMediaMaps && queryForm?.taskMediaMaps?.length > 0 ? <>
+                        <Copywriting
+                            textData={queryForm.textData}
+                            sysAdcreative={queryForm?.taskMediaMaps[0]?.sysAdcreative}
+                            value={queryForm?.texts}
+                            onChange={(data) => {
+                                setQueryForm({ ...queryForm, texts: data })
+                            }}
+                        />
+                    </> : <Tooltip title="请先设置创意">
+                        <span>添加</span>
+                    </Tooltip>}
+                </Space>
+            </div>
+        </Col>
+    </>
+}
+
+export default React.memo(CreativeCL)

+ 196 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/brandImage.tsx

@@ -0,0 +1,196 @@
+import Tables from "@/components/Tables"
+import { useAjax } from "@/Hook/useAjax"
+import SelectCloud from "@/pages/launchSystemNew/components/selectCloud"
+import { addSysBrandApi, delSysBrandApi, editSysBrandApi, getSysBrandApi } from "@/services/launchAdq/global"
+import { PlusOutlined } from "@ant-design/icons"
+import { Button, Divider, Form, Input, message, Modal, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import { useModel } from "umi"
+import { brandColumns } from "./tableConfig"
+
+interface Props {
+    onChange?: (data: any) => void,
+    value?: any
+}
+
+/**
+ * 品牌形象
+ * @returns 
+ */
+const BrandImage: React.FC<Props> = (props) => {
+
+    /****************************/
+    const { onChange, value } = props
+
+    const [visible, setVisible] = useState<boolean>(false)
+    const [addVisible, setAddVisible] = useState<boolean>(false)
+    const [form] = Form.useForm()
+    const [initialValues, setInitialValues] = useState<any>({})
+
+    const getSysBrand = useAjax(() => getSysBrandApi())
+    const addSysBrand = useAjax((params) => addSysBrandApi(params))
+    const editSysBrand = useAjax((params) => editSysBrandApi(params))
+    const delSysBrand = useAjax((params) => delSysBrandApi(params))
+    /****************************/
+
+    // 获取列表
+    useEffect(() => {
+        getSysBrand.run()
+    }, [])
+
+
+    // 新增修改
+    const handleOk = async () => {
+        form.submit()
+        let data = await form.validateFields()
+        if (Object.keys(initialValues).length > 0) { // 修改
+            editSysBrand.run({ ...data, sysBrandId: initialValues.id }).then(res => {
+                if (res) {
+                    message.success('修改成功')
+                    setAddVisible(false)
+                    getSysBrand.refresh()
+                }
+            })
+        } else { // 新增
+            addSysBrand.run(data).then(res => {
+                if (res) {
+                    message.success('新增成功')
+                    setAddVisible(false)
+                    getSysBrand.refresh()
+                }
+            })
+        }
+        setInitialValues({})
+    }
+
+    /** 删除 */
+    const del = (id: number) => {
+        delSysBrand.run({ sysBrandId: id }).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getSysBrand.refresh()
+            }
+        })
+    }
+
+    /** 修改 */
+    const edit = (data: any) => {
+        setInitialValues(data)
+        setAddVisible(true)
+    }
+
+    return <div>
+        <Select
+            showSearch
+            placeholder="请选择一个品牌跳转页,与广告创意一起展示"
+            optionFilterProp="children"
+            style={{ width: 400 }}
+            onChange={(e) => { onChange && onChange(e) }}
+            allowClear
+            value={value}
+            filterOption={(input, option) => {
+                return (option!.value as unknown as string).toLowerCase().includes(input.toLowerCase())
+            }}
+            dropdownRender={menu => <>
+                {menu}
+                <Divider style={{ margin: '8px 0' }} />
+                <div>
+                    <Button type="link" onClick={() => { setAddVisible(true); setInitialValues({}) }}>新增</Button>
+                    <Button type="link" onClick={() => setVisible(true)}>前往管理</Button>
+                </div>
+            </>}
+        >
+            {
+                getSysBrand?.data?.map((item: any) => {
+                    return <Select.Option value={item.name + '_' + item.brandImgUrl} key={item.id}>
+                        <Space>
+                            <img src={item.brandImgUrl} style={{ width: 20 }} />
+                            <span>{item.name}</span>
+                        </Space>
+                    </Select.Option>
+                })
+            }
+        </Select>
+
+        {visible && <Modal title="品牌形象" width={1000} visible={visible} footer={null} onCancel={() => setVisible(false)}>
+            <Space direction='vertical' style={{ width: '100%' }}>
+                <Button type="primary" icon={<PlusOutlined />} onClick={() => { setAddVisible(true); setInitialValues({}) }}>上传品牌形象</Button>
+                <Tables
+                    columns={brandColumns(del, edit)}
+                    dataSource={getSysBrand?.data}
+                    size="small"
+                    loading={getSysBrand?.loading}
+                    scroll={{ y: 300 }}
+                    bordered
+                />
+            </Space>
+        </Modal>}
+
+        {addVisible && <Modal title="上传品牌形象" visible={addVisible} confirmLoading={addSysBrand.loading} onOk={handleOk} onCancel={() => setAddVisible(false)}>
+            <Form
+                name="basic"
+                form={form}
+                layout='vertical'
+                autoComplete="off"
+                initialValues={{ ...initialValues }}
+            >
+                <Form.Item label={<strong>头像</strong>} name="brandImgUrl" rules={[{ required: true, message: '请选择头像!' }]}>
+                    <UploadImage />
+                </Form.Item>
+                <Form.Item label={<strong>名称</strong>} name="name" rules={[{ required: true, message: '请输入名称!' }]}>
+                    <Input placeholder="请输入名称" maxLength={12}/>
+                </Form.Item>
+            </Form>
+        </Modal>}
+    </div>
+}
+
+
+interface ImageProps {
+    onChange?: (value: string) => void,
+    value?: string
+}
+/**
+ * 处理选择图片Form
+ * @returns 
+ */
+export const UploadImage: React.FC<ImageProps> = (props) => {
+
+    /*********************/
+    const { onChange, value } = props
+    const [selectImgVisible, setSelectImgVisible] = useState<boolean>(false)
+    const [sliderImgContent, setSliderImgContent] = useState<{ url: string, width?: number, height?: number }[]>([])  // 保存回填数据
+    const { init } = useModel('useLaunchAdq.useBdMediaPup')
+    /*********************/
+
+    useEffect(() => {
+        if (value) {
+            setSliderImgContent([{ url: value }])
+        } else {
+            setSliderImgContent([])
+        }
+    }, [value])
+
+    const setImg = (content: any[]) => {
+        onChange && onChange(content[0]?.url)
+        setSelectImgVisible(false)
+    }
+
+    const selectImg = () => {
+        init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: 512, height: 512 }]], maxSize: 50 * 1024 })
+        setTimeout(() => { setSelectImgVisible(true) }, 50)
+    }
+
+    return <>
+        {value ? <img src={value} onClick={selectImg} width={100} height={100} /> : <Button onClick={selectImg}>选择图片</Button>}
+        <div style={{ fontSize: 12, color: 'rgba(0,0,0,.5)' }}>
+            <div>图片尺寸:512×512 像素</div>
+            <div>图片格式:大小要求在 50KB 以内,仅支持 jpg 和 png 格式</div>
+        </div>
+
+        {/* 选择素材 */}
+        {selectImgVisible && <SelectCloud visible={selectImgVisible} sliderImgContent={sliderImgContent} onClose={() => setSelectImgVisible(false)} onChange={setImg} />}
+    </>
+}
+
+export default React.memo(BrandImage)

+ 80 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/config.ts

@@ -0,0 +1,80 @@
+/**使用外层创意素材替换原生推广页顶部素材*/
+export const overrideCanvasHeadOptionEnum = {
+  OPTION_KEEP_DIFFERENT: '自定义广告创意素材,和原生推广页顶部素材保持两者不同',
+  OPTION_CANVAS_OVERRIDE_CREATIVE: '使用原生推广页顶部素材作为外层创意素材',
+  OPTION_CREATIVE_OVERRIDE_CANVAS: '使用外层创意素材替换原生推广页顶部素材',
+};
+/**使用外层创意素材替换原生推广页顶部素材*/
+export const creativeConfig = {
+  311: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  641: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  642: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  643: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  618: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  711: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  712: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  720: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  721: {
+    //保持一致,替换
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  1707: {
+    //卡片横版大图
+    overrideCanvasHeadOption: [
+      'OPTION_KEEP_DIFFERENT',
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  1708: {
+    //卡片横版视频
+    overrideCanvasHeadOption: [
+      'OPTION_KEEP_DIFFERENT',
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+};

+ 89 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/copywriting.tsx

@@ -0,0 +1,89 @@
+import TextAideInput from "@/pages/launchSystemNew/components/textAideInput"
+import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"
+import { Button, Collapse, Form, Modal } from "antd"
+import React, { useEffect, useState } from "react"
+import styles from './index.less'
+
+
+
+interface Props {
+    value?: any[]
+    textData?: any[],
+    sysAdcreative: any
+    onChange?: (data: any) => void
+}
+
+/**
+ * 文案设置
+ * @returns 
+ */
+const Copywriting: React.FC<Props> = (props) => {
+
+    /******************************/
+    const { onChange, textData = [], sysAdcreative, value } = props
+    const { adcreativeTemplateId } = sysAdcreative
+    const [visible, setVisible] = useState<boolean>(false)
+    const [form] = Form.useForm();
+    /******************************/
+
+    // 回填
+    useEffect(() => {
+        if (visible) {
+            if (value && value?.length > 0) {
+                form.setFieldsValue({ texts: value })
+            } else {
+                form.setFieldsValue({ texts: [undefined] })
+            }
+        }
+    }, [value, visible])
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            console.log('values=>1', values)
+            onChange && onChange(values?.texts)
+            setVisible(false)
+        })
+    }
+
+    return <>
+        <span onClick={() => { setVisible(true) }}>{value && value?.length > 0 ? '编辑' : '添加'}</span>
+        {visible && <Modal visible={visible} onCancel={() => setVisible(false)} title="创意文案" width={700} onOk={handleOk}>
+            <Form name="dynamic_form_item" form={form} labelAlign='left' >
+                <Form.List name="texts">
+                    {(fields, { add, remove }) => (<>
+                        <Collapse activeKey={fields.map(field => field.name)} bordered={false}>
+                            {fields.map((field, num) => (<Collapse.Panel showArrow={false} header={`文案组${num + 1}`} key={(field.name).toString()} extra={fields?.length > 1 && <MinusCircleOutlined className={styles.clear} onClick={() => remove(field.name)} style={{ color: 'red' }} />}>
+                                {textData?.map((item, index) => {
+                                    if (item.name === 'title') {
+                                        return <div key={'title' + item.fieldType}>
+                                            <Form.Item {...field} label={<strong>{item.description}(选填)</strong>} name={[field.name, item.name]} rules={[{ pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${item.restriction.textRestriction.maxLength}}`)), message: '请输入正确的' + item.description }]}>
+                                                <TextAideInput placeholder={'请输入' + item.description} style={{ width: 450 }} maxTextLength={item.restriction.textRestriction.maxLength} />
+                                            </Form.Item>
+                                        </div>
+                                    }
+                                    if (item.name === 'description') {
+                                        let maxNum = adcreativeTemplateId === 1708 || adcreativeTemplateId === 1707 ? item.pupState.xd_show ? 10 : item.restriction.textRestriction.maxLength : item.restriction.textRestriction.maxLength
+                                        return <div key={'description' + item.fieldType}>
+                                            <Form.Item {...field} label={<strong>{item.description}</strong>} name={[field.name, item.name]} rules={[{ required: true, pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${maxNum}}`)), message: '请输入正确的' + item.description }]}>
+                                                <TextAideInput placeholder={'请输入' + item.description} style={{ width: 450 }} maxTextLength={maxNum} />
+                                            </Form.Item>
+                                        </div>
+                                    }
+                                    return null
+                                })}
+                            </Collapse.Panel>))}
+                        </Collapse>
+                        <Form.Item>
+                            <Button type="link" onClick={() => add()} icon={<PlusOutlined />} style={{ padding: 0 }}>
+                                新增文案组
+                            </Button>
+                        </Form.Item>
+                    </>)}
+                </Form.List>
+            </Form>
+        </Modal>}
+    </>
+}
+
+
+export default React.memo(Copywriting)

+ 150 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/headNickJump.tsx

@@ -0,0 +1,150 @@
+import Tables from "@/components/Tables"
+import { useAjax } from "@/Hook/useAjax"
+import { addSysProfileApi, delSysProfileApi, editSysProfileApi, getSysProfileApi } from "@/services/launchAdq/global"
+import { PlusOutlined } from "@ant-design/icons"
+import { Button, Divider, Form, Input, message, Modal, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import { UploadImage } from "./brandImage"
+import { profileColumns } from "./tableConfig"
+
+interface Props {
+    onChange?: (data: any) => void,
+    value?: any
+}
+
+/**
+ * 头像及昵称跳转页
+ * @returns 
+ */
+const HeadNickJump: React.FC<Props> = (props) => {
+
+    /****************************/
+    const { onChange, value } = props
+
+    const [visible, setVisible] = useState<boolean>(false)
+    const [addVisible, setAddVisible] = useState<boolean>(false)
+    const [form] = Form.useForm()
+    const [initialValues, setInitialValues] = useState<any>({})
+
+    const getSysProfile = useAjax(() => getSysProfileApi())
+    const addSysProfile = useAjax((params) => addSysProfileApi(params))
+    const editSysProfile = useAjax((params) => editSysProfileApi(params))
+    const delSysProfile = useAjax((params) => delSysProfileApi(params))
+    /****************************/
+
+    // 获取列表
+    useEffect(() => {
+        getSysProfile.run()
+    }, [])
+
+
+    // 新增修改
+    const handleOk = async () => {
+        form.submit()
+        let data = await form.validateFields()
+        if (Object.keys(initialValues).length > 0) { // 修改
+            editSysProfile.run({ ...data, sysProfileId: initialValues.id }).then(res => {
+                if (res) {
+                    message.success('修改成功')
+                    setAddVisible(false)
+                    getSysProfile.refresh()
+                }
+            })
+        } else { // 新增
+            addSysProfile.run(data).then(res => {
+                if (res) {
+                    message.success('新增成功')
+                    setAddVisible(false)
+                    getSysProfile.refresh()
+                }
+            })
+        }
+        setInitialValues({})
+        form.resetFields()
+    }
+
+    /** 删除 */
+    const del = (id: number) => {
+        delSysProfile.run({ sysProfileId: id }).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getSysProfile.refresh()
+            }
+        })
+    }
+
+    /** 修改 */
+    const edit = (data: any) => {
+        setInitialValues(data)
+        setAddVisible(true)
+    }
+
+    return <div>
+        <Select
+            showSearch
+            placeholder="请选择一个品牌跳转页,与广告创意一起展示"
+            optionFilterProp="children"
+            style={{ width: 400 }}
+            onChange={(e) => { onChange && onChange(e) }}
+            allowClear
+            value={value}
+            filterOption={(input, option) => {
+                return (option!.value as unknown as string).toLowerCase().includes(input.toLowerCase())
+            }}
+            dropdownRender={menu => <>
+                {menu}
+                <Divider style={{ margin: '8px 0' }} />
+                <div>
+                    <Button type="link" onClick={() => { setAddVisible(true); setInitialValues({}) }}>新增</Button>
+                    <Button type="link" onClick={() => setVisible(true)}>前往管理</Button>
+                </div>
+            </>}
+        >
+            {
+                getSysProfile?.data?.map((item: any) => {
+                    return <Select.Option value={item.profileName + '_' + item.headImageUrl + '_' + item.description} key={item.id}>
+                        <Space>
+                            <img src={item.headImageUrl} style={{ width: 20 }} />
+                            <span>{item.profileName}</span>
+                        </Space>
+                    </Select.Option>
+                })
+            }
+        </Select>
+
+        {visible && <Modal title="头像及昵称跳转页" width={1000} visible={visible} footer={null} onCancel={() => setVisible(false)}>
+            <Space direction='vertical' style={{ width: '100%' }}>
+                <Button type="primary" icon={<PlusOutlined />} onClick={() => { setAddVisible(true); setInitialValues({}) }}>上传品牌形象</Button>
+                <Tables
+                    columns={profileColumns(del, edit)}
+                    dataSource={getSysProfile?.data}
+                    size="small"
+                    loading={getSysProfile?.loading}
+                    scroll={{ y: 300 }}
+                    bordered
+                />
+            </Space>
+        </Modal>}
+        {addVisible && <Modal title={`${Object.keys(initialValues).length > 0 ? '修改' : '上传'}头像及昵称跳转页`} visible={addVisible} confirmLoading={addSysProfile.loading || editSysProfile.loading} onOk={handleOk} onCancel={() => setAddVisible(false)}>
+            <Form
+                name="basic"
+                form={form}
+                layout='vertical'
+                autoComplete="off"
+                initialValues={{ ...initialValues }}
+            >
+                <Form.Item label={<strong>头像</strong>} name="headImageUrl" rules={[{ required: true, message: '请选择头像!' }]}>
+                    <UploadImage />
+                </Form.Item>
+                <Form.Item label={<strong>名称</strong>} name="profileName" rules={[{ required: true, message: '请输入名称!' }]}>
+                    <Input placeholder="请输入名称" maxLength={12} />
+                </Form.Item>
+                <Form.Item label={<strong>详细描述</strong>} name="description" rules={[{ required: true, message: '请输入详细描述!' }]}>
+                    <Input.TextArea placeholder="请输入详细描述" maxLength={120} />
+                </Form.Item>
+            </Form>
+        </Modal>}
+    </div>
+}
+
+export default React.memo(HeadNickJump)

+ 104 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/index.less

@@ -0,0 +1,104 @@
+.adcreative_template{
+    width: 100%;
+    overflow-y: auto;
+    display: flex;
+    height: 173px;
+    >label{
+        height: 100%;
+        margin-right: 15px;
+    }
+}
+.videoImgs{
+    width: 100%;
+    overflow-y: auto;
+    display: flex;
+    img{
+        width: 100%;
+    }
+    label{
+        height: 100%;
+        padding: 0;
+        width: 32%;
+        margin-right: 1%;
+    }
+}
+.adcreative_template_item{
+    width: 150px;
+    height: 160px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-flow: column;
+}
+
+.video{
+  
+}
+.box {
+    width: 60%;
+    height: 200px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #f5f7fa;
+    flex-direction: column;
+    color: rgba(0,0,0,.3);
+    border-radius: 5px;
+    >p{
+        display: flex;
+        align-items: center;
+        flex-flow: column;
+        font-size: 10px;
+        cursor: pointer;
+        max-height: 150px;
+        margin-bottom:0;
+        img{
+            height: 100%;
+        }
+        video{
+            height: 100%;
+        }
+    }
+}
+
+.image_list{
+    flex-flow: row wrap;
+    background-color: transparent;
+    height:auto;
+    justify-content: flex-start;
+    >p{
+        width: 150px;
+        background-color: #f5f7fa;
+        height: 150px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        border: 1px solid #e6e8ed;
+        margin: 0;
+    }
+}
+.imageMater {
+    width: 300px;
+    height: 160px;
+}
+.crt{
+    display: inline-flex;
+    align-items: center;
+    width: auto;
+    margin-left: 8px;
+    padding: 1px 4px;
+    height: 16px;
+    border-radius: 3px;
+    font-size: 12px;
+    color: #fff;
+    border: 1px solid #296bef;
+    background-color: #296bef;
+    line-height: normal;
+}
+
+.space {
+    width: 100%;
+    .clear {
+        width: 20px;
+    }
+}

+ 1011 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/index.tsx

@@ -0,0 +1,1011 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import { Modal, Form, Input, Divider, Select, Radio, Switch, Spin, List, Checkbox, Space, Button, message, Image, Empty } from 'antd'
+import styles from './index.less'
+import { useAjax } from '@/Hook/useAjax'
+import { getText, get_adcreative_template, get_adcreative_template_list, get_tools_video_capture } from '@/services/launchAdq/global'
+import { AdcreativeTemplate, AdcreativeTemplateList } from '@/services/launchAdq'
+import { mySet } from '@/utils/arrFn'
+import SelectCloud from '@/pages/launchSystemNew/components/selectCloud'
+import { useModel } from 'umi'
+import { ModalConfig } from '../../ad';
+import { outAdcreativeTemplateIdFun } from '../../../localAd/adenum'
+import { CreateAdProps } from '@/services/launchAdq/createAd'
+import { createSysAdcreative } from '@/services/launchAdq/creative'
+import { creativeConfig, overrideCanvasHeadOptionEnum } from './config'
+import BrandImage from './brandImage'
+import HeadNickJump from './headNickJump'
+import moment from 'moment'
+interface Props {
+    queryForm: Partial<CreateAdProps>,
+    title?: string,
+    visible: boolean,
+    PupFn: (arg: ModalConfig) => void,
+    callback: (params: any, material: { label: string, name: string, restriction: any }[], textData: any[]) => void,
+    confirmLoading?: boolean,
+    type?: 'add' | 'look' | 'edit',//新增,查看,编辑
+    dataInfo?: any
+}
+
+/**创意组模板*/
+function CreativePup(props: Props) {
+
+    /***************************************/
+    let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm } = props
+    const { currentUser }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
+    let [template_checked, settemplate_checked] = useState<boolean>(dataInfo?.isTemplate || false)
+    let { promotedObjectType, sysAdgroup } = queryForm
+    let { siteSet } = sysAdgroup
+    const { init } = useModel('useLaunchAdq.useBdMediaPup')
+    let arg = type === 'look' ? { footer: null } : {}
+    // 请求
+    const getAdcreativeTemplate = useAjax((params) => get_adcreative_template(params))
+    const getAdcreativeTemplateList = useAjax((params) => get_adcreative_template_list(params))
+    const getTextLsit = useAjax((params) => getText(params))
+    const addSysAdgroup = useAjax((params) => createSysAdcreative(params))
+    const getVideoCapture = useAjax((params) => get_tools_video_capture(params))
+    // 变量
+    const [adcreative_template, set_adcreative_template] = useState<AdcreativeTemplate>()
+    const [adcreative_template_list, set_adcreative_template_list] = useState<AdcreativeTemplateList[]>([])
+    const [selectImgVisible, set_selectImgVisible] = useState(false)
+    const [selectVideoVisible, set_selectVideoVisible] = useState(false)
+    const [videoImgsVisbile, set_videoImgsVisbile] = useState(false)
+    const [descriptionShow, setdescriptionshow] = useState(false)
+    const [endPageDescShow, setendPageDescnshow] = useState(false)
+    const [materialData, setMaterialData] = useState<{ label: string, name: string, restriction: any }[]>([])
+    const [textData, setTextData] = useState<any[]>([])
+    const [isShowSc, set_isShowSc] = useState(false)//是否展示素材选项
+    const [infoSet, set_infoSet] = useState(false)//回填设置已完成
+    const [videoImgs, set_videoImgs] = useState<{//视频封面图设置
+        activeUrl: string,//选中的视频封面图地址
+        preview: boolean,//是否开启图片点击预览
+        urlList: any[],//生成的视频封面列表
+    }>({
+        activeUrl: '',
+        preview: false,
+        urlList: [
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/21D8D51AD98C4FF8BF41F1C2D28EA39F.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/80DBE1AB3EDE4E85ABAE5F1670D9FED0.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/BCB2DAB86BDB4549BCB8E493C4F29E82.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/545A4C2A5B874C82A9D1C0C063624AE5.jpg'
+        ]
+    })
+    const [titleShow, settitleshow] = useState(false)
+    const [form] = Form.useForm();
+    const [pupState, setPupState] = useState({
+        kp_show: false,
+        xd_show: false,
+        sj_show: false,
+        bq_show: false,
+        sp_show: false
+    })
+    const [imgMaterialConfig, setImgMaterialConfig] = useState<{
+        adcreativeTemplateId?: number,
+        type: string,
+        cloudSize: { relation: string, width: number, height: number }[],
+        list: any[],
+        max: number
+    }>({
+        type: '',//类型
+        cloudSize: [],//素材搜索条件
+        list: [],//素材
+        max: 1,//素材数量
+    })//图片素材配置
+    const [videoMaterialConfig, setVideoMaterialConfig] = useState<{
+        adcreativeTemplateId?: number,
+        type: string,
+        cloudSize: { relation: string, width: number, height: number }[],
+        list: any[],
+        max: number
+    }>({
+        type: '',//类型
+        cloudSize: [],//素材搜索条件
+        list: [],//素材
+        max: 1,//素材数量
+    })//图片素材配置
+    const [conversionList, setConversionList] = useState<any>(null)
+    let pageType = Form.useWatch('pageType', form)
+    let adcreativeTemplateId = Form.useWatch('adcreativeTemplateId', form)
+    let actionBtn = Form.useWatch('actionBtn', form)
+    // let siteSet = Form.useWatch('siteSet', form)
+    let overrideCanvasHeadOption = Form.useWatch('overrideCanvasHeadOption', form)
+    let adcreativeElementsType = Form.useWatch('adcreativeElementsType', form)
+    let dataShow = Form.useWatch('dataShow', form)
+    let conversionDataType = Form.useWatch('conversionDataType', form)
+    let titles = Form.useWatch('title', form)
+    let description = Form.useWatch('description', form)
+    let videoOver = Form.useWatch('videoOver', form)
+    let endPageDesc = Form.useWatch('endPageDesc', form)
+    let linkPageType = Form.useWatch('linkPageType', form)
+    /***************************************/
+    
+    // 确定事件
+    const handleOk = useCallback(() => {
+        form.validateFields().then(values => {
+            console.log('values=>1', values)
+            let newValues = JSON.parse(JSON.stringify(values))
+            for (let key in newValues) {
+                switch (key) {
+                    case 'image'://图素材
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements,
+                            imageUrl: imgMaterialConfig?.list[0]?.url,
+                        }
+                        delete newValues[key]
+                        break;
+                    case 'video'://视频素材
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements,
+                            videoUrl: videoMaterialConfig?.list[0]?.url,
+                        }
+                        delete newValues[key]
+                        break;
+                    case 'image_list'://图素材
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements,
+                            imageUrlList: imgMaterialConfig.list?.map(item => item.url),
+                            description: newValues.description,
+                        }
+                        delete newValues[key]
+                        break;
+                    case 'short_video1'://视频素材
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements,
+                            shortVideoStruct: {
+                                shortVideo1Url: videoMaterialConfig?.list[0]?.url
+                            },
+                            description: newValues.description,
+                        }
+                        delete newValues[key]
+                        break;
+                    case 'description'://文案
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, description: newValues.description }
+                        break;
+                    case 'title'://文案
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, title: newValues.title }
+                        break;
+                    case 'endPageType'://视频结束l类型
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, endPage: { ...newValues.adcreativeElements.endPage, endPageType: newValues.endPageType } }
+                        delete newValues[key]
+                        break;
+                    case 'endPageDesc'://视频结束文案
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, endPage: { ...newValues.adcreativeElements.endPage, endPageDesc: newValues.endPageDesc } }
+                        delete newValues[key]
+                        break;
+                    case 'buttonText'://特殊行动按钮
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, buttonText: newValues.buttonText }
+                        delete newValues[key]
+                        break;
+                    case 'brand'://品牌形象
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements, brand: {
+                                brandName: newValues.brand.split('_')[0],
+                                brandImgUrl: newValues.brand.split('_')[1]
+                            }
+                        }
+                        break;
+                    case 'profile':
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements, brand: {
+                                brandName: newValues.profile.split('_')[0],
+                                brandImgUrl: newValues.profile.split('_')[1]
+                            }
+                        }
+                        newValues.profile = {
+                            headImageUrl: newValues.profile.split('_')[1],
+                            profileName: newValues.profile.split('_')[0],
+                            description: newValues.profile.split('_')[2]
+                        }
+                        break
+                    case 'pageUrl'://跳转落地页
+                        newValues.linkPageSpec = {
+                            ...newValues.linkPageSpec,
+                            pageUrl: newValues.pageUrl
+                        }
+                        delete newValues.pageUrl
+                        break;
+                    case 'miniProgramId':
+                        newValues.linkPageSpec = {
+                            ...newValues.linkPageSpec,
+                            miniProgramSpec: {
+                                miniProgramId: newValues.miniProgramId,
+                                miniProgramPath: newValues.miniProgramPath
+                            }
+                        }
+                        delete newValues.miniProgramId
+                        delete newValues.miniProgramPath
+                        break;
+                }
+            }
+            if (!newValues.adcreativeElements) {
+                newValues.adcreativeElements = {}
+            }
+            //假如不存在promotedObjectType
+            if (!newValues?.promotedObjectType) {
+                newValues['promotedObjectType'] = queryForm.promotedObjectType
+            }
+            // 假如不存在siteSet
+            if (!newValues?.siteSet) {
+                newValues['siteSet'] = queryForm.sysAdgroup.siteSet
+            }
+            delete newValues.description //删除外层文案
+            delete newValues.title //删除外层文案
+            delete newValues.adcreativeElementsType //删除创意形式
+            delete newValues.dataShow //删除数据开关
+            delete newValues.actionBtn //删除行动开关
+            delete newValues.brand //品牌形象
+            // 假如使用了落地页顶部素材替换外部素材
+            if (newValues.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
+                console.log(adcreative_template?.adcreativeElements)
+                adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image').forEach(item => {
+                    switch (item.name) {
+                        case 'image'://图素材
+                            newValues.adcreativeElements = {
+                                ...newValues.adcreativeElements,
+                                imageUrl: '',
+                            }
+                            break;
+                        case 'video'://视频素材
+                            newValues.adcreativeElements = {
+                                ...newValues.adcreativeElements,
+                                videoUrl: '',
+                            }
+                            break;
+                        case 'image_list'://图素材
+                            newValues.adcreativeElements = {
+                                ...newValues.adcreativeElements,
+                                imageUrlList: [],
+                            }
+                            break;
+                        case 'short_video1'://视频素材
+                            newValues.adcreativeElements = {
+                                ...newValues.adcreativeElements,
+                                shortVideoStruct: {
+                                    shortVideo1Url: ''
+                                },
+                            }
+                            break;
+                    }
+                })
+            }
+            console.log('newValues=>2', newValues)
+            newValues['isTemplate'] = template_checked
+            // // 开启存为模板开关执行
+            callback(newValues, materialData, textData)
+        })
+    }, [form, imgMaterialConfig, videoMaterialConfig, materialData, textData, queryForm, template_checked, adcreative_template, isShowSc])
+    // 获取创意形式列表
+    useEffect(() => {
+        if (siteSet?.length > 0 && promotedObjectType) {
+            getAdcreativeTemplateList.run({
+                siteSet,
+                promotedObjectType,
+                campaignType: 'CAMPAIGN_TYPE_NORMAL',
+            }).then(res => {
+                let newArr: any = []
+                // 过滤掉相同的和即将下线的
+                if (!res) {
+                    return
+                }
+                // 
+                Object.values(res)?.forEach((arr: any) => {
+                    Array.isArray(arr) && arr?.forEach((item: any) => {
+                        if (newArr.length > 0) {//假如已存在ID,需要过滤相同
+                            if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId) && newArr.every((i: { adcreativeTemplateId: any }) => i.adcreativeTemplateId !== item.adcreativeTemplateId)) {//不重复的添加
+                                newArr.push(item)
+                            } else {
+                                // 找出通用创意
+                                newArr = newArr?.map((arr: { adcreativeTemplateId: any }) => {
+                                    if (arr.adcreativeTemplateId === item.adcreativeTemplateId) {
+                                        return { ...arr, isGeneral: true }
+                                    }
+                                    return arr
+                                })
+                            }
+                        } else {//不存在ID直接过滤掉即将下线的
+                            if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId)) {
+                                newArr.push(item)
+                            }
+                        }
+                    })
+                })
+                /*****暂时排除激励和banner有问题******/
+                if (siteSet.some((i: string) => i === 'SITE_SET_MOMENTS')) {
+                    newArr = newArr.filter((item: { adcreativeTemplateId: number }) => item.adcreativeTemplateId !== 910 && item.adcreativeTemplateId !== 925)
+                }
+                set_adcreative_template_list(newArr)
+            })
+        }
+    }, [siteSet, promotedObjectType])
+    // 获取创意形式详情
+    const getTemplate = useCallback((id: any, ok?: any) => {
+        // CAMPAIGN_TYPE_NORMAL
+        if (siteSet?.length > 0 && promotedObjectType && id) {
+            if (id) {
+                getAdcreativeTemplate.run({
+                    siteSet,
+                    promotedObjectType,
+                    adcreativeTemplateId: id
+                }).then(res => {
+                    if (res?.length > 0) {
+                        form.setFieldsValue({ adcreativeName: res[0]?.adcreativeTemplateAppellation + '_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId })
+                        set_adcreative_template(res[0])
+                        if (siteSet?.some((name: string) => name === 'SITE_SET_MOMENTS')) {
+                            let id = res[0].adcreativeTemplateId
+                            set_isShowSc(!!creativeConfig[id])//判定当前创意是否需要展示替换素材选项
+                            if (creativeConfig[id] && !ok) {//假如不等于回填元素的ID
+                                let overrideCanvasHeadOption = creativeConfig[id].overrideCanvasHeadOption
+                                form.setFieldsValue({ overrideCanvasHeadOption: overrideCanvasHeadOption?.includes('OPTION_CREATIVE_OVERRIDE_CANVAS') ? 'OPTION_CREATIVE_OVERRIDE_CANVAS' : overrideCanvasHeadOption[0] })
+                            }
+                        }
+                        templateChange(res[0], ok)
+                        // 处理素材
+                        setMaterialData(res[0]?.adcreativeElements?.filter((item: any) => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image').map((item: any) => {
+                            return {
+                                label: item.description === '图片' && res[0]?.adcreativeElements?.some((item: any) => item.name === 'video') ? '视频封面图' : item.description,
+                                name: item.name,
+                                restriction: item.restriction,
+                                arrayProperty: item?.arrayProperty
+                            }
+                        }))
+                        // 处理文案
+                        setTextData(res[0]?.adcreativeElements?.filter((item: any) => item.name === 'title' || (item.required && item.name === 'description')).map((item: any) => ({ ...item, pupState })))
+                    }
+                })
+            }
+        }
+    }, [siteSet, promotedObjectType, pupState])
+    // 获取对应落地页按钮
+    const pageTypeList = useMemo(() => {
+        if (adcreativeTemplateId) {
+            let arr: any = adcreative_template?.landingPageConfig?.supportPageTypeList
+            return arr
+        }
+        return null
+
+    }, [adcreativeTemplateId, adcreative_template])
+    // 获取对应行动按钮数据
+    const linkNameList = useMemo(() => {
+        if (pageType) {
+            let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
+            return arr
+        }
+        return null
+    }, [pageType, pageTypeList])
+    // 跳转落地页
+    const linkPageList = useMemo(() => {
+        if (pageType) {
+            let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
+            return arr
+        }
+        return null
+    }, [pageType, pageTypeList])
+    const typeChange = useCallback((adcreativeElementsType) => {
+        if (adcreativeElementsType && adcreative_template_list?.length > 0) {
+            let adcreativeTemplateIdArr = adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)
+            console.log('typeChange====>', adcreativeTemplateIdArr[0].adcreativeTemplateId)
+            getTemplate(adcreativeTemplateIdArr[0].adcreativeTemplateId)
+            form.setFieldsValue({ adcreativeTemplateId: adcreativeTemplateIdArr[0].adcreativeTemplateId })
+
+        }
+    }, [adcreative_template_list])
+
+    //每次选中创意设置该展示的界面
+    const templateChange = useCallback((adcreative_template, ok?: any) => {
+        let states = {
+            kp_show: false,
+            xd_show: true,
+            sj_show: false,
+            bq_show: false,
+            sp_show: false
+        }
+        let values: any = { pageType: 'PAGE_TYPE_CANVAS_WECHAT', }
+        if (adcreative_template) {
+            let pageList = adcreative_template?.landingPageConfig?.supportPageTypeList?.filter((i: { description: string | string[] }) => i.description.includes('微信原生推广页'))//当前版本只获取微信原生页,后期改进
+            let pageType = pageList?.length ? pageList[0]?.pageType : null
+            //数据展示组件
+            if (adcreative_template.adcreativeAttributes.some((item: { name: string }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')) {
+                let arr = adcreative_template.adcreativeAttributes?.filter((item: { name: string; }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')
+                let newObj: any = {}
+                arr.forEach((item: { propertyDetail: { enumDetail: { enumeration: any[] } }; name: string | number }) => {
+                    let arr: any[] = mySet(item.propertyDetail.enumDetail.enumeration)
+                    newObj[item.name] = arr
+                })
+                setConversionList(newObj)
+                states = { ...states, sj_show: true }
+                if (newObj.conversion_data_type) {
+                    values = { ...values, conversionDataType: newObj.conversion_data_type[0].value }
+                }
+                if (newObj.conversion_target_type) {
+                    values = { ...values, conversionTargetType: newObj.conversion_target_type[0].value }
+                }
+            }
+
+            //行动按钮组件存在
+            if (states.xd_show) {
+                let linkNameList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
+                let linkPageList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
+                if (linkNameList && !linkPageType) {
+                    if (!ok) {
+                        let linkNameType = linkNameList[0]?.linkNameType
+                        let linkPageType = linkPageList?.some((i: { linkPageType: string }) => i.linkPageType === "LINK_PAGE_TYPE_CANVAS_WECHAT") ? "LINK_PAGE_TYPE_CANVAS_WECHAT" : linkPageList[0]?.linkPageType
+                        values = { ...values, linkNameType, linkPageType, actionBtn: true }
+                    }
+                } else {
+                    states = { ...states, xd_show: false }
+                }
+            }
+            // 特殊行动按钮
+            if (adcreative_template.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text') && !ok) {
+                values = { ...values, buttonText: adcreative_template?.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text')?.enumProperty?.enumeration[0].value }
+            }
+            // 视频结束页 end_page
+            if (adcreative_template.adcreativeElements.some((item: { name: string }) => item.name === 'end_page')) {
+                // let endPageType =adcreative_template?.adcreativeElements?.filter(item=>item.name === 'end_page_type')[0]?.enumProperty?.enumeration
+                if (!ok) {
+                    values = { ...values, endPageType: 'END_PAGE_AVATAR_NICKNAME_HIGHLIGHT' }
+                }
+                states = { ...states, sp_show: true }
+            }
+            setPupState(states)
+            form.setFieldsValue(values)
+        }
+    }, [pageType, linkPageType])
+    // 版位改变清空数据
+    useEffect(() => {
+        if (imgMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== imgMaterialConfig.adcreativeTemplateId) {
+            setImgMaterialConfig({ ...imgMaterialConfig, adcreativeTemplateId: undefined, list: [] })
+        }
+        if (videoMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== videoMaterialConfig.adcreativeTemplateId) {
+            setVideoMaterialConfig({ ...videoMaterialConfig, adcreativeTemplateId: undefined, list: [] })
+        }
+    }, [adcreativeTemplateId, imgMaterialConfig, videoMaterialConfig])
+    // 文案助手
+    const textList = useCallback((arg: { maxTextLength: number, keyword?: string }) => {
+        let { maxTextLength, keyword } = arg
+        getTextLsit.run({ keyword: keyword || titles || description, maxTextLength })
+    }, [titles, description])
+    // 监听点击取消文案助手弹窗
+    useEffect(() => {
+        let modal = document.querySelector('.myModal')
+        let onBiurdescription = (e: any) => {
+            let d = document.querySelector('.my_description')
+            let t = document.querySelector('.my_title')
+            let p = document.querySelector('.my_endPageDesc')
+            if (!d?.contains(e.target)) {
+                setdescriptionshow(false)
+            }
+            if (!t?.contains(e.target)) {
+                settitleshow(false)
+            }
+            if (!p?.contains(e.target)) {
+                setendPageDescnshow(false)
+            }
+        }
+        modal?.addEventListener('click', onBiurdescription)
+        return () => {
+            modal?.removeEventListener('click', onBiurdescription)
+        }
+
+    }, [])
+    // 数据回填
+    useEffect(() => {
+        if (!infoSet && dataInfo && adcreative_template_list?.length > 0) {
+            let { adcreativeName, adcreativeTemplateId, conversionDataType, conversionTargetType, linkNameType, linkPageType, pageType, promotedObjectType, siteSet, profile, adcreativeElements, overrideCanvasHeadOption, linkPageSpec } = dataInfo
+            let { description, imageUrl, title, videoUrl, imageUrlList, endPage, shortVideoStruct, brand, buttonText } = adcreativeElements
+            let obj: any = {
+                adcreativeName,
+                siteSet,
+                promotedObjectType,
+                adcreativeTemplateId,
+            }
+            getTemplate(adcreativeTemplateId, true)
+            console.log(2222, dataInfo)
+            if ([720, 721, 618, 1708, 722, 1529].some(n => n === adcreativeTemplateId)) {
+                obj = { ...obj, adcreativeElementsType: '视频' }
+            } else {
+                obj = { ...obj, adcreativeElementsType: '图片' }
+            }
+            if (conversionDataType) {
+                obj = { ...obj, conversionDataType, dataShow: true }
+            }
+            if (conversionTargetType) {
+                obj = { ...obj, conversionTargetType, dataShow: true }
+            }
+            if (linkNameType) {
+                obj = { ...obj, linkNameType, actionBtn: true }
+            }
+            if (linkPageType) {
+                obj = { ...obj, linkPageType, actionBtn: true }
+            }
+            if (pageType) {
+                obj = { ...obj, pageType }
+            }
+            if (description) {
+                obj = { ...obj, description }
+            }
+            if (title) {
+                obj = { ...obj, title }
+            }
+            if (endPage) {
+                obj = { ...obj, videoOver: true, ...endPage }
+            }
+            if (overrideCanvasHeadOption) {
+                obj = { ...obj, overrideCanvasHeadOption }
+            }
+            if (linkPageSpec?.pageUrl) {
+                obj = { ...obj, pageUrl: linkPageSpec?.pageUrl }
+            }
+            if (linkPageSpec?.miniProgramSpec && linkPageSpec?.miniProgramSpec?.miniProgramPath) {
+                obj = { ...obj, miniProgramPath: linkPageSpec?.miniProgramSpec?.miniProgramPath, miniProgramId: linkPageSpec?.miniProgramSpec?.miniProgramId }
+            }
+            if (brand && brand.brandImgUrl && brand.brandName) {
+                obj = { ...obj, brand: brand.brandName + '_' + brand.brandImgUrl }
+            }
+            if (profile && profile.headImageUrl && profile.profileName && profile.description) {
+                obj = { ...obj, profile: profile.profileName + '_' + profile.headImageUrl + '_' + profile.description }
+            }
+            if (buttonText) {
+                obj = { ...obj, buttonText }
+            }
+            if (videoUrl) {
+                setVideoMaterialConfig({
+                    cloudSize: [],
+                    list: [{ url: videoUrl }],
+                    max: 1,
+                    type: 'video',
+                    adcreativeTemplateId
+                })
+                obj = { ...obj, video: videoUrl }
+            }
+            if (imageUrl) {
+                setImgMaterialConfig({
+                    cloudSize: [],
+                    list: [{ url: imageUrl }],
+                    max: 1,
+                    type: 'image',
+                    adcreativeTemplateId
+                })
+                obj = { ...obj, image: imageUrl }
+            }
+            if (imageUrlList) {
+                setImgMaterialConfig({
+                    cloudSize: [],
+                    list: imageUrlList?.map((url: any) => ({ url })),
+                    max: imageUrlList.length,
+                    type: 'image_list',
+                    adcreativeTemplateId
+                })
+                obj = { ...obj, image_list: imageUrlList }
+            }
+            if (shortVideoStruct) {
+                setVideoMaterialConfig({
+                    cloudSize: [],
+                    list: [{ url: shortVideoStruct.shortVideo1Url }],
+                    max: 1,
+                    type: 'short_video1',
+                    adcreativeTemplateId
+                })
+                obj = { ...obj, short_video1: shortVideoStruct.shortVideo1Url }
+            }
+            console.log('数据回填====>', obj)
+            form.setFieldsValue(obj)
+            set_infoSet(true)
+        }
+        // 不是数据回填首次打开界面选中视频
+        if (!infoSet && !dataInfo && adcreative_template_list?.length > 0) {
+            typeChange('视频')
+            set_infoSet(true)
+        }
+    }, [dataInfo, adcreative_template_list, adcreative_template, infoSet])
+    // 生成视频封面图
+    const videoToImgs = useCallback(() => {
+        if (videoMaterialConfig.list[0]) {
+            set_videoImgsVisbile(true)
+            // let url = videoMaterialConfig.list[0].url
+            // fetch(url).then(res => res.blob()).then(async (blob) => {
+            //     let file = new File([blob], 'sp', { type: blob.type })
+            //     // let md5 = await getMD5(file)
+            //     let formData = new FormData()
+            //     formData.append('videoFile', file)
+            //     formData.append('number', '12')
+            //     getVideoCapture.run(formData).then(res => {
+            //         console.log(res)
+            //     })
+            // })
+        } else {
+            message.warning('请先选择视频文件!!!')
+        }
+    }, [videoMaterialConfig.list])
+    return <Modal
+        visible={visible}
+        title={type === 'add' ? '新建创意' : type === 'look' ? '创意详情' : '编辑创意'}
+        onCancel={() => { PupFn({ visible: false, dataInfo: null, type: 'add' }) }}
+        // onOk={handleOk}
+        width={1200}
+        confirmLoading={confirmLoading}
+        footer={<Space>
+            <Button onClick={() => { PupFn({ visible: false, dataInfo: null, type: 'add' }) }}>取消</Button>
+            <Button type='primary' onClick={handleOk}>确定</Button>
+            {/* {<Checkbox checked={template_checked} onChange={(e) => {
+                let checked = e.target.checked
+                settemplate_checked(checked)
+            }}>存为模板</Checkbox>} */}
+        </Space>}
+        className='myModal'
+        {...arg}
+    >
+        <Form
+            form={form}
+            labelCol={{ span: 5 }}
+            labelWrap={true}
+            className='ad_form_style'
+            initialValues={
+                {
+                    adcreativeElementsType: '视频',
+                }
+            }
+        >
+            {/* ============================================================创意形式============================================================= */}
+            <Divider orientation='center'>创意形式</Divider>
+            {/* ============================================================创意形式============================================================= */}
+            <Form.Item label={<strong>创意形式</strong>} name='adcreativeElementsType'>
+                <Radio.Group onChange={(e) => {
+                    let value = e.target.value
+                    typeChange(value)
+                }}>
+                    <Radio.Button value="视频">视频</Radio.Button>
+                    <Radio.Button value="图片">图片</Radio.Button>
+                </Radio.Group>
+            </Form.Item>
+
+            {
+                getAdcreativeTemplateList?.loading ? <Spin tip="Loading..." style={{ width: '100%' }}></Spin> :
+                    <>
+                        <Form.Item style={{ marginLeft: 177 }} name='adcreativeTemplateId'>
+                            <Radio.Group className={styles.adcreative_template} onChange={(e) => {
+                                let id = e.target.value
+                                getTemplate(id)
+                            }}>
+                                {
+                                    adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)?.map((item: any) => {
+                                        return <Radio.Button value={item.adcreativeTemplateId} key={item.adcreativeTemplateId}>
+                                            <div className={styles.adcreative_template_item}>
+                                                {item.isGeneral && <span style={{ color: '#4080ff', fontSize: 10 }}>所选版位通投</span>}
+                                                <img src={item.adcreativeSampleImage} />
+                                                <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateAppellation}</span>
+                                                <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateId}</span>
+                                            </div>
+                                        </Radio.Button>
+                                    })
+                                }
+                            </Radio.Group>
+                        </Form.Item>
+                        {/* ============================================================创意内容============================================================= */}
+                        <Divider orientation='center'>创意内容</Divider>
+                        {/* =============================================================头像及昵称跳转页===================================================================== */}
+                        {queryForm.promotedObjectType === 'PROMOTED_OBJECT_TYPE_LEAD_AD' ? adcreative_template?.adcreativeAttributes?.find(item => item.name === 'profile_id') ? <Form.Item label={<strong>头像及昵称跳转页</strong>} name='profile' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
+                            <HeadNickJump />
+                        </Form.Item> : <Form.Item label={<strong>品牌形象</strong>} name='brand' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
+                            <BrandImage />
+                        </Form.Item> : null}
+                        {/* ============================================================素材============================================================= */}
+
+                        {/* 标题 */}
+                        {/* {
+                            adcreative_template?.adcreativeElements?.filter(item => item.name === 'title').map(item => {
+                                return <div key={item.fieldType}>
+                                    <Form.Item label={<strong>{item.description}(选填)</strong>} className={'my_title'} >
+                                        <Form.Item name={item.name} rules={[{ pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${item.restriction.textRestriction.maxLength}}`)), message: '请输入正确的' + item.description }]} noStyle>
+                                            <Input
+                                                placeholder={'请输入' + item.description}
+                                                style={{ width: 500 }}
+                                                allowClear
+                                                onFocus={() => {
+                                                    settitleshow(true)
+                                                    textList({ maxTextLength: item.restriction.textRestriction.maxLength })
+                                                }}
+                                                onChange={(e) => {
+                                                    let value = e.target.value
+                                                    textList({ maxTextLength: item.restriction.textRestriction.maxLength, keyword: value })
+                                                }}
+                                            />
+                                        </Form.Item>
+                                        <span>{`${titles?.length ?? 0}/${item.restriction.textRestriction.maxLength}`}</span>
+                                        {
+                                            titleShow && <List
+                                                loading={getTextLsit?.loading}
+                                                size="small"
+                                                style={{ maxHeight: 300, overflowX: 'auto' }}
+                                                bordered
+                                                dataSource={getTextLsit?.data?.returnTexts}
+                                                renderItem={(item: any) => <List.Item onClick={() => {
+                                                    form.setFieldsValue({ title: item.text })
+                                                    settitleshow(false)
+                                                }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
+                                            />
+                                        }
+                                    </Form.Item>
+                                </div>
+                            })
+                        } */}
+                        {//过滤了不必传和品牌名称,品牌标识图(外部传)短视频结构(组装使用)
+                            // adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'description').map(item => {
+                            //     let maxNum = adcreativeTemplateId === 1708 || adcreativeTemplateId === 1707 ? pupState.xd_show ? 10 : item.restriction.textRestriction.maxLength : item.restriction.textRestriction.maxLength
+                            //     return <div key={item.fieldType}>
+                            //         <Form.Item label={<strong>{item.description}</strong>} className={'my_description'}>
+                            //             <Form.Item name={item.name} noStyle rules={[{ required: true, pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${maxNum}}`)), message: '请输入正确的' + item.description }]}>
+                            //                 <Input
+                            //                     placeholder={'请输入' + item.description}
+                            //                     style={{ width: 500 }}
+                            //                     onFocus={() => {
+                            //                         setdescriptionshow(true)
+                            //                         textList({ maxTextLength: maxNum })
+                            //                     }}
+                            //                     onChange={(e) => {
+                            //                         let value = e.target.value
+                            //                         textList({ maxTextLength: maxNum, keyword: value })
+                            //                     }}
+                            //                     allowClear
+                            //                 />
+                            //             </Form.Item>
+                            //             <span>{`${description?.length ?? 0}/${maxNum}`}</span>
+                            //             {
+                            //                 descriptionShow && <List
+                            //                     loading={getTextLsit?.loading}
+                            //                     size="small"
+                            //                     style={{ maxHeight: 300, overflowX: 'auto' }}
+                            //                     bordered
+                            //                     dataSource={getTextLsit?.data?.returnTexts}
+                            //                     renderItem={(item: any) => <List.Item onClick={(e: any) => {
+                            //                         form.setFieldsValue({ description: item.text })
+                            //                         setdescriptionshow(false)
+                            //                     }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
+                            //                 />
+                            //             }
+                            //         </Form.Item>
+                            //     </div>
+                            // })
+                        }
+                        {/* ============================================================落地页============================================================= */}
+                        {adcreativeTemplateId ? <Form.Item label={<strong>落地页</strong>} name='pageType' >
+                            <Radio.Group>
+                                {
+                                    pageTypeList?.map((item: any) => {
+                                        return <Radio.Button value={item.pageType} key={item.pageType} disabled={!item.description.includes('微信原生推广页')}>{item.description.includes('微信原生推广页') ? '微信原生推广页' : item.description}</Radio.Button>
+                                    })
+                                }
+                            </Radio.Group>
+                        </Form.Item> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
+                            <Empty description="请先选择创意形式" />
+                        </div>}
+                        {
+                            pageType === 'PAGE_TYPE_CANVAS_WECHAT' && isShowSc && <Form.Item label={<strong>素材选项</strong>} name='overrideCanvasHeadOption'>
+                                <Radio.Group disabled>
+                                    {
+                                        adcreativeTemplateId && creativeConfig[adcreativeTemplateId]?.overrideCanvasHeadOption?.map((item: string | number) => {
+                                            return <Radio value={item} key={item}>{overrideCanvasHeadOptionEnum[item]}</Radio>
+                                        })
+                                    }
+                                </Radio.Group>
+                            </Form.Item>
+                        }
+                        {/* ============================================================普通行动按钮============================================================= */}
+                        {
+                            pupState.xd_show && <Form.Item label={<strong>行动按钮</strong>} name='actionBtn' valuePropName="checked">
+                                <Switch checkedChildren="开启" unCheckedChildren="关闭" />
+                            </Form.Item>
+                        }
+                        {
+                            actionBtn && <>
+                                <Form.Item name='linkNameType' label={<strong>按钮文案</strong>}>
+                                    <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
+                                        (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                                    } allowClear>
+                                        {
+                                            linkNameList?.map((item: any) => {
+                                                return <Select.Option value={item.linkNameType} key={item.linkNameType}>{item.description}</Select.Option>
+                                            })
+                                        }
+                                    </Select>
+                                </Form.Item>
+                                <Form.Item label={<strong>跳转落地页</strong>}>
+                                    <Form.Item name='linkPageType' noStyle>
+                                        <Radio.Group style={{ display: 'flex' }}>
+                                            {
+                                                linkPageList?.map((item: { linkPageType: string; description: string; }, index: number) => {
+                                                    return <Radio.Button value={item.linkPageType} key={item.linkPageType} >{item.description}</Radio.Button>
+                                                })
+                                            }
+                                        </Radio.Group>
+                                    </Form.Item>
+                                    {/* 自定义落地页地址 */}
+                                    {linkPageType === "LINK_PAGE_TYPE_DEFAULT" && <Form.Item name='pageUrl' rules={[{ required: true, message: '请输入自定义落地页地址' }]} style={{ marginTop: 10, marginBottom: 0 }}>
+                                        <Input placeholder='请输入自定义落地页地址' style={{ width: 300 }} />
+                                    </Form.Item>}
+                                    {/* 小程序 */}
+                                    {
+                                        linkPageType === "LINK_PAGE_TYPE_MINI_PROGRAM_WECHAT" && <Form.Item noStyle >
+                                            <Form.Item rules={[{ required: true, message: '请输入小程序原始ID' }]} name='miniProgramId' style={{ marginTop: 10, marginBottom: 0 }} >
+                                                <Input placeholder='请输入小程序原始ID' style={{ width: 300 }} />
+                                            </Form.Item>
+                                            <Form.Item rules={[{ required: true, message: '请输入小程序链接' }]} name='miniProgramPath' style={{ marginTop: 10, marginBottom: 0 }}>
+                                                <Input placeholder='请输入小程序链接' style={{ width: 300 }} />
+                                            </Form.Item>
+                                        </Form.Item>
+                                    }
+                                </Form.Item>
+                                {/* 落地页 */}
+
+                            </>
+                        }
+                        {/* ============================================================特殊行动按钮============================================================= */}
+                        {
+                            adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item label={<strong>行动按钮</strong>} >
+                                <Form.Item valuePropName="checked" noStyle >
+                                    <Switch checkedChildren="开启" unCheckedChildren="关闭" checked={true} disabled defaultChecked={true} />
+                                </Form.Item>
+                            </Form.Item>
+                        }
+                        {
+                            adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item name='buttonText' label={<strong>按钮文案</strong>} rules={[{ required: true, message: '请选择按钮文案!' }]}>
+                                <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
+                                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                                } allowClear>
+                                    {
+                                        adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text')?.enumProperty?.enumeration?.map((item: any) => {
+                                            return <Select.Option value={item.value} key={item.value}>{item.value}</Select.Option>
+                                        })
+                                    }
+                                </Select>
+                            </Form.Item>
+                        }
+                        {/* ============================================================数据展示============================================================= */}
+                        {pupState.sj_show && <Form.Item label={<strong>数据展示</strong>} name='dataShow' valuePropName="checked">
+                            <Switch checkedChildren="开启" unCheckedChildren="关闭" />
+                        </Form.Item>}
+                        {
+                            dataShow && <>
+                                <Form.Item name='conversionDataType' label={<strong>数据类型</strong>}>
+                                    <Radio.Group>
+                                        {
+                                            conversionList?.conversion_data_type?.map((item: { value: string; description: string; }, index: number) => {
+                                                return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
+                                            })
+                                        }
+                                    </Radio.Group>
+                                </Form.Item>
+                                {conversionList?.conversion_target_type && conversionDataType === 'CONVERSION_DATA_ADMETRIC' && <Form.Item name='conversionTargetType' label={<strong>转化行为</strong>}>
+                                    <Radio.Group>
+                                        {
+                                            conversionList?.conversion_target_type?.map((item: { value: string; description: string; }, index: number) => {
+                                                return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
+                                            })
+                                        }
+                                    </Radio.Group>
+                                </Form.Item>}
+                            </>
+                        }
+                        {/* ============================================================视频结束页============================================================= */}
+                        {pupState.sp_show && <Form.Item label={<strong>视频结束页</strong>} name='videoOver' valuePropName="checked">
+                            <Switch checkedChildren="开启" unCheckedChildren="关闭" />
+                        </Form.Item>}
+                        {
+                            videoOver && <>
+                                <Form.Item name='endPageType' label={<strong>视频结束页类型</strong>} >
+                                    <Radio.Group>
+                                        {
+                                            adcreative_template?.adcreativeElements?.filter(item => item.name === 'end_page_type')[0]?.enumProperty?.enumeration?.map((item) => {
+                                                return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
+                                            })
+                                        }
+                                    </Radio.Group>
+                                </Form.Item>
+                                <div className={'my_endPageDesc'} >
+                                    <Form.Item label={<strong>结束文案</strong>}>
+                                        <Form.Item name='endPageDesc' rules={[{ required: true, pattern: RegExp("^[^\\<\\>\\&'\\\"\\/\\x08\\x09\\x0A\\x0D\\\\]{1,12}$"), message: '请输入正确的结束页文案' }]} noStyle>
+                                            <Input
+                                                placeholder='请输入结束页文案'
+                                                style={{ width: 300 }}
+                                                onFocus={() => {
+                                                    setendPageDescnshow(true)
+                                                    textList({ maxTextLength: 12 })
+                                                }}
+                                                onChange={(e) => {
+                                                    let value = e.target.value
+                                                    textList({ maxTextLength: 12, keyword: value })
+                                                }}
+                                                allowClear
+                                            />
+                                        </Form.Item>
+                                        <span>{endPageDesc?.length || 0}/12</span>
+                                        {
+                                            endPageDescShow && <List
+                                                loading={getTextLsit?.loading}
+                                                size="small"
+                                                style={{ maxHeight: 300, maxWidth: 300, overflowX: 'auto' }}
+                                                bordered
+                                                dataSource={getTextLsit?.data?.returnTexts}
+                                                renderItem={(item: any) => <List.Item onClick={(e: any) => {
+                                                    form.setFieldsValue({ endPageDesc: item.text })
+                                                    setendPageDescnshow(false)
+                                                }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
+                                            />
+                                        }
+                                    </Form.Item>
+                                </div>
+                            </>
+                        }
+                    </>
+            }
+            {/* ============================================================基本信息============================================================= */}
+            <Divider orientation='center'>基本信息</Divider>
+            {/* ============================================================创意名称============================================================= */}
+            <Form.Item label={<strong>创意名称</strong>} name='adcreativeName' rules={[{ required: true, message: '请输入广告名称!' }]}>
+                <Input placeholder='创意名称' style={{ width: 300 }} />
+            </Form.Item>
+        </Form>
+        {/* 选择图片素材 */}
+        {
+            selectImgVisible && <SelectCloud
+                visible={selectImgVisible}
+                onClose={() => {
+                    set_selectImgVisible(false)
+                }}
+                sliderImgContent={imgMaterialConfig.list}
+                onChange={(content) => {
+                    if (content.length > 0) {
+                        form.setFieldsValue({ [imgMaterialConfig.type]: imgMaterialConfig.type })
+                    }
+                    setImgMaterialConfig({ ...imgMaterialConfig, list: content })
+                    set_selectImgVisible(false)
+                }} />
+        }
+        {/* 选择视频素材 */}
+        {
+            selectVideoVisible && <SelectCloud
+                visible={selectVideoVisible}
+                onClose={() => set_selectVideoVisible(false)}
+                sliderImgContent={videoMaterialConfig.list}
+                onChange={(content) => {
+                    if (content.length > 0) {
+                        form.setFieldsValue({ [videoMaterialConfig.type]: videoMaterialConfig.type })
+                    }
+                    setVideoMaterialConfig({ ...videoMaterialConfig, list: content })
+                    set_selectVideoVisible(false)
+                }} />
+        }
+        {/* 视频封面图弹窗 */}
+        {
+            videoImgsVisbile && <Modal
+                visible={videoImgsVisbile}
+                title={<div>生成封面图 <Switch checkedChildren="开启预览" unCheckedChildren="关闭预览" checked={videoImgs.preview} onChange={(checked) => { set_videoImgs({ ...videoImgs, preview: checked }) }} /></div>}
+                onOk={() => {
+                    if (videoImgs.activeUrl) {
+                        setImgMaterialConfig({ ...imgMaterialConfig, list: [{ url: videoImgs.activeUrl }] })
+                        set_videoImgsVisbile(false)
+                    } else {
+                        message.error('请选择图片,获取使用取消按钮关闭弹窗!')
+                    }
+                }}
+                onCancel={() => { set_videoImgsVisbile(false) }}
+                confirmLoading={getVideoCapture.loading}
+                width={600}
+            >
+
+                <Radio.Group className={styles.videoImgs} onChange={(e) => {
+                    let url = e.target.value
+                    set_videoImgs({ ...videoImgs, activeUrl: url })
+                }}>
+                    {
+                        videoImgs?.urlList?.map((item: any, index: number) => {
+                            return <Radio.Button value={item} key={index}>
+                                <Image src={item} preview={videoImgs.preview} />
+                            </Radio.Button>
+                        })
+                    }
+                </Radio.Group>
+            </Modal>
+        }
+    </Modal >
+}
+export default CreativePup

+ 313 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/material.tsx

@@ -0,0 +1,313 @@
+import { useAjax } from "@/Hook/useAjax"
+import SelectCloud from "@/pages/launchSystemNew/components/selectCloud"
+import { get_tools_video_capture } from "@/services/launchAdq/global"
+import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"
+import { Button, Form, message, Modal, Radio, Space, Switch, Image } from "antd"
+import React, { useEffect, useState } from "react"
+import { useModel } from "umi"
+import styles from './index.less'
+
+interface Props {
+    value?: any[]
+    material?: { label: string, name: string, restriction: any, arrayProperty?: any }[],
+    sysAdcreative: any
+    onChange?: (data: any) => void
+}
+
+/**
+ * 选择素材组   
+ * @returns 
+ */
+const Material: React.FC<Props> = (props) => {
+
+    /**************************/
+    const { onChange, material = [], sysAdcreative, value } = props
+    const { adcreativeTemplateId } = sysAdcreative
+    const [visible, setVisible] = useState<boolean>(false)
+    const [selectVideoVisible, set_selectVideoVisible] = useState(false)
+    const [videoImgsVisbile, set_videoImgsVisbile] = useState(false)
+    const { init } = useModel('useLaunchAdq.useBdMediaPup')
+    const [materialConfig, setMaterialConfig] = useState<{
+        adcreativeTemplateId?: number,
+        type: string,
+        cloudSize: { relation: string, width: number, height: number }[],
+        list: any[],
+        index: number,
+        max: number,
+        sliderImgContent: any
+    }>({
+        type: '',//类型
+        cloudSize: [],//素材搜索条件
+        list: [],//素材
+        index: 0, // 素材组下标
+        max: 1,//素材数量
+        sliderImgContent: undefined
+    })//图片素材配置
+    const [videoImgs, set_videoImgs] = useState<{//视频封面图设置
+        activeUrl: string,//选中的视频封面图地址
+        preview: boolean,//是否开启图片点击预览
+        urlList: any[],//生成的视频封面列表
+    }>({
+        activeUrl: '',
+        preview: false,
+        urlList: [
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/21D8D51AD98C4FF8BF41F1C2D28EA39F.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/80DBE1AB3EDE4E85ABAE5F1670D9FED0.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/BCB2DAB86BDB4549BCB8E493C4F29E82.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/545A4C2A5B874C82A9D1C0C063624AE5.jpg'
+        ]
+    })
+
+    const [form] = Form.useForm();
+    let materials = Form.useWatch('materials', form)
+
+    const getVideoCapture = useAjax((params) => get_tools_video_capture(params))
+    /**************************/
+
+    // 回填
+    useEffect(() => {
+        if (visible) {
+            if (value && value?.length > 0) {
+                let data = value?.map((item: any) => {
+                    let data: any = {}
+                    for (const key in item) {
+                        switch (key) {
+                            case 'imageUrl'://图素材
+                                data.image = item[key]
+                                break;
+                            case 'videoUrl'://视频素材
+                                data.video = item[key]
+                                break;
+                            case 'imageUrlList'://图素材
+                                data.image_list = item[key]
+                                break;
+                            case 'shortVideo1Url'://视频素材
+                                data.short_video1 = item[key]
+                                break;
+                        }
+                    }
+                    return data
+                })
+                form.setFieldsValue({ materials: data })
+            } else {
+                form.setFieldsValue({ materials: [undefined] })
+            }
+        }
+    }, [value, visible])
+
+    // 确定
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            let data = values?.materials?.map((item: any) => {
+                let data: any = {}
+                for (const key in item) {
+                    switch (key) {
+                        case 'image'://图素材
+                            data.imageUrl = item[key]
+                            break;
+                        case 'video'://视频素材
+                            data.videoUrl = item[key]
+                            break;
+                        case 'image_list'://图素材
+                            data.imageUrlList = item[key]
+                            break;
+                        case 'short_video1'://视频素材
+                            data.shortVideo1Url = item[key]
+                            break;
+                    }
+                }
+                return data
+            })
+            onChange && onChange(data)
+            setVisible(false)
+        })
+    }
+
+    return <>
+        <span onClick={() => { setVisible(true) }}>{value && value?.length > 0 ? '编辑' : '添加'}</span>
+        {visible && <Modal visible={visible} onCancel={() => setVisible(false)} title="创意素材" width={930} onOk={handleOk}>
+            <Form name="dynamic_form_item" form={form} labelAlign='left' >
+                <Form.List name="materials">
+                    {(fields, { add, remove }) => (<>
+                        {fields.map((field, num) => (<div key={field.key}>
+                            <Space size={30} style={{ width: '100%' }} className={styles.space}>
+                                {material?.map((item, index) => {
+                                    if (item.name === 'short_video1' || item.name === 'video') {
+                                        return <Form.Item
+                                            {...field}
+                                            label={<strong>{item.label}</strong>}
+                                            rules={[{ required: true, message: '请选择素材!' }]}
+                                            name={[field.name, item.name]}
+                                            key={index}
+                                        >
+                                            <div className={`${styles.box} ${styles.video}`} style={{ width: 300, height: 160 }} onClick={() => {
+                                                init({ mediaType: 'VIDEO', cloudSize: adcreativeTemplateId === 1708 ? [[{ relation: '=', width: 1280, height: 720 }]] : [[{ relation: '=', width: item.restriction.videoRestriction.minWidth, height: item.restriction.videoRestriction.minHeight }]], maxSize: item.restriction.videoRestriction.fileSize * 1024 })
+                                                setMaterialConfig({
+                                                    ...materialConfig,
+                                                    type: item.name,
+                                                    max: 1,
+                                                    index: num,
+                                                    adcreativeTemplateId
+                                                })
+                                                setTimeout(() => {
+                                                    set_selectVideoVisible(true)
+                                                }, 100)
+                                            }}>
+                                                <p>
+                                                    {materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name) ? <video src={materials[num][item.name]} controls /> : <>
+                                                        <span>{`推荐尺寸(${adcreativeTemplateId === 1708 ? 1280 : item.restriction.videoRestriction.minWidth} x ${adcreativeTemplateId === 1708 ? 720 : item.restriction.videoRestriction.minHeight})`}</span>
+                                                        <span>{`${item.restriction.videoRestriction.fileFormat?.map((str: any) => str?.replace('MEDIA_TYPE_', ''))};< ${item.restriction.videoRestriction.fileSize / 1024}M;时长 ≥ ${item.restriction.videoRestriction.minDuration}s,≤ ${item.restriction.videoRestriction.maxDuration}s,必须带有声音`}</span>
+                                                    </>}
+                                                </p>
+                                            </div>
+                                        </Form.Item>
+                                    }
+                                    if (item.name === 'image') {
+                                        return <Form.Item
+                                            {...field}
+                                            label={<strong>{item.label}</strong>}
+                                            rules={[{ required: true, message: '请选择素材!' }]}
+                                            name={[field.name, item.name]}
+                                            key={index}
+                                        >
+                                            <div className={`${styles.box} ${styles.image}`} style={{ width: 300, height: 160 }} onClick={() => {
+                                                init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]], maxSize: item.restriction.imageRestriction.fileSize * 1024 })
+                                                setMaterialConfig({
+                                                    ...materialConfig,
+                                                    type: item.name,
+                                                    max: 1,
+                                                    index: num,
+                                                    adcreativeTemplateId
+                                                })
+                                                setTimeout(() => {
+                                                    set_selectVideoVisible(true)
+
+                                                }, 100)
+                                            }}>
+                                                <p>
+                                                    {materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name) ? <img src={materials[num][item.name]} /> : <>
+                                                        <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
+                                                        <span>{`${item.restriction.imageRestriction.fileFormat?.map((str: any) => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
+                                                    </>}
+                                                </p>
+                                            </div>
+                                        </Form.Item>
+                                    }
+                                    if (item.name === 'image_list') {
+                                        return <Form.Item
+                                            {...field}
+                                            label={<strong>{item.label}</strong>}
+                                            rules={[{ required: true, message: '请选择素材!' }]}
+                                            name={[field.name, item.name]}
+                                            key={index}
+                                        >
+                                            <div className={`${styles.box} ${item.arrayProperty.maxNumber >= 3 ? styles.image_list : styles.imageMater}`} style={item.arrayProperty.maxNumber >= 3 ? { flexFlow: 'row', width: '100%' } : {}} onClick={() => {
+                                                init({ mediaType: 'IMG', num: item.arrayProperty.maxNumber, cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]], maxSize: item.restriction.imageRestriction.fileSize * 1024 })
+                                                setMaterialConfig({
+                                                    ...materialConfig,
+                                                    type: item.name,
+                                                    max: item.arrayProperty.maxNumber,
+                                                    index: num,
+                                                    adcreativeTemplateId,
+                                                    sliderImgContent: (materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name)) ? materials[num][item.name]?.map((item: string) => ({ url: item })) : undefined
+                                                })
+                                                setTimeout(() => {
+                                                    set_selectVideoVisible(true)
+                                                }, 100)
+                                            }}>
+                                                {Array(item.arrayProperty.maxNumber).fill('').map((arr, index1) => {
+                                                    return <p key={index1} style={item.arrayProperty.maxNumber >= 3 ? { width: 130, height: 130 } : { width: 130, height: 130, justifyContent: 'center' }}>
+                                                        {materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name) && materials[num][item.name][index1] ? <img src={materials[num][item.name][index1]} /> : <>
+                                                            <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
+                                                            <span>{`${item.restriction.imageRestriction.fileFormat?.map((str: any) => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
+                                                        </>}
+                                                    </p>
+                                                })}
+                                            </div>
+                                        </Form.Item>
+                                    }
+                                    return null
+
+                                })}
+                                {fields?.length > 1 && <MinusCircleOutlined className={styles.clear} onClick={() => remove(field.name)} style={{ marginBottom: 24, color: 'red' }} />}
+                            </Space>
+                        </div>))}
+                        <Form.Item>
+                            <Button type="link" onClick={() => add()} icon={<PlusOutlined />} style={{ padding: 0 }}>
+                                新增素材组
+                            </Button>
+                        </Form.Item>
+                    </>)}
+                </Form.List>
+            </Form>
+        </Modal>}
+
+        {/* 选择视频素材 */}
+        {selectVideoVisible && <SelectCloud
+            visible={selectVideoVisible}
+            onClose={() => set_selectVideoVisible(false)}
+            sliderImgContent={materialConfig.type === 'image_list'
+                ? (materials[materialConfig.index] && Object.keys(materials[materialConfig.index])?.includes(materialConfig.type)) ? materials[materialConfig.index][materialConfig.type]?.map((item: string) => ({ url: item })) : undefined
+                : (materials[materialConfig.index] && Object.keys(materials[materialConfig.index])?.includes(materialConfig.type)) ? [{ url: materials[materialConfig.index][materialConfig.type] }] : undefined
+            }
+            onChange={(content: any) => {
+                if (content.length > 0) {
+                    materials = materials?.map((item: any, index: number) => {
+                        if (materialConfig.index === index) {
+                            if (materialConfig.type === 'image_list') {
+                                if (item) {
+                                    item[materialConfig.type] = content?.map((item: any) => item?.url)
+                                    return { ...item }
+                                } else {
+                                    return { [materialConfig.type]: content?.map((item: any) => item?.url) }
+                                }
+                            } else {
+                                if (item) {
+                                    item[materialConfig.type] = content[0]?.url
+                                    return { ...item }
+                                } else {
+                                    return { [materialConfig.type]: content[0]?.url }
+                                }
+                            }
+                        }
+                        return item
+                    })
+                    form.setFieldsValue({ materials })
+                }
+                set_selectVideoVisible(false)
+            }}
+        />}
+        {/* 视频封面图弹窗 */}
+        {videoImgsVisbile && <Modal
+            visible={videoImgsVisbile}
+            title={<div>生成封面图 <Switch checkedChildren="开启预览" unCheckedChildren="关闭预览" checked={videoImgs.preview} onChange={(checked) => { set_videoImgs({ ...videoImgs, preview: checked }) }} /></div>}
+            onOk={() => {
+                if (videoImgs.activeUrl) {
+                    // setImgMaterialConfig({ ...imgMaterialConfig, list: [{ url: videoImgs.activeUrl }] })
+                    set_videoImgsVisbile(false)
+                } else {
+                    message.error('请选择图片,获取使用取消按钮关闭弹窗!')
+                }
+            }}
+            onCancel={() => { set_videoImgsVisbile(false) }}
+            confirmLoading={getVideoCapture.loading}
+            width={600}
+        >
+
+            <Radio.Group className={styles.videoImgs} onChange={(e) => {
+                let url = e.target.value
+                set_videoImgs({ ...videoImgs, activeUrl: url })
+            }}>
+                {
+                    videoImgs?.urlList?.map((item: any, index: number) => {
+                        return <Radio.Button value={item} key={index}>
+                            <Image src={item} preview={videoImgs.preview} />
+                        </Radio.Button>
+                    })
+                }
+            </Radio.Group>
+        </Modal>}
+    </>
+}
+
+export default React.memo(Material)

+ 152 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/tableConfig.tsx

@@ -0,0 +1,152 @@
+import React from "react"
+import { Image, Popconfirm, Space } from 'antd'
+
+let brandColumns = (del: (id: number) => void, edit: (data: any) => void) => {
+
+
+    let data: any[] = [
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                return <Space>
+                    <a onClick={() => edit(b)}>修改</a>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del(b.id)}
+                        okText="是"
+                        cancelText="否"
+                    >
+                        <a style={{ color: 'red' }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            width: 60,
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '头像预览图',
+            dataIndex: 'brandImgUrl',
+            key: 'brandImgUrl',
+            width: 120,
+            ellipsis: true,
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <Image width={40} style={{ borderRadius: 4 }} src={a} />
+            }
+        },
+        {
+            title: '头像名称',
+            dataIndex: 'name',
+            key: 'name',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        }
+    ]
+
+    return data
+}
+
+let profileColumns = (del: (id: number) => void, edit: (data: any) => void) => {
+
+
+    let data: any[] = [
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                return <Space>
+                    <a onClick={() => edit(b)}>修改</a>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del(b.id)}
+                        okText="是"
+                        cancelText="否"
+                    >
+                        <a style={{ color: 'red' }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            width: 60,
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '头像预览图',
+            dataIndex: 'headImageUrl',
+            key: 'headImageUrl',
+            width: 120,
+            ellipsis: true,
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <Image width={40} style={{ borderRadius: 4 }} src={a} />
+            }
+        },
+        {
+            title: '头像名称',
+            dataIndex: 'profileName',
+            key: 'profileName',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '详细描述',
+            dataIndex: 'description',
+            key: 'description',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        }
+    ]
+
+    return data
+}
+
+export {
+    brandColumns,
+    profileColumns
+}

+ 49 - 3
src/pages/launchSystemNew/launchManage/createAd/index.less

@@ -5,6 +5,7 @@
     font-weight: 600;
   }
 }
+
 // #page_tabs,#ad_tabs{
 //   >div>div>div>div{
 //     padding: 5px;
@@ -84,7 +85,8 @@
             font-size: 12px;
             color: rgb(90, 90, 90);
           }
-          >a{
+
+          >a {
             font-size: 12px;
             font-weight: 400;
           }
@@ -133,12 +135,14 @@
 
             .close {
               cursor: pointer;
-              margin-right: 10px;
+              margin-right: 4px;
               display: none;
               position: absolute;
               right: 4px;
               top: 50%;
               transform: translateY(-50%);
+              color: #0e95f6;
+              box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
             }
 
             &:hover {
@@ -157,6 +161,7 @@
           line-height: 40px;
           display: flex;
           justify-content: center;
+
           span {
             cursor: pointer;
             color: #108ee9;
@@ -177,6 +182,7 @@
     justify-content: flex-end;
   }
 }
+
 .popoverContent {
   border: 1px solid #dcdee2;
   padding: 5px;
@@ -184,11 +190,13 @@
   transition: all 0.5s;
   margin-top: 1px;
   margin-bottom: 5px;
+
   &:hover {
     border-color: rgb(24, 144, 255);
     box-shadow: 0 0 4px 1px rgba(24, 144, 255, 0.4);
   }
 }
+
 .popover {
   max-width: 450px;
   max-height: 150px;
@@ -204,4 +212,42 @@
   -webkit-line-clamp: 2;
   line-clamp: 2;
   -webkit-box-orient: vertical;
-}
+}
+
+
+.group {
+  display: flex;
+  justify-content: space-between;
+  gap: 5px;
+  margin-bottom: 10px;
+
+  >div {
+    flex: 1;
+
+    >img {
+      width: 100%;
+    }
+
+    >video {
+      width: 100%;
+    }
+  }
+}
+
+.otherGroup {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+
+  .group {
+    width: 35%;
+
+    >img {
+      width: 100%;
+    }
+
+    >video {
+      width: 100%;
+    }
+  }
+}

+ 321 - 101
src/pages/launchSystemNew/launchManage/createAd/index.tsx

@@ -6,8 +6,8 @@ import { PromotedObjectType } from "@/services/launchAdq/enum"
 import { getTagsList } from "@/services/launchAdq/global"
 import { getSysAdgroupsInfo } from "@/services/launchAdq/localAd"
 import { getsysTargetingInfo } from "@/services/launchAdq/targeting"
-import { CloseOutlined, SearchOutlined } from "@ant-design/icons"
-import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Tabs } from "antd"
+import { CheckOutlined, CloseOutlined, SearchOutlined } from "@ant-design/icons"
+import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Tabs, Popconfirm, notification } from "antd"
 import React, { useCallback, useEffect, useState } from "react"
 import { useModel } from "umi"
 import Ad from "./ad"
@@ -17,7 +17,6 @@ import IdModal from "../../components/idModal"
 import LookLanding from "../../components/lookLanding"
 import PageModal from "../../components/pageModal"
 import SelectCloud from "../../components/selectCloud"
-import { WxAutoButton } from "../../req"
 import style from './index.less'
 import Selector from "./selector"
 import SubmitModal from "./submitModal"
@@ -26,6 +25,8 @@ import TargetIng from './targeting'
 import Creative from './creative'
 import AddGroup from '../../components/addGroup'
 import CustomerServiceModal from "../../components/customerServiceModal"
+import { getTaskDetailsApi } from "@/services/launchAdq/taskList"
+import CreativeCL from "./creativeCL"
 
 const CreateAd: React.FC = () => {
 
@@ -49,7 +50,8 @@ const CreateAd: React.FC = () => {
         expandEnabled: false,
         expandTargeting: []
     })
-    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: number, productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any, pageList?: any, coldStartAudienceList?: any[] }[]>([])  // 账户
+    const [launchMode, setLaunchMode] = useState<number>(Number(localStorage.getItem('LAUNCHMODE')) || 1) // 投放模式 1 现在投放模式  2 创量模式
+    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: any[], productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any, pageList?: any, coldStartAudienceList?: any[] }[]>([])  // 账户
     const { currentUser: { userId } }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
 
     const [goodsVisible, setGoodsVisible] = useState<boolean>(false) // 选择商品弹窗控制
@@ -76,15 +78,66 @@ const CreateAd: React.FC = () => {
     const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
     const getSysAdcreative = useAjax((params) => getSysAdcreativeInfo(params))
     const createAdBatch = useAjax((params) => createAdBatchApi(params))
+    const getTaskDetails = useAjax((params) => getTaskDetailsApi(params))
     /*************************/
 
     /**数据回填 */
     useEffect(() => {
-        let adqAdData = localStorage.getItem('ADQAD')
-        if (adqAdData) {
-            const { queryForm, accountCreateLogs } = JSON.parse(adqAdData)
-            setQueryForm({ ...queryForm })
-            setAccountCreateLogs(accountCreateLogs)
+        let taskId = sessionStorage.getItem('TASKID')
+        if (taskId) {
+            getTaskDetails.run(taskId).then(res => {
+                console.log('res----->', res)
+                const { adCreateLogs, campaignType, promotedObjectType, speedMode, sysAdgroup, sysAdgroupId, sysTargeting, sysTargetingId } = res
+                setAccountCreateLogs(adCreateLogs?.map((item: any) => {
+
+                    return { adAccountId: item?.accountId, id: item?.adAccountId }
+                }))
+                let taskMediaMaps = adCreateLogs?.map((item: any) => {
+                    let pageElementsSpecList = item?.sysPage?.pageSpecsList[0]?.pageElementsSpecList // 内容区
+                    let globalSpec = item?.sysPage?.globalSpec  // 悬浮组件
+                    /** 处理客服 */
+                    let cropUserGroupMap: any[] = []
+                    // if ((pageElementsSpecList as any[])?.some((item: { elementType: string }) => item?.elementType === 'ENTERPRISE_WX') || (globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList?.some((item: { floatButtonSpec: { elementType: string } }) => item?.floatButtonSpec?.elementType === 'ENTERPRISE_WX'))) {
+                    //     let groupList: { type: number, name: string, cropList: any[], cropId?: number, groupId?: number }[] = [];
+                    //     (pageElementsSpecList as any[])?.forEach((item: { elementType: string, enterpriseWxSpec: { btnTitle: string } }) => {
+                    //         if (item?.elementType === 'ENTERPRISE_WX') {
+                    //             groupList.push({ type: 1, name: '联系商家', cropList: [] }) // item.enterpriseWxSpec.btnTitle
+                    //         }
+                    //     })
+                    //     if ((globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList)) {
+                    //         groupList.push({ type: 2, name: '悬浮组件', cropList: [] })
+                    //     }
+                    //     cropUserGroupMap = adCreateLogs?.map((item: any) => ({ adAccountId: item.accountId, id: item.adAccountId, data: groupList }))
+                    // }
+                    return { sysAdcreative: item?.sysAdcreative, sysPageId: item?.sysPageId, cropUserGroupMap }
+                })
+                let pageList = adCreateLogs?.map((item: any) => {
+                    return item?.sysPage || null
+                })
+                setQueryForm({
+                    ...queryForm,
+                    campaignType,
+                    promotedObjectType,
+                    speedMode,
+                    sysAdgroup,
+                    sysAdgroupId,
+                    sysTargeting,
+                    sysTargetingId,
+                    adgroupName: sysAdgroup?.adgroupName,
+                    configuredStatus: sysAdgroup?.configuredStatus,
+                    expandEnabled: sysAdgroup?.expandEnabled || false,
+                    expandTargeting: sysAdgroup?.expandTargeting || [],
+                    taskMediaMaps: taskMediaMaps || [],
+                    pageList,
+                })
+            })
+        } else {
+            let adqAdData = localStorage.getItem('ADQAD')
+            if (adqAdData) {
+                const { queryForm, accountCreateLogs } = JSON.parse(adqAdData)
+                setQueryForm({ ...queryForm })
+                setAccountCreateLogs(accountCreateLogs)
+            }
         }
     }, [])
     // 设置地域
@@ -112,6 +165,31 @@ const CreateAd: React.FC = () => {
         getAdAccount.run()
     }, [])
 
+    // 账号对比
+    useEffect(() => {
+        if (getAdAccount?.data?.data && accountCreateLogs) {
+            if (accountCreateLogs.some(item => !getAdAccount?.data?.data?.find((item1: { id: number, accountId: number }) => item.id === item1.id && item.adAccountId == item1.accountId))) {
+                let errorData: any[] = []
+                let newAccountCreateLogs = accountCreateLogs.filter(item => {
+                    let data = getAdAccount?.data?.data?.find((item1: { id: number, accountId: number }) => item.id === item1.id && item.adAccountId == item1.accountId)
+                    if (data) {
+                        return true
+                    } else {
+                        errorData.push(item.adAccountId)
+                        return false
+                    }
+                })
+                notification.error({
+                    duration: 60 * 5,
+                    message: '重要提示',
+                    description: `本地媒体账户与你所拥有账户对不上,当前创建账号不符合账号及部分相关配置已清空。请把保存在本地的媒体账户或者媒体账户组清空,重新选择保存。问题账户:(${errorData.toString()})`
+                })
+                setAccountCreateLogs(newAccountCreateLogs)
+                setQueryForm({ ...queryForm, adqPageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map(item => ({ ...item, accountPageIdMap: {} })) })
+            }
+        }
+    }, [getAdAccount?.data, accountCreateLogs, queryForm])
+
     /** 获取广告详情 */
     useEffect(() => {
         if (getSysAdgroups?.data?.bidMode !== 'BID_MODE_CPM' && accountCreateLogs?.length > 0) {
@@ -126,8 +204,6 @@ const CreateAd: React.FC = () => {
     }, [getSysAdgroups?.data?.bidMode])
 
 
-
-
     /** 删除商品内容 */
     const goodsDel = (index: number) => {
         let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
@@ -135,13 +211,6 @@ const CreateAd: React.FC = () => {
         setAccountCreateLogs(newArr)
     }
 
-    /** 删除云端内容 */
-    const pageDel = (index: number) => {
-        let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
-        delete newArr[index].pageList
-        setAccountCreateLogs(newArr)
-    }
-
     /** 删除数据源 */
     const sourceDel = (index: number, num: number) => {
         let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
@@ -156,36 +225,51 @@ const CreateAd: React.FC = () => {
         setAccountCreateLogs(newArr)
     }
 
+    // 创意素材与文案叉乘处理
+    const whatever = (...arrs: any[]) => {
+        return arrs.reduce((total, curr) => total.flatMap((e: any) => curr.map((e2: any) => ({ ...e2, ...e }))))
+    }
 
 
     /** 预览 */
     const preview = () => {
+        let newQueryForm: Partial<CreateAdProps> = JSON.parse(JSON.stringify(queryForm))
         if (accountCreateLogs?.length === 0) {
             message.error('请选择媒体账户')
             return
         }
-        if (!queryForm.promotedObjectType) {
+        if (!newQueryForm.promotedObjectType) {
             message.error('请选择推广目标')
             return
         }
-        if (!queryForm.sysAdgroup) {
+        if (!newQueryForm.sysAdgroup) {
             message.error('请先设置广告基本信息')
             return
         }
-        if (!queryForm.sysTargeting) {
+        if (!newQueryForm.sysTargeting) {
             message.error('请选择定向')
             return
         }
-        if (!queryForm.taskMediaMaps?.every(item => item.sysAdcreative)) {
+        if (!newQueryForm.taskMediaMaps?.every(item => item.sysAdcreative)) {
             message.error('请设置创意的基本信息')
             return
         }
-        if (!queryForm.taskMediaMaps?.every(item => item.sysPageId || item.accountPageIdMap)) {
+        if (!newQueryForm.taskMediaMaps?.every(item => item.sysPageId || item.accountPageIdMap)) {
             message.error('请选择落地页')
             return
         }
-        if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps?.some((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)) {
-            let cropData = queryForm?.taskMediaMaps?.filter((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)
+        if (launchMode === 2) {
+            if (!(newQueryForm?.materials && newQueryForm?.materials?.length > 0)) {
+                message.error('请选择创意素材')
+                return
+            }
+            if (!(newQueryForm?.texts && newQueryForm?.texts?.length > 0)) {
+                message.error('请选择创意文案')
+                return
+            }
+        }
+        if (newQueryForm?.taskMediaMaps && newQueryForm?.taskMediaMaps?.some((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)) {
+            let cropData = newQueryForm?.taskMediaMaps?.filter((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)
             if (cropData?.some((item: { cropUserGroupMap: { data: { cropList: any[] }[] }[] }) => {
                 return item?.cropUserGroupMap?.some((item1: { data: { cropList: any[] }[] }) => item1?.data?.some((item2: { cropList: any[] }) => item2?.cropList?.length === 0))
             })) {
@@ -194,27 +278,48 @@ const CreateAd: React.FC = () => {
             }
         }
         let data: any[] = []
+        if (launchMode === 2) {
+            if (newQueryForm?.materials && Array.isArray(newQueryForm.materials) && newQueryForm?.texts && Array.isArray(newQueryForm?.texts)) {
+                let taskMediaMap = JSON.parse(JSON.stringify(newQueryForm.taskMediaMaps[0]))
+                let adcreativeElements = taskMediaMap.sysAdcreative?.adcreativeElements || {}
+                let newTaskMediaMaps = whatever(newQueryForm.materials, newQueryForm.texts).map((item: any) => {
+                    taskMediaMap.sysAdcreative.adcreativeElements = { ...adcreativeElements, ...item }
+                    return JSON.parse(JSON.stringify(taskMediaMap))
+                })
+                newQueryForm.taskMediaMaps = newTaskMediaMaps
+            }
+        }
         accountCreateLogs.forEach((item: any) => {
-            queryForm.taskMediaMaps?.forEach((task, index) => {
+            newQueryForm.taskMediaMaps?.forEach((task, index) => {
                 let obj = {
                     ...item,
-                    ...queryForm,
-                    sysAdGroupData: queryForm.sysAdgroup,
-                    targetingData: queryForm.sysTargeting,
+                    ...newQueryForm,
+                    sysAdGroupData: newQueryForm.sysAdgroup,
+                    targetingData: newQueryForm.sysTargeting,
                     sysAdcreativeData: task.sysAdcreative,
-                    pageData: (queryForm.pageList as any)[index] || (queryForm.adqPageList as any)[index]?.find((adq: { adAccountId: any }) => adq.adAccountId === item.adAccountId)?.pageList[0],
+                    pageData: launchMode === 2 ? (newQueryForm.pageList as any)[0] || (newQueryForm.adqPageList as any)[0]?.find((adq: { adAccountId: any }) => adq.adAccountId === item.adAccountId)?.pageList[0] : (newQueryForm.pageList as any)[index] || (newQueryForm.adqPageList as any)[index]?.find((adq: { adAccountId: any }) => adq.adAccountId === item.adAccountId)?.pageList[0],
                     myId: Number(item.id + '' + index)
                 }
                 data.push(obj)
             })
         })
-        console.log('tableData--->', data)
         setTableData(data)
     }
 
     const submit = (props: { campaignName: string, count?: number }) => {
         console.log(111111, tableSelect);
         let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+        if (launchMode === 2) {
+            if (newQueryForm?.materials && Array.isArray(newQueryForm.materials) && newQueryForm?.texts && Array.isArray(newQueryForm?.texts)) {
+                let taskMediaMap = JSON.parse(JSON.stringify(newQueryForm.taskMediaMaps[0]))
+                let adcreativeElements = taskMediaMap.sysAdcreative?.adcreativeElements || {}
+                let newTaskMediaMaps = whatever(newQueryForm.materials, newQueryForm.texts).map((item: any) => {
+                    taskMediaMap.sysAdcreative.adcreativeElements = { ...adcreativeElements, ...item }
+                    return JSON.parse(JSON.stringify(taskMediaMap))
+                })
+                newQueryForm.taskMediaMaps = newTaskMediaMaps
+            }
+        }
         let newtaskMediaMaps = newQueryForm.taskMediaMaps.map((item1: { cropUserGroupMap?: any[] }) => {
             let { cropUserGroupMap, ...data } = item1
             if (cropUserGroupMap && cropUserGroupMap?.length > 0) {
@@ -284,8 +389,11 @@ const CreateAd: React.FC = () => {
         delete params.count
         delete params?.expandEnabled
         delete params?.expandTargeting
+        delete params?.texts
+        delete params?.textData
+        delete params?.materialData
+        delete params?.materials
         console.log('paramsSubmit====>', params)
-
         createAdBatch.run(params).then(res => {
             if (res) {
                 sessionStorage.setItem('CAMP', props?.campaignName)
@@ -444,11 +552,34 @@ const CreateAd: React.FC = () => {
         setUsersArr(data)
     }, [])
 
-    console.log('queryForm111111', queryForm);
+    // 切换投放模式
+    const switchLaunchMode = () => {
+        if (launchMode === 1) {
+            setLaunchMode(2)
+            localStorage.setItem('LAUNCHMODE', '2')
+        } else {
+            setLaunchMode(1)
+            localStorage.setItem('LAUNCHMODE', '1')
+        }
+        delBdPlan()
+        set_targetKey('0')
+    }
+
+    console.log('queryForm111111', queryForm, accountCreateLogs);
 
     return <Space direction="vertical" style={{ width: '100%' }}>
         <Card
-            title={<div className={style.cardTitle}>配置区</div>}
+            title={<Space>
+                <div className={style.cardTitle}>配置区</div>
+                <Popconfirm
+                    title="数据部分不会保存,是否切换?"
+                    onConfirm={switchLaunchMode}
+                    okText="是"
+                    cancelText="否"
+                >
+                    <Button type="link" style={{ padding: 0 }}>切换投放模式</Button>
+                </Popconfirm>
+            </Space>}
             className={style.createAd}
             hoverable
             extra={<AddGroup onChange={usersChange} pitcherData={getAdAccount?.data?.data} />}
@@ -496,7 +627,7 @@ const CreateAd: React.FC = () => {
                         value={accountCreateLogs?.map((item: { id: number }) => item?.id)}
                         onChange={(e, option) => {
                             console.log(option)
-                            setQueryForm({ ...queryForm, taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '' })) })
+                            setQueryForm({ ...queryForm, adqPageList: [], pageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '', accountPageIdMap: {} })) })
                             setAccountCreateLogs(option?.map((item: any) => ({ adAccountId: item?.children?.toString()?.split('——')[0], id: item?.value })))
                             clearData()
                         }}
@@ -507,17 +638,21 @@ const CreateAd: React.FC = () => {
                 <Selector label="推广目标">
                     <Select style={{ width: 200 }} value={queryForm?.promotedObjectType} placeholder="请选择推广目标" bordered={false} showSearch filterOption={(input: any, option: any) =>
                         (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
-                    } onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e, sysAdgroup: null, sysAdgroupId: undefined, taskMediaMaps: [], sysAdcreativeId: undefined }); clearData() }}>
+                    } onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e, sysAdgroup: null, sysAdgroupId: undefined, taskMediaMaps: [], sysAdcreativeId: undefined, materials: [], textData: [], texts: [] }); clearData() }}>
                         {Object.keys(PromotedObjectType).map(key => {
                             return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
                         })}
                     </Select>
                 </Selector>
+                {launchMode === 2 && accountCreateLogs?.length > 0 && <>
+                    <Button onClick={() => { setGoodsVisible(true) }}>商品广告(选填){accountCreateLogs?.some(item => item?.productList?.length) && <CheckOutlined style={{ color: '#1890ff' }} />}</Button>
+                    <Button onClick={() => { setSourceVisible(true) }}>精准匹配归因(选填){accountCreateLogs?.some(item => item?.userActionSetsList?.length) && <CheckOutlined style={{ color: '#1890ff' }} />}</Button>
+                </>}
             </Space>
 
             <div className={style.cardBody}>
                 <Row className={style.content}>
-                    <Col span={12} className={style.conLeft}>
+                    <Col span={launchMode === 1 ? 12 : 8} className={style.conLeft}>
                         <Row className={`${style.conTitle} ${style.conRightBorder}`}><Col span={24}>广告</Col></Row>
                         <Row className={style.items}>
                             {/* =============广告基本信息=========== */}
@@ -535,73 +670,76 @@ const CreateAd: React.FC = () => {
                                 cpDel={cpDel}
                                 accountCreateLogs={accountCreateLogs}
                             />
-                            {/* =============商品=========== */}
-                            <Col className={style.conRightBorder} span={5}>
-                                <div className={style.top}>
-                                    商品{/* <span>已选:{1}</span> */}
-                                </div>
-                                <div className={style.center}>
-                                    <div className={style.centerContent}>
-                                        {accountCreateLogs?.map((item: any, index: number) => {
-                                            if (item?.productList) {
-                                                return <div className={style.acc} key={index}>
-                                                    <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
-                                                    {
-                                                        item?.productList?.map((pack: { productName: string, author: string, id: number }, index: number) => {
-                                                            return <div className={style.accCon} key={pack.id}>{pack.productName}<CloseOutlined className={style.close} onClick={() => {
-                                                                goodsDel(index)
-                                                            }} /></div>
-                                                        })
-                                                    }
-                                                </div>
-                                            } else {
-                                                return null
-                                            }
-                                        })}
+
+                            {launchMode === 1 && <>
+                                {/* =============商品=========== */}
+                                <Col className={style.conRightBorder} span={5}>
+                                    <div className={style.top}>
+                                        商品
                                     </div>
-                                </div>
-                                <div className={style.bottom}>
-                                    {accountCreateLogs?.length > 0 ? <span onClick={() => { setGoodsVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
-                                        <span>编辑</span>
-                                    </Tooltip>}
-                                </div>
-                            </Col>
-                            {/* 数据源 */}
-                            <Col className={style.conRightBorder} span={5}>
-                                <div className={style.top}>
-                                    数据源 {/* <span>已选:{1}</span> */}
-                                </div>
-                                <div className={style.center}>
-                                    {/* userActionSetsList */}
-                                    <div className={style.centerContent}>
-                                        {accountCreateLogs?.map((item: any, index: number) => {
-                                            if (item?.userActionSetsList && item?.userActionSetsList?.length > 0) {
-                                                return <div className={style.acc} key={index}>
-                                                    <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
-                                                    {
-                                                        item?.userActionSetsList?.map((pack: { name: string, type: string, id: number }, index1: number) => {
-                                                            return <div className={style.accCon} key={pack.id}> <span className={style.title}>{pack.name}{' > '}{pack.type?.replace('USER_ACTION_SET_TYPE_', '')}</span> <CloseOutlined className={style.close} onClick={() => {
-                                                                sourceDel(index, index1)
-                                                            }} /></div>
-                                                        })
-                                                    }
-                                                </div>
-                                            } else {
-                                                return null
-                                            }
-                                        })}
+                                    <div className={style.center}>
+                                        <div className={style.centerContent}>
+                                            {accountCreateLogs?.map((item: any, index: number) => {
+                                                if (item?.productList) {
+                                                    return <div className={style.acc} key={index}>
+                                                        <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
+                                                        {
+                                                            item?.productList?.map((pack: { productName: string, author: string, id: number }, index: number) => {
+                                                                return <div className={style.accCon} key={pack.id}>{pack.productName}<CloseOutlined className={style.close} onClick={() => {
+                                                                    goodsDel(index)
+                                                                }} /></div>
+                                                            })
+                                                        }
+                                                    </div>
+                                                } else {
+                                                    return null
+                                                }
+                                            })}
+                                        </div>
                                     </div>
-                                </div>
-                                <div className={style.bottom}>
-                                    {accountCreateLogs?.length > 0 ? <span onClick={() => { setSourceVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
-                                        <span>编辑</span>
-                                    </Tooltip>}
-                                </div>
-                            </Col>
+                                    <div className={style.bottom}>
+                                        {accountCreateLogs?.length > 0 ? <span onClick={() => { setGoodsVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
+                                            <span>编辑</span>
+                                        </Tooltip>}
+                                    </div>
+                                </Col>
+
+                                {/* 数据源 */}
+                                <Col className={style.conRightBorder} span={5}>
+                                    <div className={style.top}>
+                                        数据源
+                                    </div>
+                                    <div className={style.center}>
+                                        <div className={style.centerContent}>
+                                            {accountCreateLogs?.map((item: any, index: number) => {
+                                                if (item?.userActionSetsList && item?.userActionSetsList?.length > 0) {
+                                                    return <div className={style.acc} key={index}>
+                                                        <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
+                                                        {
+                                                            item?.userActionSetsList?.map((pack: { name: string, type: string, id: number }, index1: number) => {
+                                                                return <div className={style.accCon} key={pack.id}> <span className={style.title}>{pack.name}{' > '}{pack.type?.replace('USER_ACTION_SET_TYPE_', '')}</span> <CloseOutlined className={style.close} onClick={() => {
+                                                                    sourceDel(index, index1)
+                                                                }} /></div>
+                                                            })
+                                                        }
+                                                    </div>
+                                                } else {
+                                                    return null
+                                                }
+                                            })}
+                                        </div>
+                                    </div>
+                                    <div className={style.bottom}>
+                                        {accountCreateLogs?.length > 0 ? <span onClick={() => { setSourceVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
+                                            <span>编辑</span>
+                                        </Tooltip>}
+                                    </div>
+                                </Col>
+                            </>}
                         </Row>
                     </Col>
                     {/* =============广告创意=========== */}
-                    <Col span={12} className={style.conRight}>
+                    {launchMode === 1 ? <Col span={12} className={style.conRight}>
                         <Row className={style.conTitle}><Col span={24}>广告创意</Col></Row>
                         <Row className={style.items}>
                             {/* 创意 */}
@@ -676,7 +814,7 @@ const CreateAd: React.FC = () => {
                                                 init({ mediaType: 'PAGE', cloudSize: undefined })
                                             }
                                         }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId ? '修改' : '选择落地页'}</Button>
-                                        
+
                                         {accountCreateLogs?.length > 0 ? <Button type="link" onClick={() => {
                                             setPageVisible(true)
                                             if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
@@ -693,7 +831,89 @@ const CreateAd: React.FC = () => {
                                 </div>
                             </Col>
                         </Row>
-                    </Col>
+                    </Col> : <Col span={16} className={style.conRight}>
+                        <Row className={style.conTitle}><Col span={24}>广告创意</Col></Row>
+                        <Row className={style.items}>
+                            {/* 创意 */}
+                            <CreativeCL queryForm={queryForm} setQueryForm={setQueryForm} clearData={clearData} getSysAdcreative={getSysAdcreative} targetKey={targetKey} />
+                            {/* 落地页 */}
+                            <Col className={style.conRightBorder} style={{ maxWidth: '25%', border: 'none' }}>
+                                <div className={style.top}>
+                                    落地页
+                                    {(queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.cropUserGroupMap?.length > 0) && <CustomerServiceModal data={queryForm?.taskMediaMaps[targetKey]?.cropUserGroupMap} onChange={(data) => {
+                                        let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                                        newQueryForm.taskMediaMaps[targetKey].cropUserGroupMap = data
+                                        setQueryForm(newQueryForm)
+                                    }} />}
+                                </div>
+                                <div className={style.center}>
+                                    {queryForm?.taskMediaMaps?.filter((item, index) => index === 0)?.map((item, index) => {
+                                        return <Spin spinning={get.loading} key={index}>
+                                            <div className={style.centerContent}>
+                                                {item?.sysPageId || item?.accountPageIdMap ? <>
+                                                    {(item?.sysPageId && queryForm?.pageList) && <>
+                                                        <div>落地页名称:{queryForm?.pageList[targetKey]?.pageName || ''}</div>
+                                                        <div>分享名称:{queryForm?.pageList[targetKey]?.shareContentSpec?.shareTitle || ''}</div>
+                                                        <div>分享描述:{queryForm?.pageList[targetKey]?.shareContentSpec?.shareDescription || ''}</div>
+                                                        <div style={{ marginBottom: 10 }}>原生推广页顶部素材预览:
+                                                            <div>{queryForm?.pageList[targetKey]?.pageSpecsList && queryForm?.pageList[targetKey]?.pageSpecsList[0]?.pageElementsSpecList?.filter((item: any, index: number) => index === 0)?.map((item: { elementType: 'TOP_IMAGE' | 'TOP_VIDEO' | 'TOP_SLIDER', topImageSpec: any, topSliderSpec: any, topVideoSpec: any }, index: number) => {
+                                                                switch (item?.elementType) {
+                                                                    case 'TOP_IMAGE':
+                                                                        return <Image width={80} src={item?.topImageSpec?.imageUrl} style={{ borderRadius: 8, overflow: 'hidden' }} key={index} />
+                                                                    case 'TOP_SLIDER':
+                                                                        return <Space wrap key={index}>
+                                                                            {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={70} src={url} style={{ borderRadius: 8 }} key={'TOP_SLIDER' + index} />)}
+                                                                        </Space>
+                                                                    case 'TOP_VIDEO':
+                                                                        return <video src={item?.topVideoSpec?.videoUrl} width={150} controls key={index}></video>
+                                                                }
+                                                            })}</div>
+                                                        </div>
+                                                    </>}
+                                                    {queryForm?.adqPageList && queryForm?.adqPageList[targetKey]?.map((adq: any) => {
+                                                        return <div className={style.acc} key={adq.adAccountId}>
+                                                            <div className={style.accName} style={{ fontWeight: 800 }}>{adq.adAccountId}</div>
+                                                            <div className={style.accCon}>
+                                                                <span className={style.title}>{adq.pageList[0].pageName}</span>
+                                                            </div>
+                                                        </div>
+                                                    })}
+                                                </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
+                                            </div>
+                                        </Spin>
+                                    })}
+                                </div>
+                                <div className={style.bottom}>{
+                                    (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative) ? <>
+                                        {queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId && <Button type="link" onClick={() => { setLookVisible(true) }}>查看</Button>}
+                                        <Button type="link" onClick={() => {
+                                            setSelectImgVisible(true)
+                                            // 判定是否用原生页顶部替换外部素材
+                                            if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
+                                                init({ mediaType: 'PAGE', cloudSize: undefined, adcreativeTemplateId: queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.adcreativeTemplateId })
+                                            } else {
+                                                init({ mediaType: 'PAGE', cloudSize: undefined })
+                                            }
+                                        }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId ? '修改' : '选择落地页'}</Button>
+
+                                        {accountCreateLogs?.length > 0 ? <Button type="link" onClick={() => {
+                                            setPageVisible(true)
+                                            if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
+                                                setCloudParams({ adcreativeTemplateId: queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.adcreativeTemplateId })
+                                            } else {
+                                                setCloudParams({})
+                                            }
+                                        }}>云端落地页</Button> : <Tooltip title="请先选择媒体账户">
+                                            <Button type="link">云端落地页</Button>
+                                        </Tooltip>}
+                                    </> : <Tooltip title="请先设置创意">
+                                        <Button type="link"><span>选择落地页</span></Button>
+                                    </Tooltip>}
+                                </div>
+                            </Col>
+                        </Row>
+                    </Col>}
+
                 </Row>
                 {/* =============广告底部按钮=========== */}
                 <Space className={style.bts}>

+ 5 - 1
src/pages/launchSystemNew/launchManage/createAd/tableConfig.tsx

@@ -41,7 +41,11 @@ let columns = () => {
             align: 'center',
             width: 120,
             render: (a: any, b: any) => {
-                return <span style={{ fontSize: "12px" }}>{a?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span>
+                return <div className={style.twoText}>
+                    <Tooltip title={a?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}>
+                        <span style={{ fontSize: "12px" }}>{a?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span>
+                    </Tooltip>
+                </div>
             }
         },
         {

+ 10 - 3
src/pages/launchSystemNew/launchManage/createAd/targeting/index.tsx

@@ -1,7 +1,7 @@
 import { CreateAdProps } from "@/services/launchAdq/createAd"
 import { BaseResult } from "@ahooksjs/use-request/lib/types"
 import { CloseOutlined, DownOutlined } from "@ant-design/icons"
-import { Button, Col, Dropdown, Empty, Menu, Popover, Space, Spin, Tooltip } from "antd"
+import { Button, Col, Dropdown, Empty, Menu, message, Popover, Space, Spin, Tooltip } from "antd"
 import React, { useCallback, useEffect, useState } from "react"
 import TargetingModal from "../../../components/targetingModal"
 import TargetingPup from "./modal"
@@ -17,7 +17,7 @@ type Props = {
     setAccountCreateLogs: React.Dispatch<React.SetStateAction<{
         adAccountId: number;
         id: number;
-        userActionSetsList?: number | undefined;
+        userActionSetsList?: any[];
         productList?: any;
         conversionList?: any;
         customAudienceList?: any;
@@ -202,7 +202,14 @@ function TargetIng(props: Props) {
         />}
         {/* 新建定向包 */}
         {adModalConfig.visible && <TargetingPup visible={adModalConfig.visible} PupFn={handleAdModalConfig} callback={(values: any) => {
-            setQueryForm({ ...queryForm, sysTargeting: values, }); setDxVisible(false); clearData();
+            if (queryForm?.expandEnabled && queryForm?.expandTargeting && queryForm?.expandTargeting?.length > 0) {
+                message.error('不可突破定向已重置,需要请重新设置')
+                setQueryForm({ ...queryForm, sysTargeting: values, expandTargeting: [] }); 
+            } else {
+                setQueryForm({ ...queryForm, sysTargeting: values }); 
+            }
+            setDxVisible(false); 
+            clearData();
             handleAdModalConfig({ visible: false, dataInfo: null, type: 'add' })
         }} type={adModalConfig.type} dataInfo={queryForm.sysTargeting} />}
     </Col>

+ 6 - 1
src/pages/launchSystemNew/launchManage/taskList/index.tsx

@@ -34,13 +34,18 @@ const TaskList: React.FC = () => {
         getTaskList.run(queryForm)
     }
 
-    const callback = (data: any, type: 'log' | 'page', allData?: any) => {
+    const callback = (data: any, type: 'log' | 'page' | 'copy', allData?: any) => {
         switch (type) {
             case 'log':
                 setLogData({ ...data })
                 setLogVisible(true)
                 setAllData(allData)
                 break
+            case 'copy':
+                console.log('111111111111--->', data);
+                sessionStorage.setItem('TASKID', data.taskId)
+                window.location.href = '/#/launchSystemNew/launchManage/createAd'
+                break
         }
     }
 

+ 3 - 2
src/pages/launchSystemNew/launchManage/taskList/tableConfig.tsx

@@ -3,17 +3,18 @@ import { Badge, Space } from "antd"
 import { AdStatus, PromotedObjectType, SpeedMode } from "@/services/launchAdq/enum"
 import TargetingPopover from "../../components/targetingPopover"
 import AdPopover from "../../components/adPopover"
-function tableConfig(callback: (data: any, type: 'log' | 'page', allData?: any) => void): any {
+function tableConfig(callback: (data: any, type: 'log' | 'page' | 'copy', allData?: any) => void): any {
     return [
         {
             title: '操作',
             dataIndex: 'taskName',
             key: 'taskName',
-            width: 60,
+            width: 80,
             align: 'center',
             render: (a: any, b: any) => {
                 return <Space>
                     <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback({ taskId: b.id, campaignName: b.campaignName }, 'log', b) }}>日志</a>
+                    <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback({ taskId: b.id, campaignName: b.campaignName }, 'copy') }}>复制</a>
                 </Space>
             }
         },

+ 4 - 0
src/services/launchAdq/createAd.ts

@@ -28,6 +28,10 @@ export interface CreateAdProps {
   bidAmount?: number, // 出价
   expandEnabled?: boolean,  // 自动扩量
   expandTargeting?: string[], // 扩量不可突破定向
+  materialData?: { label: string, name: string, restriction: any, arrayProperty?: any }[]  // 创量模式素材
+  materials?: any[],  // 素材
+  textData?: any[], // 文案所需参数
+  texts?: any[],   // 文案列表
   accountCreateLogs: {
     adAccountId: number, // 媒体账户ID
     userActionSets?: {

+ 5 - 1
src/services/launchAdq/taskList.ts

@@ -19,7 +19,11 @@ export async function getTaskListApi(data: TaskListProps) {
     });
 }
 
-
+export async function getTaskDetailsApi(taskId: number) {
+    return request(api + `/adq/adCreateTask/${taskId}`, {
+        method: 'GET'
+    });
+}
 
 /**
  * 任务日志列表