Forráskód Böngészése

Merge branch 'wangjianxin' of http://git.zanxiangnet.com/wjx/ad-manage into new

shenwu 2 éve
szülő
commit
8cb864c7cf

+ 111 - 108
src/components/FileBoxAD/index.tsx

@@ -193,7 +193,7 @@ function FlieBox(props: Props) {
             <div className={style.files} onContextMenu={rightMenu} style={height ? { height } : {}}>
                 {/* 关联公众号筛选 */}
                 <div className={style.wxSelect}>
-                    
+
                 </div>
                 {/* 层级路径 */}
                 <div className={style.path} >
@@ -215,120 +215,122 @@ function FlieBox(props: Props) {
                     }
                 </div>
                 {/* 内容 */}
-                <div className={style.file_content} >
-                    {
-                        listData?.records?.map((item: Item) => {
-                            if (item.folder) {
-                                {/* 文件夹模板 */ }
-                                return <Popconfirm
-                                    title="确定要删除吗?"
-                                    onConfirm={() => { dels(item) }}
-                                    okText="是"
-                                    cancelText="否"
-                                    onCancel={delPupOff}
-                                    visible={delPupId === item.id}
-                                    key={item.id}
-                                >
-                                    <Spin tip='正在请求素材详情,请耐心等待...' spinning={get?.loading}>
-                                        <div
-                                            className={`${style.flex_box} ${selectFile?.some((id: number) => id === item.id) ? style.action : ''}`}
-                                            onContextMenu={(e) => { rightMenu(e, item) }}
-                                            onClick={(e) => { changeClickFile(e, item, isAll, noFile) }}
-                                            onDragStart={(e: any) => {
-                                                e.preventDefault()
-                                            }}
-                                        >
-                                            {/* {isAll && <span className={`${style.select}`} onClick={(e) => { changeClickFile(e, item, isAll, noFile) }}>
+                <Spin spinning={list.loading} style={{ width: '100%' }}>
+                    <div className={style.file_content}>
+                        {
+                            listData?.records?.map((item: Item) => {
+                                if (item.folder) {
+                                    {/* 文件夹模板 */ }
+                                    return <Popconfirm
+                                        title="确定要删除吗?"
+                                        onConfirm={() => { dels(item) }}
+                                        okText="是"
+                                        cancelText="否"
+                                        onCancel={delPupOff}
+                                        visible={delPupId === item.id}
+                                        key={item.id}
+                                    >
+                                        <Spin tip='正在请求素材详情,请耐心等待...' spinning={get?.loading}>
+                                            <div
+                                                className={`${style.flex_box} ${selectFile?.some((id: number) => id === item.id) ? style.action : ''}`}
+                                                onContextMenu={(e) => { rightMenu(e, item) }}
+                                                onClick={(e) => { changeClickFile(e, item, isAll, noFile) }}
+                                                onDragStart={(e: any) => {
+                                                    e.preventDefault()
+                                                }}
+                                            >
+                                                {/* {isAll && <span className={`${style.select}`} onClick={(e) => { changeClickFile(e, item, isAll, noFile) }}>
                                                 <span role="img" aria-label="check" style={{ color: '#fff' }}><svg viewBox="64 64 896 896" focusable="false" data-icon="check" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 00-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"></path></svg></span>
                                             </span>} */}
-                                            <img src={fileImg}
-                                                className={style.flex_box_img}
-                                                onClick={(e: any) => { e.stopPropagation(); fileClick(item) }}
-                                                onDragOver={(ev) => {
-                                                    ev.preventDefault()
-                                                }}
-                                                onDrop={() => {
-                                                    if (item.id !== folderId) {
-                                                        setActionId(item.id)
-                                                    }
-                                                }}
-                                            />
-                                            <span className={style.flex_box_name} >{item?.title}</span>
-                                            <span className={style.flex_box_id} onClick={(e) => { copyId(e, item?.number) }} >{item?.number}</span>
-                                        </div>
-                                    </Spin>
-                                </Popconfirm>
-                            } else {
-                                {/* 图片模板 ,视频模板,音频模板*/ }
-                                let topPageElements: any
-                                let topName: string = ""
-                                if (mediaType === 'PAGE' && item?.pageSpecsList) {
-                                    topPageElements = item?.pageSpecsList[0]?.pageElementsSpecList[0]
-                                }
-                                let El = null
-                                if (mediaType === 'IMG') {
-                                    El = <Image src={item.url} onClick={(e) => { e.stopPropagation() }} />
-                                } else if (mediaType === 'VIDEO') {
-                                    El = <video src={item.url} style={{ width: 130, height: 100 }} controls />
-                                } else if (mediaType === 'PAGE') {
-                                    switch (topPageElements?.elementType) {
-                                        case 'TOP_IMAGE':
-                                            topName = "顶部图片"
-                                            El = <Image src={topPageElements?.topImageSpec?.imageUrl} preview={{ visible: false }} onClick={(e) => { e.stopPropagation(); setPage && setPage(1, item.id) }} />
-                                            break
-                                        case 'TOP_SLIDER':
-                                            topName = "顶部轮播图"
-                                            El = <>
-                                                <Carousel autoplay style={{ width: 150, textAlign: 'center' }}>
-                                                    {topPageElements?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <div key={index}>
-                                                        <Image preview={{ visible: false }} src={url} onClick={(e) => { e.stopPropagation(); /*setImgVisible(true)*/ setPage && setPage(1, item.id) }} />
-                                                    </div>)}
-                                                </Carousel>
-                                                {/* <div style={{ display: 'none' }}>
+                                                <img src={fileImg}
+                                                    className={style.flex_box_img}
+                                                    onClick={(e: any) => { e.stopPropagation(); fileClick(item) }}
+                                                    onDragOver={(ev) => {
+                                                        ev.preventDefault()
+                                                    }}
+                                                    onDrop={() => {
+                                                        if (item.id !== folderId) {
+                                                            setActionId(item.id)
+                                                        }
+                                                    }}
+                                                />
+                                                <span className={style.flex_box_name} >{item?.title}</span>
+                                                <span className={style.flex_box_id} onClick={(e) => { copyId(e, item?.number) }} >{item?.number}</span>
+                                            </div>
+                                        </Spin>
+                                    </Popconfirm>
+                                } else {
+                                    {/* 图片模板 ,视频模板,音频模板*/ }
+                                    let topPageElements: any
+                                    let topName: string = ""
+                                    if (mediaType === 'PAGE' && item?.pageSpecsList) {
+                                        topPageElements = item?.pageSpecsList[0]?.pageElementsSpecList[0]
+                                    }
+                                    let El = null
+                                    if (mediaType === 'IMG') {
+                                        El = <Image src={item.url} onClick={(e) => { e.stopPropagation() }} />
+                                    } else if (mediaType === 'VIDEO') {
+                                        El = <video src={item.url} style={{ width: 130, height: 100 }} controls />
+                                    } else if (mediaType === 'PAGE') {
+                                        switch (topPageElements?.elementType) {
+                                            case 'TOP_IMAGE':
+                                                topName = "顶部图片"
+                                                El = <Image src={topPageElements?.topImageSpec?.imageUrl} preview={{ visible: false }} onClick={(e) => { e.stopPropagation(); setPage && setPage(1, item.id) }} />
+                                                break
+                                            case 'TOP_SLIDER':
+                                                topName = "顶部轮播图"
+                                                El = <>
+                                                    <Carousel autoplay style={{ width: 150, textAlign: 'center' }}>
+                                                        {topPageElements?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <div key={index}>
+                                                            <Image preview={{ visible: false }} src={url} onClick={(e) => { e.stopPropagation(); /*setImgVisible(true)*/ setPage && setPage(1, item.id) }} />
+                                                        </div>)}
+                                                    </Carousel>
+                                                    {/* <div style={{ display: 'none' }}>
                                                     <Image.PreviewGroup preview={{ visible: imgVisible, onVisibleChange: vis => setImgVisible(vis) }}>
                                                         {topPageElements?.topSliderSpec?.imageUrlList?.map((item: string, index: number) => <Image src={item} key={index} />)}
                                                     </Image.PreviewGroup>
                                                 </div> */}
-                                            </>
-                                            break
-                                        case 'TOP_VIDEO':
-                                            topName = "顶部视频"
-                                            El = <div className={style.pageVideo}>
-                                                <span className={style.pagePreview} onClick={(e) => { e.stopPropagation(); setPage && setPage(1, item.id) }}><EyeOutlined /> 预览</span>
-                                                <video src={topPageElements?.topVideoSpec?.videoUrl} style={{ width: 130, height: 100 }} controls />
-                                            </div>
-                                            break
+                                                </>
+                                                break
+                                            case 'TOP_VIDEO':
+                                                topName = "顶部视频"
+                                                El = <div className={style.pageVideo}>
+                                                    <span className={style.pagePreview} onClick={(e) => { e.stopPropagation(); setPage && setPage(1, item.id) }}><EyeOutlined /> 预览</span>
+                                                    <video src={topPageElements?.topVideoSpec?.videoUrl} style={{ width: 130, height: 100 }} controls />
+                                                </div>
+                                                break
+                                        }
                                     }
+                                    return <Popconfirm
+                                        title="确定要删除吗?"
+                                        onConfirm={() => { dels(item.id) }}
+                                        okText="是"
+                                        cancelText="否"
+                                        onCancel={delPupOff}
+                                        visible={delPupId === item.id}
+                                        key={item.id}
+                                    >
+                                        <Spin tip='正在请求素材详情,请耐心等待...' spinning={get?.loading}>
+                                            <div
+                                                className={`${style.image_box} ${!isBack ? (selectFile?.some((id: number) => id === item.id) ? style.action : '') : (selectItem?.some((item1: { url: string }) => item1.url === item.url) ? style.action : '')}`}
+                                                onContextMenu={(e) => { rightMenu(e, item) }}
+                                                onClick={(e) => { changeClickFile(e, item, isAll) }}
+                                                {...moveConfig(item)}
+                                            >
+                                                <span className={`${style.select}`} onClick={(e) => { changeClickFile(e, item, isAll) }}>
+                                                    <span role="img" aria-label="check" style={{ color: '#fff' }}><svg viewBox="64 64 896 896" focusable="false" data-icon="check" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 00-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"></path></svg></span>
+                                                </span>
+                                                {El}
+                                                <span className={style.flex_box_name} onClick={(e) => { copyId(e, item?.videoTitle || item?.title) }}>{item?.videoTitle || item?.title}</span>
+                                                {mediaType === 'PAGE' ? <span>{topName}</span> : <span>{item?.width}*{item.height}</span>}
+                                            </div>
+                                        </Spin>
+                                    </Popconfirm>
                                 }
-                                return <Popconfirm
-                                    title="确定要删除吗?"
-                                    onConfirm={() => { dels(item.id) }}
-                                    okText="是"
-                                    cancelText="否"
-                                    onCancel={delPupOff}
-                                    visible={delPupId === item.id}
-                                    key={item.id}
-                                >
-                                    <Spin tip='正在请求素材详情,请耐心等待...' spinning={get?.loading}>
-                                        <div
-                                            className={`${style.image_box} ${!isBack ? (selectFile?.some((id: number) => id === item.id) ? style.action : '') : (selectItem?.some((item1: { url: string }) => item1.url === item.url) ? style.action : '')}`}
-                                            onContextMenu={(e) => { rightMenu(e, item) }}
-                                            onClick={(e) => { changeClickFile(e, item, isAll) }}
-                                            {...moveConfig(item)}
-                                        >
-                                            <span className={`${style.select}`} onClick={(e) => { changeClickFile(e, item, isAll) }}>
-                                                <span role="img" aria-label="check" style={{ color: '#fff' }}><svg viewBox="64 64 896 896" focusable="false" data-icon="check" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 00-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"></path></svg></span>
-                                            </span>
-                                            {El}
-                                            <span className={style.flex_box_name} onClick={(e) => { copyId(e, item?.videoTitle || item?.title) }}>{item?.videoTitle || item?.title}</span>
-                                            {mediaType === 'PAGE' ? <span>{topName}</span> : <span>{item?.width}*{item.height}</span>}
-                                        </div>
-                                    </Spin>
-                                </Popconfirm>
-                            }
-                        })
-                    }
-                </div>
+                            })
+                        }
+                    </div>
+                </Spin>
                 {/* 鼠标右键菜单 */}
                 {rightClickPup.id ? rightClickPup.id === 'all' ? <Menu /> : <Menu isItem /> : null}
                 {/* 新建文件弹窗 */}
@@ -349,8 +351,9 @@ function FlieBox(props: Props) {
                         onShowSizeChange={(current: number, size: number) => {
                             getList({ pageSize: size, pageNum: current })
                         }}
+                        pageSizeOptions={['10', '20', '30', '50', '100']}
                         current={listData?.current}
-                        defaultPageSize={20}
+                        defaultPageSize={30}
                         total={listData?.total}
                     />
                 </div>

+ 2 - 2
src/pages/launchSystemNew/components/addLandingPage/index.tsx

@@ -1867,7 +1867,7 @@ function AddLandingPage(props: Props) {
                                                                     <div className="adui-form-item" style={{ alignItems: 'center' }}>
                                                                         <Select style={{ width: 180 }} className="aside-select" dropdownClassName="aside-select" onChange={(e) => { setGlobalComponentItem('componentItem', e) }} value={componentItem?.elementType} size="small">
                                                                             <Option value="GH"><FollowAcc />关注公众号</Option>
-                                                                            {/* <Option value="ENTERPRISE_WX"><WxAutoSvg />添加商家微信</Option> */}
+                                                                            <Option value="ENTERPRISE_WX"><WxAutoSvg />添加商家微信</Option>
                                                                         </Select>
                                                                     </div>
                                                                 </div>
@@ -2261,7 +2261,7 @@ function AddLandingPage(props: Props) {
                         <div className={style.assembly}>
                             <div {...getDragPropsCon(`GH`)}> <FollowAcc /> <span className="my">关注公众号</span> </div>
                             {/* <div {...getDragPropsCon(`JumpLink`)}> <JumpLink /> <span className="my">跳转链接</span> </div> */}
-                            {/* <div {...getDragPropsCon(`ENTERPRISE_WX`)}> <WxAutoSvg /> <span className="my">添加商家微信</span> </div> */}
+                            <div {...getDragPropsCon(`ENTERPRISE_WX`)}> <WxAutoSvg /> <span className="my">添加商家微信</span> </div>
                         </div>
                         <div className={style.title}>营销组件</div>
                         <div className={style.assembly}>

+ 168 - 0
src/pages/launchSystemNew/components/customerServiceModal/index.tsx

@@ -0,0 +1,168 @@
+import Tables from "@/components/Tables"
+import { useAjax } from "@/Hook/useAjax"
+import { getCropWechatApi, sysCropWechatApi } from "@/services/launchAdq/createAd"
+import { CheckOutlined, SyncOutlined } from "@ant-design/icons"
+import { Button, Input, message, Modal, Space, Tooltip } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../goodsModal/index.less'
+import columns from "./tableConfig"
+
+/**
+ * 获取客服组
+ * @returns 
+ */
+interface Props {
+    onChange?: (data: any) => void,
+    data: any
+}
+const CustomerServiceModal: React.FC<Props> = (props) => {
+
+    /************************/
+    const { data: data1, onChange } = props
+    const [tableData, setTableData] = useState<any[]>([])//table数据
+    const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
+    const [selectCrop, setSelectCrop] = useState<number>(1)   // 选择按钮
+    const [data, setData] = useState<any>(data1)
+    const [visible, setVisible] = useState<boolean>(false)
+    const [sysVisible, setSysVisible] = useState<boolean>(false)
+    const [cropId, setCropId] = useState<string>('')
+    const [notConfigured, setNotConfigured] = useState<any>([])
+
+    const getCropWechat = useAjax((params) => getCropWechatApi(params))
+    const sysCropWechat = useAjax((params) => sysCropWechatApi(params))
+    /************************/
+
+    /** 未配置账户获取 */
+    useEffect(() => {
+        setNotConfigured(() => data.filter((item: { data: { cropList: any[] }[] }) => item?.data?.some((item: { cropList: any[] }) => item?.cropList?.length === 0)))
+    }, [data])
+
+    useEffect(() => {
+        if (data?.length > 0) {
+            getList([data[selectAdz - 1].adAccountId])
+        } else {
+            setTableData([])
+        }
+    }, [selectAdz])
+
+
+    // 获取客服组列表
+    const getList = (params: number[]) => {
+        getCropWechat.run(params).then(res => {
+            if (res && Object.keys(res)?.indexOf(params[0].toString()) !== -1) {
+                setTableData(res[params[0]]?.map((item: { groupId: string }) => ({ ...item, id: item.groupId })))
+            } else {
+                setTableData([])
+            }
+        })
+    }
+
+    const handleOk = () => {
+        onChange && onChange(data)
+        setVisible(false)
+    }
+
+    /** 设置选中广告主 */
+    const handleSelectAdz = (value: number, item: any) => {
+        if (value === selectAdz) {
+            return
+        }
+        setSelectAdz(value)
+        setSelectCrop(1)
+    }
+
+    /** 设置选中广告主 */
+    const handleSelectCrop = (value: number, item: any) => {
+        if (value === selectCrop) {
+            return
+        }
+        setSelectCrop(value)
+    }
+
+    /** 表格选折 */
+    const onChangeTable = (selectedRowKeys: React.Key[], selectedRows: any) => {
+        let newData = JSON.parse(JSON.stringify(data))
+        newData[selectAdz - 1].data[selectCrop - 1].cropList = selectedRows
+        setData([...newData])
+    }
+
+    /** 同步 */
+    const handleSys = () => {
+        if (cropId) {
+            console.log(data[selectAdz - 1].id, cropId);
+            sysCropWechat.run({ accountId: data[selectAdz - 1].id, cropId: cropId }).then(res => {
+                if (res) {
+                    getList([data[selectAdz - 1].adAccountId])
+                    message.success('同步成功')
+                    setSysVisible(false)
+                }
+            })
+        } else {
+            message.error('请输入企业微信ID')
+        }
+    }
+
+    return <div>
+        <Button type="link" size="small" onClick={() => setVisible(true)} style={{ fontSize: 12 }}>{data1.some((item: { data: { cropList: any[] }[] }) => item?.data?.some((item: { cropList: any[] }) => item?.cropList?.length > 0)) ? '修改客服' : '配置客服'}</Button>
+        {visible && <Modal
+            title={<Space>
+                <span>客服组</span>
+                <Button size="small" onClick={() => { setSysVisible(true) }} type='link'>同步客服组</Button>
+            </Space>}
+            visible={visible}
+            footer={<Space>
+                <Button onClick={() => setVisible(false)}>取消</Button>
+                {notConfigured?.length > 0 ? <Tooltip title={`${notConfigured?.map((item: { adAccountId: string }) => item.adAccountId)?.toString()}下按钮未全部配置客服`}>
+                    <Button type="primary">确定</Button>
+                </Tooltip> : <Button type="primary" onClick={handleOk}>确定</Button>}
+            </Space>}
+            width={1100}
+            className={style.SelectPackage}
+            bodyStyle={{ padding: '0 10px 0 10px' }}
+        >
+            <div className={style.content}>
+                <div className={style.left}>
+                    <h4 className={style.title}>媒体账户</h4>
+                    {data?.map((item: { adAccountId: number, id: number }, index: number) => (
+                        <div key={index} onClick={() => { handleSelectAdz(index + 1, item) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
+                            {item?.adAccountId}
+                            {data[index]?.data?.every((item: { cropList: any[] }) => item?.cropList?.length > 0) && <CheckOutlined style={{ color: '#1890ff' }} />}
+                        </div>))}
+                </div>
+                <div className={style.left}>
+                    <h4 className={style.title}>企微按钮</h4>
+                    {data[selectAdz - 1].data?.map((item: { name: number }, index: number) => (
+                        <div key={index} onClick={() => { handleSelectCrop(index + 1, item) }} className={`${style.accItem} ${selectCrop === index + 1 && style.select} `}>
+                            <div>按钮{index + 1}<span style={{ fontSize: 12 }}>({item.name})</span></div>
+                            {data[selectAdz - 1]?.data[index]?.cropList?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
+                        </div>))}
+                </div>
+                <div className={style.right}>
+                    <Space style={{ marginBottom: 10 }} align="end">
+                        <Button icon={<SyncOutlined />} type='link' loading={getCropWechat?.loading} onClick={() => { getList([data[selectAdz - 1].adAccountId]) }}></Button>
+                    </Space>
+                    <Tables
+                        columns={columns()}
+                        dataSource={tableData}
+                        size="small"
+                        loading={getCropWechat?.loading}
+                        scroll={{ y: 300 }}
+                        bordered
+                        rowSelection={{
+                            type: 'radio',
+                            selectedRowKeys: data[selectAdz - 1]?.data[selectCrop - 1]?.cropList?.map((item: any) => item?.id?.toString()),
+                            onChange: onChangeTable
+                        }}
+                    />
+                </div>
+            </div>
+        </Modal>}
+
+        {/* 输入企业微信ID同步 */}
+        {sysVisible && <Modal title="输入企业微信ID同步" visible={sysVisible} onOk={handleSys} onCancel={() => setSysVisible(false)} okText='同步' confirmLoading={sysCropWechat.loading}>
+            <Input value={cropId} onChange={(e) => setCropId(e.target.value)} placeholder='输入企业微信ID同步' />
+        </Modal>}
+    </div>
+}
+
+export default React.memo(CustomerServiceModal)

+ 44 - 0
src/pages/launchSystemNew/components/customerServiceModal/tableConfig.tsx

@@ -0,0 +1,44 @@
+import React from "react"
+let columns = () => [
+    {
+        title: '媒体账户',
+        dataIndex: 'accountId',
+        key: 'accountId',
+        width: 120,
+        ellipsis: true,
+        align: 'center',
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{a}</span>
+        }
+    },
+    {
+        title: '客服组名称',
+        dataIndex: 'groupName',
+        key: 'groupName',
+        align: 'center',
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{a}</span>
+        }
+    },
+    {
+        title: '客服组ID',
+        dataIndex: 'groupId',
+        key: 'groupId',
+        align: 'center',
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{a}</span>
+        }
+    },
+    {
+        title: 'cropId',
+        dataIndex: 'cropId',
+        key: 'cropId',
+        align: 'center',
+        ellipsis: true,
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{a}</span>
+        }
+    }
+]
+
+export default columns

+ 11 - 9
src/pages/launchSystemNew/components/pageModal/index.tsx

@@ -130,7 +130,6 @@ import Tables from "@/components/Tables"
 import { useAjax } from "@/Hook/useAjax"
 import { getAdqLandingPageList, putAdqLandingPage } from "@/services/launchAdq/adq"
 import { CheckOutlined, SyncOutlined } from "@ant-design/icons"
-import e from "@umijs/deps/compiled/express"
 import { Button, message, Modal, Space } from "antd"
 import React, { useEffect, useState } from "react"
 import style from '../goodsModal/index.less'
@@ -142,6 +141,7 @@ import columns from "./tableConfig"
  * @returns 
  */
 interface Props {
+    cloudParams: { adcreativeTemplateId?: number }
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: any) => void,
@@ -150,12 +150,12 @@ interface Props {
 const PageModal: React.FC<Props> = (props) => {
 
     /*************************/
-    const { visible, onClose, data: data1, onChange } = props
+    const { cloudParams, visible, onClose, data: data1, onChange } = props
     const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
     const [data, setData] = useState<any>(data1)
     const [queryForm, setQueryForm] = useState<{ accountId?: number, pageSize: number, pageNum: number }>({ pageNum: 1, pageSize: 20 })
+    const [loading, setLoading] = useState<boolean>(false)
 
-    const putAdq = useAjax((params) => putAdqLandingPage(params))
     const listAjax = useAjax((params) => getAdqLandingPageList(params))
     /*************************/
 
@@ -173,14 +173,14 @@ const PageModal: React.FC<Props> = (props) => {
 
     // 获取落地页列表
     const getList = () => {
-        listAjax.run(queryForm)
+        listAjax.run({ ...queryForm, ...cloudParams })
     }
 
     const handleOk = () => {
         console.log('data---->', data);
-        if(data?.every((item: { pageList: any })=>item.pageList)){
+        if (data?.every((item: { pageList: any }) => item.pageList)) {
             onChange && onChange(data)
-        }else{
+        } else {
             message.error('还有账号未选择落地页!')
         }
     }
@@ -202,15 +202,17 @@ const PageModal: React.FC<Props> = (props) => {
 
     /** 同步落地页 */
     const synPageList = () => {
-        let ajaxs = data?.map((item: { id: number }) => putAdq.run(item.id))
+        setLoading(true)
+        let ajaxs = data?.map((item: { id: number }) => putAdqLandingPage(item.id))
         Promise.all(ajaxs).then(res => {
+            setLoading(false)
             listAjax.refresh()
-        })
+        }).catch(() => setLoading(false))
     }
     return <Modal
         title={<Space>
             <span>ADQ落地页</span>
-            <Button size="small" onClick={() => { synPageList() }} type="link" loading={putAdq?.loading}>同步落地页</Button>
+            <Button size="small" onClick={() => { synPageList() }} type="link" loading={loading}>同步落地页</Button>
         </Space>}
         visible={visible}
         onCancel={() => { onClose && onClose() }}

+ 3 - 3
src/pages/launchSystemNew/components/pageModal/tableConfig.tsx

@@ -7,7 +7,7 @@ let columns = () => [
         dataIndex: 'pageId',
         key: 'pageId',
         align: 'center',
-        width: 120,
+        width: 100,
         render: (a: string) => {
             return <a>{a}</a>
         }
@@ -43,7 +43,7 @@ let columns = () => [
         dataIndex: 'sourceType',
         key: 'sourceType',
         align: 'center',
-        width: 110,
+        width: 130,
         render: (a: any, b: any) => {
             return SourceTypeEnum[a]
         }
@@ -53,7 +53,7 @@ let columns = () => [
         dataIndex: 'createdTime',
         key: 'createdTime',
         align: 'center',
-        width: 160,
+        width: 145,
     },
 ]
 

+ 14 - 2
src/pages/launchSystemNew/components/selectCloud/index.tsx

@@ -1,9 +1,10 @@
 import { Button, Image, message, Modal, Space, Tabs, Tooltip } from "antd"
-import React, { useEffect } from "react"
+import React, { useEffect, useState } from "react"
 import { useModel } from "umi"
 import FileBoxAD from "@/components/FileBoxAD"
 import { CloseCircleFilled, QuestionCircleFilled } from "@ant-design/icons"
 import style from './index.less'
+import LookLanding from "../lookLanding"
 
 interface Props {
     visible?: boolean,
@@ -22,6 +23,9 @@ const SelectCloud: React.FC<Props> = (props) => {
     const { visible, onChange, onClose, sliderImgContent, isBack = true } = props
     const { state, set, getList } = useModel('useLaunchAdq.useBdMediaPup')
     const { mediaType, belongUser, parentId, selectItem, num } = state
+
+    const [lookVisible, setLookVisible] = useState<boolean>(false)
+    const [id, setId] = useState<number>(0)
     /**================================**/
 
     useEffect(() => {
@@ -96,7 +100,15 @@ const SelectCloud: React.FC<Props> = (props) => {
             <Tabs.TabPane tab={'个人本地'} key={1} />
             <Tabs.TabPane tab={'公共本地'} key={0} />
         </Tabs>
-        <FileBoxAD isAll={false} noFile={true} isBack={isBack} />
+        <FileBoxAD isAll={false} noFile={true} isBack={isBack} setPage={(type, data) => {
+            setId(data || 0)
+            if (type === 1) {
+                setLookVisible(true)
+            }
+        }} />
+
+        {/* 查看落地页 */}
+        {lookVisible && <LookLanding visible={lookVisible} onClose={() => setLookVisible(false)} id={id} />}
     </Modal>
 }
 

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

@@ -36,15 +36,15 @@ const TextAideInput: React.FC<Props> = (props) => {
         <Space>
             <Popover placement="topLeft" overlayClassName="textAideInputPopover" style={{ minWidth: 600 }} visible={descriptionShow} content={<>
                 {
-                    descriptionShow && <List
+                    <List
                         loading={getTextLsit?.loading}
                         size="small"
                         style={{ maxHeight: 300, overflowX: 'auto', minWidth: 600 }}
                         dataSource={getTextLsit?.data?.returnTexts}
                         renderItem={(item: any) => <List.Item onClick={(e: any) => {
                             setText(item.text)
-                            setDescriptionshow(false)
                             onChange && onChange(item.text)
+                            setTimeout(() => { setDescriptionshow(false) }, 50)
                         }}><span >{item.text}{item.tag && <span className="crt">{'CTR 高'}</span>}</span></List.Item>}
                     />
                 }
@@ -55,10 +55,10 @@ const TextAideInput: React.FC<Props> = (props) => {
                     value={text}
                     onFocus={() => {
                         setDescriptionshow(true)
-                        textList()
+                        textList(value)
                     }}
                     onBlur={() => {
-                        setTimeout(() => { setDescriptionshow(false) }, 100)
+                        // setTimeout(() => { setDescriptionshow(false) }, 100)
                     }}
                     onChange={(e) => {
                         let value = e.target.value

+ 1 - 1
src/pages/launchSystemNew/launchManage/createAd/adcreativeCol.tsx

@@ -45,7 +45,7 @@ const AdcreativeCol: React.FC<Props> = (props) => {
         <div><strong>推广目标:</strong> <span style={{ color: "#5a5a5a" }}>{PromotedObjectType[promotedObjectType]}</span></div>
         <div><strong>广告版位:</strong> <span style={{ color: "#5a5a5a" }}>{siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span></div>
         <div><strong>创意形式: </strong><span style={{ color: "#5a5a5a" }}>{adcreativeTemplate?.find((item: any) => item?.adcreativeTemplateId === adcreativeTemplateId)?.adcreativeTemplateAppellation || ''}</span></div>
-        {adcreativeElements?.brand && <div><strong>品牌形象:</strong> <span style={{ color: "#5a5a5a" }}><img src={adcreativeElements?.brand?.brandImg} style={{ width: 20,marginRight:5 }} />{adcreativeElements?.brand?.brandName}</span></div>}
+        {adcreativeElements?.brand && <div><strong>品牌形象:</strong> <span style={{ color: "#5a5a5a" }}><img src={adcreativeElements?.brand?.brandImgUrl} style={{ width: 20,marginRight:5 }} />{adcreativeElements?.brand?.brandName}</span></div>}
         {adcreativeElements?.description && <div><strong>创意文案:</strong> <span style={{ color: "#5a5a5a" }}>{adcreativeElements?.description}</span></div>}
         {adcreativeElements?.title && <div><strong>文案:</strong> <span style={{ color: "#5a5a5a" }}>{adcreativeElements?.title}</span></div>}
         {linkNameType && <div><strong>按钮文案:</strong> <span style={{ color: "#5a5a5a" }}>{LinkPageNameTypeEnum[linkNameType]}</span></div>}

+ 196 - 0
src/pages/launchSystemNew/launchManage/createAd/creative/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 columns 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={columns(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 
+ */
+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)

+ 14 - 110
src/pages/launchSystemNew/launchManage/createAd/creative/modal/index.tsx

@@ -12,6 +12,7 @@ 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'
 interface Props {
     queryForm: Partial<CreateAdProps>,
     title?: string,
@@ -22,84 +23,7 @@ interface Props {
     type?: 'add' | 'look' | 'edit',//新增,查看,编辑
     dataInfo?: any
 }
-const changgui = [
-    {
-        "id": 11401259460,
-        "app_id": 0,
-        "company_admin_user_id": 10000006600,
-        "type": 3,
-        "brand_name": "\u70ed\u95e8\u5c0f\u8bf4",
-        "description": "\u5168\u7f51\u70ed\u95e8\u5c0f\u8bf4",
-        "file_uri": "https:\/\/wxsnsencsvp.wxs.qq.com\/141\/20204\/snscosdownload\/SZ\/reserved\/6xykWLEnztJV9MqNeOQWia0nLSB26Kqnnb4PZFhdrEDUuw5CM5oIIbKBjKHreKeliaFq3HNvh2FbLIqcKDZ9EFDg?ck=df49be8f42708c0d8ac92eb56a329cac&idx=1&m=df49be8f42708c0d8ac92eb56a329cac&sha256=481be980e82dc2f5a3fe7a6b7eb5eac704289f155d8deb09dd0e0a588a194457&token=cztXnd9GyrGapkCPG2XUCdmc8GpN7o4mAVYH3Dsic0iaiaffCW6eGpdv5xykaCIP1wlxEwyhBpkgkZgJGcbLeIu6w",
-        "file_uri_local": "\/data\/chuangliang_storage\/public\/material_gdt\/images\/20220823194656-6304be30474a5.jpg",
-        "file_md5": "d41d8cd98f00b204e9800998ecf8427e",
-        "unique_key": "c410c21293f43c54e5d030fe629476de",
-        "unique_key_by_file_url": "87ab824b0e122a889792368da6e21aff",
-        "create_time": "2022-08-18 15:49:06",
-        "create_user_id": 0,
-        "update_time": "2022-08-23 19:46:56",
-        "update_user_id": 0,
-        "is_upload_cos": 0,
-        "main_user_id": 10000006600
-    },
-    {
-        "id": 10000020002,
-        "app_id": 0,
-        "company_admin_user_id": 10000006600,
-        "type": 3,
-        "brand_name": "\u542c\u4e66\u751c\u751c",
-        "description": "\u52a0\u6211\u5fae\u4fe1\u4e00\u8d77\u6536\u542c~",
-        "file_uri": "http:\/\/wxsnsencsvp.wxs.qq.com\/141\/20204\/snscosdownload\/SZ\/reserved\/6xykWLEnztKaHEvzepQhQOyFkdNy5AiceN5NcQewlDQXNspwfRjbfjwHiaxicIzfWUW0re5TN7RdPohbibvGTWOITg?ck=7dfbfec2a56571980e87501eb27308ae&idx=1&m=7dfbfec2a56571980e87501eb27308ae&sha256=aba52a73e40f79a776aefeada66975cf8d79dcad0b997edb1a81302e27028325&token=cztXnd9GyrFBiaHvxEeklKyDIYqSqTgzBbNxPhLabEiaU7bxLrh4n8RQbXibVgbrAfmMuwfMhHzd2c79Xaa0PXsYw",
-        "file_uri_local": "\/data\/chuangliang_storage\/public\/material_gdt\/images\/20220823194720-6304be484765b.jpg",
-        "file_md5": "7dfbfec2a56571980e87501eb27308ae",
-        "unique_key": "43eabfae223f0f56d0e74e11fee07a53",
-        "unique_key_by_file_url": "0060d604211ea2dfef8762c8b0ea1ccc",
-        "create_time": "2022-08-15 18:10:08",
-        "create_user_id": 0,
-        "update_time": "2022-08-23 19:47:20",
-        "update_user_id": 0,
-        "is_upload_cos": 0,
-        "main_user_id": 10000006600
-    },
-    {
-        "id": 10000020001,
-        "app_id": 0,
-        "company_admin_user_id": 10000006600,
-        "type": 3,
-        "brand_name": "\u751c\u751c\u542c\u4e66",
-        "description": "\u5feb\u6765\u4e00\u8d77\u6536\u542c\u5427~",
-        "file_uri": "http:\/\/wxsnsencsvp.wxs.qq.com\/141\/20204\/snscosdownload\/SZ\/reserved\/6xykWLEnztJ649NJWA2QNpQnO60FV3es98fD3ic1nqfE9ADTiawgEjxtT3Qia0sJgkC0Qv1VCpPGv1kXaIdJAhnsA?ck=7dfbfec2a56571980e87501eb27308ae&idx=1&m=7dfbfec2a56571980e87501eb27308ae&sha256=aba52a73e40f79a776aefeada66975cf8d79dcad0b997edb1a81302e27028325&token=6xykWLEnztL4weIvrU6UicTe88syvZWTcg2HIN6RvHKzQdwib2XpiazZwGZWUs4fy8lbJ4ItMh0ubZx22GWImZL5g",
-        "file_uri_local": "",
-        "file_md5": "7dfbfec2a56571980e87501eb27308ae",
-        "unique_key": "c71a7d54bae6974d41650f6ad670c570",
-        "unique_key_by_file_url": "f175c74d37a3e81e0cc08dd310d152de",
-        "create_time": "2022-08-15 18:10:06",
-        "create_user_id": 0,
-        "update_time": "2022-08-15 18:10:06",
-        "update_user_id": 0,
-        "is_upload_cos": 0,
-        "main_user_id": 10000006600
-    },
-    {
-        "id": 10000019780,
-        "app_id": 0,
-        "company_admin_user_id": 10000006600,
-        "type": 3,
-        "brand_name": "\u70ed\u95e8\u5c0f\u8bf4",
-        "description": "\u5168\u7f51\u8d85\u706b\u70ed\u95e8\u5c0f\u8bf4\uff01",
-        "file_uri": "https:\/\/wxsnsencsvp.wxs.qq.com\/141\/20204\/snscosdownload\/SZ\/reserved\/6xykWLEnztJhQ4jPM2vDczvxPaLYQicQkI9lYT97xbicxuktc9kiaLZB7rwbaogBc7tHGAhW5p0fHGj2Ov0sr5EXg?ck=c0cf5f032e0cfa600e27f0a8183891b3&idx=1&m=c0cf5f032e0cfa600e27f0a8183891b3&sha256=be7f5f69ba4925bde4bd70b1687bbbbf4a367d29555b562537cd73d01b64848f&token=cztXnd9GyrHQWaiafRG2RM6bqiblkVM0vsf4ZwjJvzV6Rwf32JwGu5brGOOjQNAyZfxlt066tw84lZia4Pjibwdz7w",
-        "file_uri_local": "\/data\/chuangliang_storage\/public\/material_gdt\/images\/20220815181138-62fa1bda6cea7.jpg",
-        "file_md5": "c0cf5f032e0cfa600e27f0a8183891b3",
-        "unique_key": "9af7aa2f9c40c50b5f333f4c72f8538d",
-        "unique_key_by_file_url": "affd3e3ee54a6d937d7791031bb012bf",
-        "create_time": "2022-08-08 17:36:21",
-        "create_user_id": 0,
-        "update_time": "2022-08-15 18:11:38",
-        "update_user_id": 0,
-        "is_upload_cos": 0,
-        "main_user_id": 10000006600
-    }
-]
+
 /**创意模板*/
 function CreativePup(props: Props) {
     let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm } = props
@@ -241,9 +165,14 @@ function CreativePup(props: Props) {
                         newValues.adcreativeElements = {
                             ...newValues.adcreativeElements, brand: {
                                 brandName: newValues.brand.split('_')[0],
-                                brandImg: newValues.brand.split('_')[1]
+                                brandImgUrl: newValues.brand.split('_')[1]
                             }
                         }
+                        // newValues.headClickSpec = {
+                        //     headImageUrl: newValues.brand.split('_')[1],
+                        //     profileName: newValues.brand.split('_')[0],
+                        //     description: newValues.brand.split('_')[2]
+                        // }
                         break;
                     case 'pageUrl'://跳转落地页
                         newValues.linkPageSpec = {
@@ -522,7 +451,7 @@ function CreativePup(props: Props) {
     // 数据回填
     useEffect(() => {
         if (!infoSet && dataInfo && adcreative_template_list?.length > 0) {
-            let { adcreativeName, adcreativeTemplateId, conversionDataType, conversionTargetType, linkNameType, linkPageType, pageType, promotedObjectType, siteSet, adcreativeElements, overrideCanvasHeadOption, linkPageSpec } = dataInfo
+            let { adcreativeName, adcreativeTemplateId, conversionDataType, conversionTargetType, linkNameType, linkPageType, pageType, promotedObjectType, siteSet, headClickSpec, adcreativeElements, overrideCanvasHeadOption, linkPageSpec } = dataInfo
             let { description, imageUrl, title, videoUrl, imageUrlList, endPage, shortVideoStruct, brand } = adcreativeElements
             let obj: any = {
                 adcreativeName,
@@ -570,8 +499,8 @@ function CreativePup(props: Props) {
             if (linkPageSpec?.miniProgramSpec && linkPageSpec?.miniProgramSpec?.miniProgramPath) {
                 obj = { ...obj, miniProgramPath: linkPageSpec?.miniProgramSpec?.miniProgramPath, miniProgramId: linkPageSpec?.miniProgramSpec?.miniProgramId }
             }
-            if (brand && brand.brandImg && brand.brandName) {
-                obj = { ...obj, brand: brand.brandName + '_' + brand.brandImg, }
+            if (brand && brand.brandImgUrl && brand.brandName) {
+                obj = { ...obj, brand: brand.brandName + '_' + brand.brandImgUrl }
             }
             if (videoUrl) {
                 setVideoMaterialConfig({
@@ -715,34 +644,9 @@ function CreativePup(props: Props) {
                         {/* ============================================================创意内容============================================================= */}
                         <Divider orientation='center'>创意内容</Divider>
                         {/* =============================================================品牌形象===================================================================== */}
-                        {
-                            queryForm.promotedObjectType === 'PROMOTED_OBJECT_TYPE_LEAD_AD' && <Form.Item label={<strong>品牌形象</strong>} name='brand' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
-                                <Select
-                                    showSearch
-                                    placeholder="请选择一个头像及昵称跳转页,与广告创意一起展示"
-                                    optionFilterProp="children"
-                                    style={{ width: 400 }}
-                                    onChange={() => { }}
-                                    allowClear
-                                    filterOption={(input, option) => {
-                                        return (option!.value as unknown as string).toLowerCase().includes(input.toLowerCase())
-                                    }
-                                    }
-                                >
-                                    {
-                                        changgui?.map(item => {
-                                            return <Select.Option value={item.brand_name + '_' + item.file_uri} key={item.id}>
-                                                <Space>
-                                                    <img src={item.file_uri} style={{ width: 20 }} />
-                                                    <span>{item.brand_name}</span>
-                                                </Space>
-                                            </Select.Option>
-                                        })
-                                    }
-
-                                </Select>
-                            </Form.Item>
-                        }
+                        {queryForm.promotedObjectType === 'PROMOTED_OBJECT_TYPE_LEAD_AD' && <Form.Item label={<strong>品牌形象</strong>} name='brand' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
+                            <BrandImage />
+                        </Form.Item>}
                         {/* ============================================================素材============================================================= */}
                         {/* 优先展示视频或图片,朋友圈常规不勾选使用外部素材替换内部,隐藏此选项,后期自动将落地页顶部素材添加进入 */}
                         {((overrideCanvasHeadOption !== 'OPTION_CANVAS_OVERRIDE_CREATIVE') || siteSet.every((name: string) => name !== 'SITE_SET_MOMENTS')) && <div style={{ display: 'flex', flexFlow: 'column' }}>

+ 72 - 0
src/pages/launchSystemNew/launchManage/createAd/creative/modal/tableConfig.tsx

@@ -0,0 +1,72 @@
+import React from "react"
+import { Image, Popconfirm, Space } from 'antd'
+
+let columns = (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
+}
+
+export default columns

+ 94 - 32
src/pages/launchSystemNew/launchManage/createAd/index.tsx

@@ -6,9 +6,9 @@ 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, EditOutlined, QuestionCircleOutlined, SearchOutlined } from "@ant-design/icons"
-import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Popover, Tabs, Checkbox } from "antd"
-import React, { useCallback, useEffect, useRef, useState } from "react"
+import { CloseOutlined, SearchOutlined } from "@ant-design/icons"
+import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Tabs } from "antd"
+import React, { useCallback, useEffect, useState } from "react"
 import { useModel } from "umi"
 import Ad from "./ad"
 import DataSourceModal from "../../components/dataSourceModal"
@@ -25,6 +25,7 @@ import columns from "./tableConfig"
 import TargetIng from './targeting'
 import Creative from './creative'
 import AddGroup from '../../components/addGroup'
+import CustomerServiceModal from "../../components/customerServiceModal"
 
 const CreateAd: React.FC = () => {
 
@@ -56,7 +57,6 @@ const CreateAd: React.FC = () => {
     const [lookVisible, setLookVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
     const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
     const [pageVisible, setPageVisible] = useState<boolean>(false) // 选择云端落地页控制
-    const [wxButtonList, setWxButtonList] = useState<WxAutoButton[]>([])
     const [tableData, setTableData] = useState<any[]>([])   // 预览表格
     const [tableSelect, setTableSelect] = useState<any[]>([])
     const [geoLocationList, setGeoLocationList] = useState<any>({}) // 所有地域列表
@@ -65,6 +65,7 @@ const CreateAd: React.FC = () => {
     const [page_checked, set_page_checked] = useState(false)//创意key
     const [usesArr, setUsersArr] = useState<any>(localStorage.getItem('ADQUSERS' + userId) ? JSON.parse(localStorage.getItem('ADQUSERS' + userId) as any) : [])
     const { init, get } = useModel('useLaunchAdq.useBdMediaPup')
+    const [cloudParams, setCloudParams] = useState<{ adcreativeTemplateId?: number }>({})
 
 
     const tagsList_REGION = useAjax((params) => getTagsList(params))
@@ -87,7 +88,7 @@ const CreateAd: React.FC = () => {
     // 设置地域
     useEffect(() => {
         tagsList_REGION.run({ type: 'REGION' }).then(res => {
-            if (res) {
+            if (res && Array.isArray(res)) {
                 setGeoLocationList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
                     prev[cur.id] = cur
                     return prev
@@ -95,7 +96,7 @@ const CreateAd: React.FC = () => {
             }
         })
         tagsList_MODEL.run({ type: 'DEVICE_BRAND_MODEL' }).then(res => {
-            if (res) {
+            if (res && Array.isArray(res)) {
                 setModelList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
                     prev[cur.id] = cur
                     return prev
@@ -107,7 +108,6 @@ const CreateAd: React.FC = () => {
     // 获取账户列表
     useEffect(() => {
         getAdAccount.run()
-        // init({ mediaType: 'PAGE' })
     }, [])
 
     /** 获取广告详情 */
@@ -182,6 +182,15 @@ const CreateAd: React.FC = () => {
             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 (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))
+            })) {
+                message.error('请完善落地页企微客服组')
+                return
+            }
+        }
         let data: any[] = []
         accountCreateLogs.forEach((item: any) => {
             queryForm.taskMediaMaps?.forEach((task, index) => {
@@ -203,7 +212,36 @@ const CreateAd: React.FC = () => {
 
     const submit = (props: { campaignName: string, count?: number }) => {
         console.log(111111, tableSelect);
-        let params = { ...queryForm, ...props }
+        let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+        let newtaskMediaMaps = newQueryForm.taskMediaMaps.map((item1: { cropUserGroupMap?: any[] }) => {
+            let { cropUserGroupMap, ...data } = item1
+            if (cropUserGroupMap && cropUserGroupMap?.length > 0) {
+                let cropUserGroup1Map: any = {}
+                let cropUserGroup2Map: any = {}
+                cropUserGroupMap.forEach((cropData: { id: number, data: any[] }) => {
+                    let cropData1: { cropId: string, groupId: number }[] = []
+                    let cropData2: { cropId: string, groupId: number }[] = []
+                    cropData?.data.forEach((crop: { type: 1 | 2, cropList: { cropId: string, groupId: number }[] }) => {
+                        let cropList = crop.cropList
+                        if (crop.type === 1) {
+                            cropData1.push({ cropId: cropList[0].cropId, groupId: cropList[0].groupId })
+                        } else {
+                            cropData2.push({ cropId: cropList[0].cropId, groupId: cropList[0].groupId })
+                        }
+                    })
+                    if (cropData1.length > 0) {
+                        cropUserGroup1Map[cropData.id.toString()] = cropData1
+                    }
+                    if (cropData2.length > 0) {
+                        cropUserGroup2Map[cropData.id.toString()] = cropData2
+                    }
+                })
+                return { ...data, cropUserGroup1Map: Object.keys(cropUserGroup1Map)?.length > 0 ? cropUserGroup1Map : null, cropUserGroup2Map: Object.keys(cropUserGroup2Map)?.length > 0 ? cropUserGroup2Map : null }
+            }
+            return data
+        })
+        newQueryForm.taskMediaMaps = newtaskMediaMaps
+        let params = { ...newQueryForm, ...props }
         console.log(accountCreateLogs)
         let accountLogs = accountCreateLogs.map((item: any, index) => {
             // userActionSetsList 数据源  productList 商品
@@ -316,6 +354,7 @@ const CreateAd: React.FC = () => {
         getPageInfo(arr)
         setSelectImgVisible(false)
     }
+
     /** 获取落地页详情 */
     const getPageInfo = useCallback((arrList) => {
         console.log('arrList====>', arrList)
@@ -323,13 +362,28 @@ const CreateAd: React.FC = () => {
             get.run({ mediaType: 'PAGE', sysMediaId: arrList[targetKey]?.sysPageId }).then(res => {
                 if (!Object.keys(res)?.includes('fail')) {
                     let data = res
-                    let pageElementsSpecList = data?.pageSpecsList[0]?.pageElementsSpecList
+                    let pageElementsSpecList = data?.pageSpecsList[0]?.pageElementsSpecList // 内容区
+                    let globalSpec = data?.globalSpec  // 悬浮组件
                     let arr: any = queryForm.pageList || []
                     let adqPageArr: any = queryForm.adqPageList || []
                     adqPageArr[targetKey] = null
                     arr[targetKey] = data
+                    /** 处理客服 */
+                    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 = accountCreateLogs?.map((item: any) => ({ adAccountId: item.adAccountId, id: item.id, data: groupList }))
+                    }
+                    arrList[targetKey].cropUserGroupMap = cropUserGroupMap
                     setQueryForm({ ...queryForm, pageList: arr, taskMediaMaps: arrList, adqPageList: adqPageArr })//设置落地页详情数组
-                    setWxButtonList(() => (pageElementsSpecList as any[])?.filter((item: { elementType: string }) => item?.elementType === 'ENTERPRISE_WX'))
                 } else {
                     //清空对应创意中的落地页ID
                     let arr = queryForm.taskMediaMaps || []
@@ -338,7 +392,7 @@ const CreateAd: React.FC = () => {
                 }
             })
         }
-    }, [queryForm, targetKey])
+    }, [queryForm, targetKey, accountCreateLogs])
     // 设置云端落地页
     const setAdqPage = useCallback((data) => {
         if (Array.isArray(data) && data.length > 0) {
@@ -351,6 +405,7 @@ const CreateAd: React.FC = () => {
             let pageArr: any = queryForm.pageList || []
             adqPageArr[targetKey as string] = data
             pageArr[targetKey as string] = null
+            delete arr[targetKey as string]?.cropUserGroupMap
             arr[targetKey as string] = { ...arr[targetKey as string], sysPageId: '', accountPageIdMap: objMap }
             // 重新设置云端数据并清空本地数据
             setQueryForm({ ...queryForm, taskMediaMaps: arr, adqPageList: adqPageArr, pageList: pageArr })
@@ -371,11 +426,12 @@ const CreateAd: React.FC = () => {
         }
     }, [queryForm, targetKey])
     // 媒体组更新通知
-    const usersChange=useCallback(()=>{
+    const usersChange = useCallback(() => {
         let data = JSON.parse(localStorage.getItem('ADQUSERS' + userId) as any)
         setUsersArr(data)
-    },[])
-    console.log('queryForm======>111', queryForm)
+    }, [])
+
+    console.log('queryForm111111', queryForm);
     return <Space direction="vertical" style={{ width: '100%' }}>
         <Card
             title={<div className={style.cardTitle}>配置区</div>}
@@ -396,13 +452,13 @@ const CreateAd: React.FC = () => {
                             return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
                         }}
                         onChange={(e, option) => {
-                            console.log(e,option)
-                            let userArr:any[] = []
-                            e.forEach((key: any)=> {
-                                let obj = usesArr.find((item: { id: any })=>item.id === key)
-                               if(obj){
-                                  userArr.push(obj['pitcher'])
-                               }
+                            console.log(e, option)
+                            let userArr: any[] = []
+                            e.forEach((key: any) => {
+                                let obj = usesArr.find((item: { id: any }) => item.id === key)
+                                if (obj) {
+                                    userArr.push(obj['pitcher'])
+                                }
                             })
                             userArr = [...new Set(userArr.flat())]
                             setAccountCreateLogs(userArr?.map((item: any) => ({ adAccountId: item?.split('_')[1], id: Number(item?.split('_')[0]) })))
@@ -422,12 +478,11 @@ const CreateAd: React.FC = () => {
                         bordered={false}
                         filterOption={(input: any, option: any) => {
                             return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
-                        }
-
-                        }
+                        }}
                         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: '' })) })
                             setAccountCreateLogs(option?.map((item: any) => ({ adAccountId: item?.children?.toString()?.split('——')[0], id: item?.value })))
                             clearData()
                         }}
@@ -541,10 +596,11 @@ const CreateAd: React.FC = () => {
                             <Col span={12} >
                                 <div className={style.top}>
                                     落地页
-                                    {/* <span >落地页<Tooltip title="云端落地页优先于本地落地页">
-                                        <QuestionCircleOutlined />
-                                    </Tooltip></span> */}
-                                    {wxButtonList?.length > 0 && <Button type="link" size="small">配置客服</Button>}
+                                    {(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}>
                                     <Tabs size={'small'} onEdit={onEdit} type="editable-card" activeKey={targetKey} onChange={(key) => { set_targetKey(key) }} hideAdd >
@@ -605,11 +661,17 @@ const CreateAd: React.FC = () => {
                                             } else {
                                                 init({ mediaType: 'PAGE', cloudSize: undefined })
                                             }
-
                                         }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId ? '修改' : '选择落地页'}</Button>
-                                        {/* {accountCreateLogs?.length > 0  ?  <Button type="link" onClick={() => { setPageVisible(true) }}>云端落地页</Button> : <Tooltip title="请先选择媒体账户">
+                                        {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>}
                                     </> : <Tooltip title="请先设置创意">
                                         <Button type="link"><span>选择落地页</span></Button>
                                     </Tooltip>}
@@ -677,7 +739,7 @@ const CreateAd: React.FC = () => {
         {/* 选择转化ID */}
         {idVisible && <IdModal visible={idVisible} data={accountCreateLogs} onClose={() => setIdVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setSourceVisible(false); clearData() }} />}
         {/* 选择ADQ落地页 */}
-        {pageVisible && <PageModal visible={pageVisible} data={accountCreateLogs} onClose={() => setPageVisible(false)} onChange={(e) => { setAdqPage(e); setPageVisible(false); clearData() }} />}
+        {pageVisible && <PageModal cloudParams={cloudParams} visible={pageVisible} data={accountCreateLogs} onClose={() => setPageVisible(false)} onChange={(e) => { setAdqPage(e); setPageVisible(false); clearData() }} />}
         {/* 选择素材 */}
         {selectImgVisible && <SelectCloud visible={selectImgVisible} onClose={() => setSelectImgVisible(false)} onChange={setPage} isBack={false} />}
         {/* 查看落地页 */}

+ 19 - 5
src/pages/launchSystemNew/launchManage/createAd/submitModal.tsx

@@ -1,6 +1,7 @@
+import { SpeedMode } from "@/services/launchAdq/enum"
 import { QuestionCircleOutlined } from "@ant-design/icons"
-import { Form, Input, Modal, InputNumber, Tooltip } from "antd"
-import React, {useState } from "react"
+import { Form, Input, Modal, InputNumber, Tooltip, Select } from "antd"
+import React, { useState } from "react"
 
 
 /**
@@ -18,9 +19,8 @@ const SubmitModal: React.FC<Props> = (props) => {
 
     /********************/
     const { visible, onClose, onChange, ajax, data } = props
-    // const { endDate, beginDate, bidAmount } = data
     const [form] = Form.useForm()
-    const [initialValues, setInitialValues] = useState<{count?:number}>({count:1})
+    const [initialValues, setInitialValues] = useState<{ speedMode: string, count?: number }>({ count: 1, speedMode: 'SPEED_MODE_STANDARD' })
 
     const handleOk = async () => {
         form.submit()
@@ -40,8 +40,22 @@ const SubmitModal: React.FC<Props> = (props) => {
             <Form.Item label={<strong>计划名称</strong>} name="campaignName">
                 <Input placeholder="请输入计划名称" />
             </Form.Item>
+            <Form.Item label={<strong>投放方式</strong>} name="speedMode" rules={[{ required: true, message: '请选择投放方式!' }]}>
+                <Select
+                    style={{ width: 200 }}
+                    placeholder="请选择投放方式"
+                    showSearch
+                    filterOption={(input: any, option: any) =>
+                        (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                    }
+                >
+                    {Object.keys(SpeedMode).map(key => {
+                        return <Select.Option value={key} key={key}>{SpeedMode[key]}</Select.Option>
+                    })}
+                </Select>
+            </Form.Item>
             <Form.Item label={<strong>创建数量<Tooltip title='每条计划创建的数量!'><QuestionCircleOutlined /></Tooltip></strong>} name="count">
-                <InputNumber placeholder="创建数量" min={1} max={30}/>
+                <InputNumber placeholder="创建数量" min={1} max={30} />
             </Form.Item>
         </Form>
     </Modal>

+ 0 - 1
src/pages/launchSystemNew/launchManage/localAd/creative/modal.tsx

@@ -35,7 +35,6 @@ function CreativeModal(props: Props) {
     const [descriptionShow, setdescriptionshow] = useState(false)
     const [endPageDescShow, setendPageDescnshow] = useState(false)
     const [titleShow, settitleshow] = useState(false)
-    const [login, setlogin] = useState(true)
     const [form] = Form.useForm();
     const [pupState, setPupState] = useState({
         kp_show: false,

+ 6 - 24
src/pages/launchSystemNew/launchManage/taskList/logTableConfig.tsx

@@ -46,6 +46,7 @@ function tableConfig(copyCreative: (data: any) => void, callback: (data: any) =>
             key: 'id',
             align: 'center',
             width: 50,
+            fixed: 'left'
         },
         {
             title: '媒体账户',
@@ -53,6 +54,7 @@ function tableConfig(copyCreative: (data: any) => void, callback: (data: any) =>
             key: 'accountId',
             align: 'center',
             width: 90,
+            fixed: 'left',
             render: (a: any, b: any) => {
                 return <a onClick={()=>{goTo(JSON.stringify({accountId:a}))}}>{a || '--'}</a>
             }
@@ -63,6 +65,7 @@ function tableConfig(copyCreative: (data: any) => void, callback: (data: any) =>
             key: 'campaignIds',
             align: 'center',
             width: 100,
+            fixed: 'left',
             render: (a: any[]) => {
                 return a ? a?.length <= 1 ? <a onClick={()=>{goTo(JSON.stringify({campaignId:a[0]}))}}>{a}</a> : <Modal arr={a} title={'计划ID'} key='campaignId'/> : '--'
             }
@@ -180,33 +183,12 @@ function tableConfig(copyCreative: (data: any) => void, callback: (data: any) =>
             key: 'failMsgs',
             align: 'left',
             ellipsis: true,
-            width: 200,
+            width: 250,
+            fixed: 'right',
             render: (a: any, b: any) => {
                 return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a || '--'}</a>
             }
-        },
-        // {
-        //     title: '操作',
-        //     dataIndex: 'cz',
-        //     key: 'cz',
-        //     width: 100,
-        //     align: 'center',
-        //     render: (a: any, b: any) => {
-        //         return <Dropdown trigger={['click']} overlay={<Menu items={[
-        //             {
-        //                 key: '1',
-        //                 label: (<Button size="small" type="link" onClick={() => { copyCreative(b) }}>批量复制</Button>)
-        //             }
-        //         ]} />}>
-        //             <a onClick={e => e.preventDefault()} style={{ fontSize: 12 }}>
-        //                 <Space>
-        //                     操作
-        //                     <DownOutlined />
-        //                 </Space>
-        //             </a>
-        //         </Dropdown>
-        //     }
-        // }
+        }
     ]
 }
 

+ 81 - 55
src/services/launchAdq/createAd.ts

@@ -6,46 +6,46 @@ import { api } from '../api';
  * 获取商品库列表
  */
 export interface CreateAdProps {
-    campaignName: string, // 计划名称
-    campaignType: string, // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH 
-    promotedObjectType: string, // 推广目标类型
-    dailyBudget?: number,   // 推广计划日预算
-    totalBudget?: number, // 推广计划总预算
-    speedMode: string, // 投放速度模式
-    sysAdgroupId: number,  // 广告组ID
-    pageList:any[],//本地落地页详情入口
-    adqPageList:any[],//云落地页
-    sysAdgroup:any,//广告组内容
-    sysTargetingId: number,  // 定向包 id
-    sysTargeting: any,  // 定向包内容
-    adgroupName: string,  // 广告名称
-    configuredStatus: string,  // 广告状态
-    sysAdcreativeId: number, // 创意ID
-    taskMediaMaps:any[],//创意内容
-    beginDate?: string, // 开始日期
-    firstDayBeginTime?: string,  //hh:mm:ss 开始时间
-    endDate?: string, // 结束日期
-    bidAmount?: number, // 出价
-    accountCreateLogs: {
-        adAccountId: number, // 媒体账户ID
-        userActionSets?: {
-            id: number,
-            type: string
-        }[],  // 数据源
-        conversionId?: number, // 广告组 转化Id
-        productCatalogId?: number, // 商品库ID
-        productId?: number, // 商品Id
-        enterpriseWx?: any[]  // 企业微信客服组
-        customAudience?: number[],  // 定向人群
-        excludedCustomAudience?: number[], // 排除人群
-        pageId?: number,  // 腾讯落地页ID
-    }[]
+  campaignName: string, // 计划名称
+  campaignType: string, // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH 
+  promotedObjectType: string, // 推广目标类型
+  dailyBudget?: number,   // 推广计划日预算
+  totalBudget?: number, // 推广计划总预算
+  speedMode: string, // 投放速度模式
+  sysAdgroupId: number,  // 广告组ID
+  pageList: any[],//本地落地页详情入口
+  adqPageList: any[],//云落地页
+  sysAdgroup: any,//广告组内容
+  sysTargetingId: number,  // 定向包 id
+  sysTargeting: any,  // 定向包内容
+  adgroupName: string,  // 广告名称
+  configuredStatus: string,  // 广告状态
+  sysAdcreativeId: number, // 创意ID
+  taskMediaMaps: any[],//创意内容
+  beginDate?: string, // 开始日期
+  firstDayBeginTime?: string,  //hh:mm:ss 开始时间
+  endDate?: string, // 结束日期
+  bidAmount?: number, // 出价
+  accountCreateLogs: {
+    adAccountId: number, // 媒体账户ID
+    userActionSets?: {
+      id: number,
+      type: string
+    }[],  // 数据源
+    conversionId?: number, // 广告组 转化Id
+    productCatalogId?: number, // 商品库ID
+    productId?: number, // 商品Id
+    enterpriseWx?: any[]  // 企业微信客服组
+    customAudience?: number[],  // 定向人群
+    excludedCustomAudience?: number[], // 排除人群
+    pageId?: number,  // 腾讯落地页ID
+  }[]
 }
 export async function createAdBatchApi(data: CreateAdProps) {
-    return request(api + `/adq//adCreateTask/createAdBatch`, {
-        method: 'POST',
-        data
-    })
+  return request(api + `/adq//adCreateTask/createAdBatch`, {
+    method: 'POST',
+    data
+  })
 }
 
 /**
@@ -77,10 +77,10 @@ export async function synGoodsApi(data: number[]) {
  * @returns
  */
 export async function getDataSourceApi(data: number[]) {
-    return request(api + `/adq/userActionSets/allByAccount`, {
-        method: 'POST',
-        data
-    })
+  return request(api + `/adq/userActionSets/allByAccount`, {
+    method: 'POST',
+    data
+  })
 }
 
 /**
@@ -113,10 +113,10 @@ export async function getIdApi(data: number[]) {
  * @returns
  */
 export async function sysIdApi(data: number[]) {
-    return request(api + `/adq/conversions/syncByAdAccountId`, {
-        method: 'PUT',
-        data
-    })
+  return request(api + `/adq/conversions/syncByAdAccountId`, {
+    method: 'PUT',
+    data
+  })
 }
 
 
@@ -126,10 +126,10 @@ export async function sysIdApi(data: number[]) {
  * @returns 
  */
 export async function getCrowdPackApi(data: number[]) {
-    return request(api + `/adq/customAudiences/allByAccount`, {
-        method: 'POST',
-        data
-    })
+  return request(api + `/adq/customAudiences/allByAccount`, {
+    method: 'POST',
+    data
+  })
 }
 
 
@@ -138,9 +138,35 @@ export async function getCrowdPackApi(data: number[]) {
  * @param data 
  * @returns 
  */
- export async function sysCrowdPackApi(data: number[]) {
-    return request(api + `/adq/customAudiences/syncByAdAccountId`, {
-        method: 'PUT',
-        data
-    })
+export async function sysCrowdPackApi(data: number[]) {
+  return request(api + `/adq/customAudiences/syncByAdAccountId`, {
+    method: 'PUT',
+    data
+  })
+}
+
+
+
+/**
+ * 获取客服组
+ * @param data 
+ * @returns 
+ */
+export async function getCropWechatApi(data: number[]) {
+  return request(api + `/adq/cropWechatCsgroup/allByAccount`, {
+    method: 'POST',
+    data
+  })
 }
+
+
+/**
+ * 同步客服组
+ * @param data 
+ * @returns 
+ */
+export async function sysCropWechatApi({ accountId, cropId }: { accountId: number, cropId: number }) {
+  return request(api + `/adq/cropWechatCsgroup/syncByAccount/${accountId}/${cropId}`, {
+    method: 'POST'
+  })
+}

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

@@ -1,7 +1,7 @@
 /**广告组推广目标类型*/
 export enum PromotedObjectType {
   PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT = '微信公众号',
-  // PROMOTED_OBJECT_TYPE_LEAD_AD = '销售线索',
+  PROMOTED_OBJECT_TYPE_LEAD_AD = '销售线索',
   // PROMOTED_OBJECT_TYPE_LINK = '网页',
   // PROMOTED_OBJECT_TYPE_LINK_WECHAT = '品牌网页',
   // PROMOTED_OBJECT_TYPE_ECOMMERCE = '商品推广',
@@ -383,6 +383,7 @@ export enum SourceTypeEnum {
   UNKNOWN = '未知',
   SOURCE_TYPE_EQQ = '投放端配置',
   SOURCE_TYPE_CRM = '内部工具配置',
+  SOURCE_TYPE_MP = '微信公众平台创建'
 }
 /**落地页状态*/
 export enum PageStatusEnum {

+ 78 - 31
src/services/launchAdq/global.ts

@@ -4,20 +4,20 @@ import { api } from '../api';
  * 定向标签获取
  * 
 */
-export async function getTagsList(params:any){
-    return request(api+`/adq/launch/tools/targeting/tags/list`,{
-        method:'POST',
-        data:params
+export async function getTagsList(params: any) {
+    return request(api + `/adq/launch/tools/targeting/tags/list`, {
+        method: 'POST',
+        data: params
     })
 }
 /**
  * 场景定向标签获取
- * */ 
+ * */
 
- export async function getSceneTagsList(params:any){
-    return request(api+`/adq/launch/tools/scene/spec/tags/list`,{
-        method:'POST',
-        data:params
+export async function getSceneTagsList(params: any) {
+    return request(api + `/adq/launch/tools/scene/spec/tags/list`, {
+        method: 'POST',
+        data: params
     })
 }
 /**
@@ -27,12 +27,12 @@ export async function getTagsList(params:any){
  * @param siteSet 数组版位 
  * https://developers.e.qq.com/docs/api/tools/adcreative_template/adcreative_template_get?version=1.3&_preview=1
  * https://developers.e.qq.com/docs/tools/adcreative_template
- * */ 
+ * */
 
- export async function get_adcreative_template(params:any){
-    return request(api+`/adq/launch/tools/adcreative/template`,{
-        method:'POST',
-        data:params
+export async function get_adcreative_template(params: any) {
+    return request(api + `/adq/launch/tools/adcreative/template`, {
+        method: 'POST',
+        data: params
     })
 }
 /**
@@ -41,33 +41,80 @@ export async function getTagsList(params:any){
  * @param siteSet 数组版位 
  * @param campaignType 投放位置
  * https://developers.e.qq.com/docs/api/tools/adcreative_template/adcreative_template_list_get?version=1.3&_preview=1
- * */ 
+ * */
 
- export async function get_adcreative_template_list(params:any){
-    return request(api+`/adq/launch/tools/adcreative/template/list`,{
-        method:'POST',
-        data:params
+export async function get_adcreative_template_list(params: any) {
+    return request(api + `/adq/launch/tools/adcreative/template/list`, {
+        method: 'POST',
+        data: params
     })
 }
 /**
  * 文案助手
  * */
-export async function getText(params:{
-    maxTextLength:number,
-    adAccountId:number,
+export async function getText(params: {
+    maxTextLength: number,
+    adAccountId: number,
 }) {
-    return request(api+`/adq/launch/tools/creative/tools/text`,{
-        method:'GET',
+    return request(api + `/adq/launch/tools/creative/tools/text`, {
+        method: 'GET',
         params
     })
-    
-} 
+
+}
 /**
  * 视频封面图生成
- * */ 
- export async function get_tools_video_capture(params:any){
-    return request(api+`/adq/launch/tools/video/maker/capture`,{
-        method:'POST',
-        data:params
+ * */
+export async function get_tools_video_capture(params: any) {
+    return request(api + `/adq/launch/tools/video/maker/capture`, {
+        method: 'POST',
+        data: params
+    })
+}
+
+/**
+ * 获取品牌形象列表
+ * @returns 
+ */
+export async function getSysBrandApi() {
+    return request(api + `/adq/sysBrand/allOfUser`, {
+        method: 'GET'
+    })
+}
+
+/**
+ * 新增品牌形象
+ * @param data 
+ * @returns 
+ */
+export async function addSysBrandApi(data: { name: string, brandImgUrl: string }) {
+    return request(api + `/adq/sysBrand`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 修改品牌形象
+ * @param data 
+ * @returns 
+ */
+export async function editSysBrandApi(data: { name: string, brandImgUrl: string, sysBrandId: number }) {
+    const { sysBrandId, ...params } = data
+    return request(api + `/adq/sysBrand/${sysBrandId}`, {
+        method: 'PUT',
+        data: params
+    })
+}
+
+/**
+ * 删除品牌形象
+ * @param data 
+ * @returns 
+ */
+export async function delSysBrandApi(data: { sysBrandId: number }) {
+    const { sysBrandId } = data
+    return request(api + `/adq/sysBrand/${sysBrandId}`, {
+        method: 'DELETE'
     })
 }