Procházet zdrojové kódy

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

wjx před 8 měsíci
rodič
revize
584c5f6dc1
28 změnil soubory, kde provedl 1737 přidání a 172 odebrání
  1. 12 0
      config/routerConfig.ts
  2. 20 17
      src/components/FileBoxAD/components/fileModal/index.tsx
  3. 12 8
      src/components/FileBoxAD/components/imgsModal/index.tsx
  4. 274 9
      src/components/FileBoxAD/components/uploadsTable/index.tsx
  5. 28 13
      src/components/FileBoxAD/index.tsx
  6. 2 2
      src/models/useLaunchAdq/useBdMedia.ts
  7. 1 3
      src/pages/launchSystemNew/components/selectCloud/index.tsx
  8. 245 0
      src/pages/launchSystemV3/components/PageModal/MiniProgramPage.tsx
  9. 78 1
      src/pages/launchSystemV3/components/PageModal/tableConfig.tsx
  10. 5 3
      src/pages/launchSystemV3/components/TextAideInput/index.tsx
  11. 187 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/index.tsx
  12. 67 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/modify.tsx
  13. 86 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/tableConfig.tsx
  14. 197 0
      src/pages/launchSystemV3/tencenTasset/miniProgramWechat/index.tsx
  15. 94 0
      src/pages/launchSystemV3/tencenTasset/miniProgramWechat/modify.tsx
  16. 107 0
      src/pages/launchSystemV3/tencenTasset/miniProgramWechat/tableConfig.tsx
  17. 11 8
      src/pages/launchSystemV3/tencenTasset/profiles/index.tsx
  18. 3 1
      src/pages/launchSystemV3/tencentAdPutIn/const.ts
  19. 8 1
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsMarketingContent.tsx
  20. 3 1
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/index.tsx
  21. 18 1
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeConversionAssistant.tsx
  22. 2 2
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeTemplateContent.tsx
  23. 35 18
      src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/newDynamic.tsx
  24. 6 6
      src/pages/launchSystemV3/tencentAdPutIn/create/Material/addMaterial.tsx
  25. 71 70
      src/pages/launchSystemV3/tencentAdPutIn/create/PageList/index.tsx
  26. 7 5
      src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx
  27. 20 2
      src/pages/launchSystemV3/tencentAdPutIn/create/tableConfig.tsx
  28. 138 1
      src/services/adqV3/global.ts

+ 12 - 0
config/routerConfig.ts

@@ -168,6 +168,18 @@ const launchSystemV3 = {
                     path: '/launchSystemV3/tencenTasset/wechatCanvasPage',
                     access: 'wechatCanvasPage',
                     component: './launchSystemV3/tencenTasset/wechatCanvasPage',
+                },
+                {
+                    name: '微信小程序',
+                    path: '/launchSystemV3/tencenTasset/miniProgramWechat',
+                    access: 'miniProgramWechat',
+                    component: './launchSystemV3/tencenTasset/miniProgramWechat',
+                },
+                {
+                    name: '企业微信',
+                    path: '/launchSystemV3/tencenTasset/corpWechat',
+                    access: 'corpWechat',
+                    component: './launchSystemV3/tencenTasset/corpWechat',
                 }
             ],
         },

+ 20 - 17
src/components/FileBoxAD/components/fileModal/index.tsx

@@ -1,12 +1,15 @@
 import { Input, InputNumber, Modal, Tag } from 'antd'
 import React, { useMemo } from 'react'
 import { useModel } from 'umi'
+import '../../../../pages/launchSystemV3/tencentAdPutIn/index.less'
+
 /**新建文件夹,修改非图文素材名称 */
 function FileModal(props: { isAll?: boolean }) {
     const { isAll } = props
     const { state, set, addFolder, nameOk, offEditFile } = useModel(isAll ? 'useLaunchAdq.useBdMedia' : 'useLaunchAdq.useBdMediaPup')
-    const { fileVisible, actionItem, fileName, sort, videoDescription, videoTitle } = state
+    const { fileVisible, actionItem, fileName, sort } = state
     const { state: { selectWx }, initSelectWx } = useModel('useOperating.useWxGroupList')//公众号筛选
+
     const title = useMemo(() => {
         if (actionItem?.folder) {
             return '编辑文件夹'
@@ -19,18 +22,20 @@ function FileModal(props: { isAll?: boolean }) {
         }
         return '新建文件夹'
     }, [actionItem])
+
     return <Modal
         visible={fileVisible}
         destroyOnClose
         onOk={(e) => { actionItem ? nameOk(selectWx) : addFolder(); initSelectWx() }}
         onCancel={() => { offEditFile(); initSelectWx() }}
-        width={400}
-        title={title}
+        width={500}
+        title={<strong>{title}</strong>}
         closable={false}
+        className='modalResetCss'
     >
         <Tag color='warning'>数值越大越靠前</Tag>
         <div style={{ display: 'flex', flexFlow: 'row', marginBottom: 10, alignItems: 'center', marginTop: 10 }}>
-            <label style={{ width: '15%' }}>排序</label>
+            <label style={{ width: '15%', fontWeight: 'bold' }}>排序</label>
             <InputNumber
                 style={{ width: '100%' }}
                 value={sort}
@@ -41,19 +46,17 @@ function FileModal(props: { isAll?: boolean }) {
             />
         </div>
 
-        {
-            <div style={{ display: 'flex', flexFlow: 'row', marginBottom: 10, alignItems: 'center' }}>
-                <label style={{ width: '15%' }}>名称:</label>
-                <Input
-                    value={fileName}
-                    allowClear
-                    placeholder='请输入名称'
-                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
-                        set({ fileName: event.target.value })
-                    }}
-                />
-            </div>
-        }
+        {<div style={{ display: 'flex', flexFlow: 'row', marginBottom: 10, alignItems: 'center' }}>
+            <label style={{ width: '15%', fontWeight: 'bold' }}>名称</label>
+            <Input
+                value={fileName}
+                allowClear
+                placeholder='请输入名称'
+                onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
+                    set({ fileName: event.target.value })
+                }}
+            />
+        </div>}
     </Modal>
 }
 export default React.memo(FileModal)

+ 12 - 8
src/components/FileBoxAD/components/imgsModal/index.tsx

@@ -5,6 +5,7 @@ import { RcFile } from 'antd/lib/upload'
 import CropperImg from '../Cropper'
 import { CloseCircleFilled} from '@ant-design/icons'
 import './index.less'
+import '../../../../pages/launchSystemV3/tencentAdPutIn/index.less'
 
 /**新建非图文素材 */
 let ImgsModal = React.memo((props: { isAll?: boolean }) => {
@@ -34,42 +35,45 @@ let ImgsModal = React.memo((props: { isAll?: boolean }) => {
 
     return <div>
         <Modal
-            title={(actionItem ? '编辑' : '批量新建') + typeEnum[mediaType as string]}
+            title={<strong>{(actionItem ? '编辑' : '批量新建') + typeEnum[mediaType as keyof typeof typeEnum]}</strong>}
             visible={imgsVisrible}
             onOk={(e) => { handleOk(e) }}
             confirmLoading={upLoadLoading}
             onCancel={() => {
                 offEditFile();
             }}
-            width={450}
+            width={700}
             destroyOnClose
             maskClosable={false}
+            className='modalResetCss'
         >
             <Form
                 name="basic"
-                labelCol={{ span: 5 }}
-                wrapperCol={{ span: 19 }}
+                labelCol={{ span: 4 }}
+                wrapperCol={{ span: 20 }}
                 autoComplete="off"
+                colon={false}
+                labelAlign='left'
             >
                 <Form.Item
-                    label="图片名称"
+                    label={<strong>图片名称</strong>}
                 >
                     <Input value={queryForm.title} onChange={(e) => setQueryForm({ ...queryForm, title: e.target.value })} placeholder="请输入图片名称" />
                 </Form.Item>
                 {mediaType === 'IMG' && <Form.Item
-                    label="图片大小"
+                    label={<strong>图片大小</strong>}
                     tooltip="单位KB,0代表不填默认图片300KB;图片太大会压缩到此大小下"
                 >
                     <InputNumber value={queryForm.fileSize} onChange={(e) => setQueryForm({ ...queryForm, fileSize: e })} placeholder="请输入图片大小" />
                 </Form.Item>}
                 <Form.Item
-                    label="排序"
+                    label={<strong>排序</strong>}
                     tooltip="数值越大越靠前"
                 >
                     <InputNumber value={queryForm.sort} onChange={(e) => setQueryForm({ ...queryForm, sort: e })} placeholder="请输入排序" />
                 </Form.Item>
                 <Form.Item
-                    label={'上传' + typeEnum[mediaType as string]}
+                    label={<strong>{'上传' + typeEnum[mediaType as keyof typeof typeEnum]}</strong>}
                 >
                     <Space direction='vertical'>
                         <Image.PreviewGroup>

+ 274 - 9
src/components/FileBoxAD/components/uploadsTable/index.tsx

@@ -1,24 +1,289 @@
-import { Modal } from "antd"
-import { RcFile } from "antd/lib/upload"
-import React from "react"
+import { Badge, Button, Modal, Space, Table } from "antd"
+import React, { useEffect, useRef, useState } from "react"
 import '../../../../pages/launchSystemV3/tencentAdPutIn/index.less'
+import { compressAccurately } from "image-conversion"
+import { blobToBase64, dataURLtoFile, videoMessage } from "@/utils/compress"
+import { getImgSize } from "@/utils/utils"
+import { useAjax } from "@/Hook/useAjax"
+import { bdSysMediaAdd, getFileUrl } from "@/services/launchAdq/material"
+import request from "umi-request"
+import getMD5 from "@/components/MD5"
 
 interface Props {
-    fileList: RcFile[]
+    parentId: any
+    belongUser: any,
+    mediaType?: 'VIDEO' | 'IMG' | 'PAGE',//类型
+    fileList?: FileList
     visible?: boolean
     onClose?: () => void
+    onChange?: () => void
 }
-const UploadsTable: React.FC<Props> = ({ visible, onClose }) => {
+const UploadsTable: React.FC<Props> = ({ fileList, visible, onClose, onChange, mediaType, parentId, belongUser }) => {
 
+    /*******************************/
+    const [dataSource, setDataSource] = useState<any[]>([])
+    const [loading, setLoading] = useState<boolean>(false)
+    const tableRef = useRef(null);
+
+    //请求上传地址
+    const fileUrl = useAjax((params: { type: string, fileType: 'video' | 'image' }) => getFileUrl(params), { manual: true })
+    const add = useAjax((params) => bdSysMediaAdd(params))
+    /*******************************/
+
+    const scrollToRow = (key: number) => {
+        if (tableRef.current) {
+            const tableBody = (tableRef.current as any).querySelector('.ant-table-body');
+            const row = tableBody.querySelector(`.ant-table-row[data-row-key="${key}"]`);
+
+            if (row) {
+                row.scrollIntoView({ behavior: 'smooth', block: 'center' });
+            }
+        }
+    };
+
+    useEffect(() => {
+        if (fileList?.length) {
+            setDataSource([...Array.from(fileList)].map((file, index) => {
+                let type = 'default'
+                let mediaTypeName = '其它文件'
+                if (file.type.startsWith('image/')) {
+                    mediaTypeName = '图片'
+                    if (mediaType !== 'IMG') type = 'warning'
+                } else if (file.type.startsWith('video/')) {
+                    mediaTypeName = '视频'
+                    if (mediaType !== 'VIDEO') type = 'warning'
+                } else {
+                    type = 'warning'
+                }
+                return { id: index + 1, file, type, mediaTypeName }
+            }))
+        } else {
+            setDataSource([])
+        }
+    }, [fileList, mediaType])
+
+    // 上传方法
+    const upload = (file: File) => {
+        return new Promise(async (resolve) => {
+            let fileSize: number = 0
+            if (mediaType === 'IMG') {
+                fileSize = 409600
+            } else {
+                fileSize = 524288000
+            }
+
+            if (mediaType === 'IMG') {
+                if (file?.size > fileSize) { // 大于400kb进入压缩
+                    let bole = await compressAccurately(file, fileSize / 1024 - 50)
+                    if (bole?.size > fileSize) {
+                        bole = await compressAccurately(file, fileSize / 1024 - 100)
+                    }
+                    if (bole?.size > fileSize) {
+                        bole = await compressAccurately(file, fileSize / 1024 - 150)
+                    }
+                    if (bole?.size > fileSize) {
+                        bole = await compressAccurately(file, fileSize / 1024 - 200)
+                    }
+                    let newFile = await blobToBase64(bole)
+                    file = await dataURLtoFile(newFile, file?.name)
+                }
+            } else if (mediaType === 'VIDEO') {
+                if (file?.size > fileSize) { // 大于100mb进入压缩
+                    return resolve(`选择的视频大于${fileSize / 1024 / 1024}MB,请重新选择提交`)
+                }
+            }
+            let width = 0
+            let height = 0
+            if (mediaType === 'IMG') {
+                const imgData = await getImgSize(file as any)
+                width = imgData.width
+                height = imgData.height
+            } else if (mediaType === "VIDEO") {
+                const videoInfo: any = await videoMessage([file as any])
+                width = videoInfo[0].width
+                height = videoInfo[0].height
+            }
+            /**修改文件名以用户设置的文件title命名*/
+            let newFile = new File([file], file?.name, { type: file?.type })
+            let formData = new FormData();
+            fileUrl.run({ type: newFile.type, fileType: mediaType === 'VIDEO' ? 'video' : 'image' }).then(res1 => {
+                Object.keys(res1).forEach((key: string) => {
+                    if (key !== 'url') {
+                        formData.append(key, res1[key])
+                    }
+                })
+                formData.append('file', newFile)
+                /**向阿里云返回的上传地址上传文件*/
+                request(res1?.ossUrl, { method: 'post', body: formData }).then(async (res2: { code: number, data: { url: string } }) => {
+                    if (res2.code === 200) {
+                        if (res2?.data?.url) {
+                            let fileMd5 = await getMD5(newFile)
+                            let obj: any = { title: file?.name?.split('.')[0], mediaType, folder: false, parentId, width, height, fileMd5, belongUser: belongUser === '0' ? false : true, url: res2?.data?.url, sort: 0, fileSize: newFile?.size, fileMime: newFile.type }
+                            if (mediaType === 'VIDEO') {
+                                obj['videoTitle'] = file?.name?.split('.')[0]
+                            }
+                            if (obj.title.match(RegExp(/[<>&\\'"/\x08\x09\x0A\x0D\x7F]/g))) {
+                                obj.title = obj.title.replace(RegExp(/[<>&\\'"/\x08\x09\x0A\x0D\x7F]/g), '')
+                            }
+                            obj.title = obj.title.replace(/\.(jpg|jpeg|gif|png)$/i, '')
+                            add.run(obj).then((res) => {
+                                resolve('')
+                            }).catch(() => resolve(`上传失败`))
+                        }
+                    } else {
+                        resolve(`上传失败`)
+                    }
+                }).catch(() => resolve(`上传失败`))
+            }).catch(() => resolve(`上传失败`))
+        })
+    }
+
+    // 点击上传
+    const handleOk = () => {
+        setLoading(true)
+        async function uoload(index: number) {
+            let fileData = dataSource[index]
+            scrollToRow(index)
+            if (fileData?.type === 'default') {
+                setDataSource(data => {
+                    const newData = [...data]
+                    newData[index].type = 'processing'
+                    return newData
+                })
+                let error = await upload(fileData.file)
+                setDataSource(data => {
+                    const newData = [...data]
+                    newData[index].type = error ? 'error' : 'success'
+                    newData[index].error = error
+                    return newData
+                })
+            }
+            if (dataSource.length > index) {
+                uoload(index + 1)
+            } else { // 上传完成
+                setLoading(false)
+                onChange?.()
+            }
+        }
+        uoload(0)
+    }
 
     return <Modal
-        title={<strong>上传预览</strong>}
+        title={<strong>上传{mediaType === 'IMG' ? '图片' : '视频'}预览</strong>}
         visible={visible}
-        onCancel={onClose}
         className='modalResetCss'
-        width={900}
+        width={1100}
+        closable={false}
+        maskClosable={false}
+        footer={<Space>
+            {!loading && <Button onClick={onClose}>取消</Button>}
+            <Button type="primary" onClick={handleOk} loading={loading}>上传</Button>
+        </Space>}
     >
-
+        <Table
+            dataSource={dataSource}
+            size="small"
+            rowKey={'id'}
+            bordered
+            ref={tableRef}
+            scroll={{ y: 450 }}
+            columns={[
+                {
+                    title: '序号',
+                    dataIndex: 'id',
+                    key: 'id',
+                    width: 60,
+                    align: 'center',
+                    fixed: 'left',
+                    render: text => <span style={{ fontSize: 12 }}>{text}</span>
+                },
+                {
+                    title: '上传状态',
+                    dataIndex: 'type',
+                    key: 'type',
+                    width: 130,
+                    fixed: 'left',
+                    render(value) {
+                        switch (value) {
+                            case 'success':
+                                return <Badge status="success" text={<span style={{ fontSize: 12 }}>上传成功</span>} />
+                            case 'processing':
+                                return <Badge status="processing" text={<span style={{ fontSize: 12 }}>正在上传...</span>} />
+                            case 'error':
+                                return <Badge status="error" text={<span style={{ fontSize: 12 }}>上传失败</span>} />
+                            case 'error_qx':
+                                return <Badge status="error" text={<span style={{ fontSize: 12 }}>取消上传</span>} />
+                            case 'warning':
+                                return <Badge status="warning" text={<span style={{ fontSize: 12 }}>{`当期只能上传${mediaType === 'IMG' ? '图片' : '视频'}`}</span>} />
+                            default:
+                                return <Badge status="default" text={<span style={{ fontSize: 12 }}>等待上传</span>} />
+                        }
+                    },
+                },
+                {
+                    title: '素材类型',
+                    dataIndex: 'mediaTypeName',
+                    key: 'mediaTypeName',
+                    width: 75,
+                    align: 'center',
+                    render: text => <span style={{ fontSize: 12 }}>{text}</span>
+                },
+                {
+                    title: '素材名称',
+                    dataIndex: 'file',
+                    key: 'file',
+                    width: 150,
+                    render(value, record) {
+                        if (record.mediaTypeName === '图片' || record.mediaTypeName === '视频') {
+                            return <span style={{ fontSize: 12 }}>{value?.name}</span>
+                        }
+                        return <span style={{ fontSize: 12 }}>其它文件</span>
+                    },
+                },
+                {
+                    title: '素材',
+                    dataIndex: 'file',
+                    key: 'file',
+                    width: 230,
+                    render(value, record) {
+                        if (record.mediaTypeName === '图片') {
+                            return <img src={URL.createObjectURL(value)} style={{ height: 100, maxWidth: 200 }} />
+                        } else if (record.mediaTypeName === '视频') {
+                            return <video src={URL.createObjectURL(value)} style={{ maxWidth: 200, height: 100 }}></video>
+                        }
+                        return <span style={{ fontSize: 12 }}>其它文件</span>
+                    },
+                },
+                {
+                    title: '错误信息',
+                    dataIndex: 'error',
+                    key: 'error',
+                    width: 300,
+                    render: text => <span style={{ fontSize: 12 }}>{text || '--'}</span>
+                },
+                {
+                    title: '操作',
+                    dataIndex: 'cz',
+                    key: 'cz',
+                    width: 70,
+                    align: 'center',
+                    fixed: 'right',
+                    render(_, record) {
+                        if (record.type === 'default') {
+                            return <a
+                                style={{ fontSize: 12, color: 'red' }}
+                                onClick={() => {
+                                    setDataSource(data => {
+                                        return data.map(item => item.id === record.id ? { ...item, type: 'error_qx' } : item)
+                                    })
+                                }}
+                            >取消上传</a>
+                        }
+                        return '--'
+                    },
+                },
+            ]}
+        />
     </Modal>
 }
 

+ 28 - 13
src/components/FileBoxAD/index.tsx

@@ -13,6 +13,8 @@ import MoveTo from "./components/moveTo"
 import VideoNews from "@/pages/launchSystemNew/components/newsModal/videoNews"
 import { getVideoImgUrl } from "@/utils/utils"
 import useFileDrop from "@/Hook/useFileDrop"
+import { RcFile } from "antd/lib/upload"
+import UploadsTable from "./components/uploadsTable"
 
 interface News {
     id: number,
@@ -295,17 +297,13 @@ function FlieBox(props: Props) {
     
 
     // 获取拖拽的文件
-    // const { isDragOver, dropAreaProps } = useFileDrop((fileList) => {
-    //     let file = fileList[0]
-    //     // handleFileUpload(file as RcFile)
-    //     // if (file.type.startsWith('image/')) {
-            
-    //     // } else if (file.type.startsWith('video/')) {
-            
-    //     // } else {
-            
-    //     // }
-    // });
+    const [fileList, setFileList] = useState<FileList>()
+    const [uploadVisible, setUploadVisible] = useState<boolean>(false)
+    const { isDragOver, dropAreaProps } = useFileDrop((fileList) => {
+        console.log(mediaType, fileList)
+        setFileList(fileList)
+        setUploadVisible(true)
+    });
 
     return <div style={{ display: 'flex', flexFlow: 'row', width: '100%', padding: get_folder_tree?.data?.length ? 0 : '0 16px 0' }}>
         {get_folder_tree?.data?.length > 0 && <div style={{ flexShrink: 0 }}>
@@ -329,9 +327,9 @@ function FlieBox(props: Props) {
                 data-type="right-boxzones"
                 onContextMenu={rightMenu}
                 style={height ? { height } : {}}
-                // {...dropAreaProps}
+                {...(mediaType !== 'PAGE' ? dropAreaProps : {})}
             >
-                {/* {isDragOver && <div className={`${style.placeholder} ${isDragOver ? style.dragOver : ''}`}><span>拖动文件到这里</span></div>} */}
+                {mediaType !== 'PAGE' && isDragOver && <div className={`${style.placeholder} ${isDragOver ? style.dragOver : ''}`}><span>拖动文件到这里</span></div>}
                 {/* 层级路径 */}
                 <div className={style.path} >
                     {//存在渲染个人本地路径不存在执行公共本地
@@ -543,6 +541,23 @@ function FlieBox(props: Props) {
             </div>
             <div className={style.moveSelected} id='moveSelected' />
         </div>
+
+        {uploadVisible && <UploadsTable
+            parentId={parentId}
+            belongUser={belongUser}
+            mediaType={mediaType}
+            visible={uploadVisible}
+            fileList={fileList}
+            onClose={() => {
+                setUploadVisible(false)
+                setFileList(undefined)
+            }}
+            onChange={() => {
+                setUploadVisible(false)
+                setFileList(undefined)
+                list.refresh()
+            }}
+        />}
     </div>
 }
 export default React.memo(FlieBox)

+ 2 - 2
src/models/useLaunchAdq/useBdMedia.ts

@@ -240,9 +240,9 @@ function useBdMediaPup() {
                 let file = item
                 let fileSize = size || 0
                 if (mediaType === 'IMG') {
-                    fileSize = value?.fileSize ? value?.fileSize * 1024 : 307200
+                    fileSize = value?.fileSize ? value?.fileSize * 1024 : 409600
                 } else {
-                    fileSize = 104857600
+                    fileSize = 524288000 
                 }
 
                 if (mediaType === 'IMG') {

+ 1 - 3
src/pages/launchSystemNew/components/selectCloud/index.tsx

@@ -101,9 +101,7 @@ const SelectCloud: React.FC<Props> = (props) => {
         className='modalResetCss'
     >
         <Tabs tabBarStyle={{ marginBottom: 0 }} onChange={(activeKey: any) => { set({ belongUser: activeKey }) }} activeKey={belongUser} style={{ margin: '0 24px' }} tabBarExtraContent={<Space>
-            {
-                mediaType === 'PAGE' ? null : <Button size='small' type="primary" onClick={() => { set({ imgVisrible: true }) }}>新建素材</Button>
-            }
+            {mediaType === 'PAGE' ? null : <Button size='small' type="primary" onClick={() => { set({ imgVisrible: true }) }}>新建素材</Button>}
             <Tooltip title="功能都在下方内容区右键" placement="left">
                 <QuestionCircleFilled />
             </Tooltip>

+ 245 - 0
src/pages/launchSystemV3/components/PageModal/MiniProgramPage.tsx

@@ -0,0 +1,245 @@
+
+import { useAjax } from "@/Hook/useAjax"
+import { CheckOutlined, CloseOutlined, QuestionCircleOutlined, SyncOutlined } from "@ant-design/icons"
+import { Button, Input, message, Modal, Space, Table, Tooltip, Typography } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../GoodsModal/index.less'
+import columns from "./tableConfig"
+import { getWechatAppletApi } from "@/services/adqV3/global"
+import New1Radio from "../New1Radio"
+const { Title, Text } = Typography;
+
+/**
+ * 获取微信小程序
+ * @returns 
+ */
+interface Props {
+    visible?: boolean,
+    onClose?: () => void,
+    onChange?: (value: { data: PULLIN.AccountCreateLogsProps[], landingPageType: 0 | 1 }) => void,
+    adgroups: any
+    dynamic: any
+    data: PULLIN.AccountCreateLogsProps[]
+}
+const MiniProgramPage: React.FC<Props> = (props) => {
+
+    /*************************/
+    const { visible, onClose, data: data1, onChange, dynamic } = props
+    const { creativeTemplateId, deliveryMode, creativeComponents: { mainJumpInfo }, landingPageType } = dynamic
+    const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
+    const [data, setData] = useState<PULLIN.AccountCreateLogsProps[]>(data1 || [])
+    const [queryForm, setQueryForm] = useState<{ appletName?: string, appletIdList?: string[], pageSize: number, pageNum: number }>({ pageNum: 1, pageSize: 20 })
+    const [pageAllocationType, setPageAllocationType] = useState<0 | 1>(landingPageType || 0)
+
+    const listAjax = useAjax((params) => getWechatAppletApi(params))
+    /*************************/
+
+    useEffect(() => {
+        getList()
+    }, [queryForm])
+
+    // 获取落地页列表
+    const getList = () => {
+        listAjax.run(queryForm)
+    }
+
+    const handleOk = () => {
+        if (deliveryMode === "DELIVERY_MODE_COMPONENT" && !data?.every(item => item.pageList?.length === mainJumpInfo?.length)) {
+            message.error(`当前落地页数量不足,跳转类型选择了${mainJumpInfo.length}组,落地页应当选择${mainJumpInfo.length}组`)
+            return
+        }
+        if (data?.every(item => item.pageList)) {
+            onChange && onChange({ data, landingPageType: pageAllocationType })
+        } else {
+            message.error('还有账号未选择落地页!')
+        }
+    }
+
+    /** 设置选中广告主 */
+    const handleSelectAdz = (value: number, item: any) => {
+        if (value === selectAdz) {
+            return
+        }
+        setSelectAdz(value)
+    }
+
+    /** 一键设置 */
+    const setOnekey = () => {
+        const hide = message.loading(`正在设置...`, 0, () => {
+            message.success('设置成功');
+        });
+        let newData: PULLIN.AccountCreateLogsProps[] = JSON.parse(JSON.stringify(data))
+        let pageList = data[selectAdz - 1]['pageList']
+        newData = newData.map(item => {
+            if (item.accountId !== data[selectAdz - 1].accountId) {
+                return { ...item, pageList: pageList }
+            }
+            return item
+        })
+        setData(newData)
+        message.success('设置完成');
+        hide()
+    }
+
+    return <Modal
+        title={<Space>
+            <strong>选择微信小程序</strong>
+            {data?.length > 1 && <Button style={{ padding: 0, margin: 0 }} disabled={!data[selectAdz - 1]['pageList']?.length} onClick={setOnekey} type="link" loading={listAjax.loading}>
+                <Space>
+                    <span style={{ fontSize: 12 }}>一键设置</span>
+                    <Tooltip color="#FFF" overlayInnerStyle={{ color: '#000' }} title="设置其它账号有相同名称的落地页为那个账号的落地页(需要落地页名称相同,否则不设置,注意只会根据当前条件去搜索,比如选择了被授权落地页,只会去被授权落地页里找对应落地页)">
+                        <QuestionCircleOutlined />
+                    </Tooltip>
+                </Space>
+            </Button>}
+        </Space>}
+        visible={visible}
+        onCancel={() => { onClose && onClose() }}
+        onOk={handleOk}
+        width={1100}
+        className={`${style.SelectPackage} modalResetCss`}
+        bodyStyle={{ padding: '0 10px 0 10px' }}
+    >
+        {[910].includes(creativeTemplateId) && <div className={style.pageType}>
+            <Space size={12}>
+                <strong>落地页分配规则</strong>
+                <New1Radio
+                    data={[{ label: '全部相同', value: 0 }, { label: '平均分配', value: 1 }]}
+                    value={pageAllocationType}
+                    onChange={(e) => {
+                        setPageAllocationType(e)
+                    }}
+                />
+            </Space>
+        </div>}
+        <div className={style.content}>
+            <div className={style.left}>
+                <h4 className={style.title}>媒体账户</h4>
+                {data?.map((item, index) => (
+                    <div key={index} onClick={() => { handleSelectAdz(index + 1, item) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
+                        {item?.accountId}
+                        {data[index].pageList?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
+                    </div>
+                ))}
+            </div>
+            <div className={style.right}>
+                <Space style={{ marginBottom: 10 }}>
+                    <Input style={{ width: 200 }} placeholder="请输入小程序名称" value={queryForm?.appletName} allowClear onChange={(e) => setQueryForm({ ...queryForm, appletName: e.target.value, pageNum: 1 })} />
+                    <Input.TextArea
+                        style={{ width: 300 }}
+                        placeholder="请输入小程序原始ID(多个逗号隔开)"
+                        allowClear
+                        rows={1}
+                        onChange={(e) => {
+                            let value = e.target.value
+                            let arr: string[] = []
+                            if (value) {
+                                value = value.replace(/[,,\s]/g, ',')
+                                arr = value.split(',').filter((a: string) => a)
+                            }
+                            setQueryForm({ ...queryForm, appletIdList: arr, pageNum: 1 })
+                        }}
+                    />
+                    <Button style={{ padding: 0, margin: 0 }} icon={<SyncOutlined />} type='link' loading={listAjax?.loading} onClick={() => { listAjax?.refresh() }}><span style={{ fontSize: 12 }}>刷新</span></Button>
+                </Space>
+                <Table
+                    columns={columns(3)}
+                    dataSource={listAjax.data?.records?.map((item: { appletName: any; id: any }) => ({ ...item, pageName: item.appletName, pageId: item.id }))}
+                    size="small"
+                    loading={listAjax?.loading}
+                    scroll={{ y: 300 }}
+                    bordered
+                    rowKey={'id'}
+                    pagination={{
+                        defaultPageSize: 20,
+                        current: listAjax.data?.current || 1,
+                        pageSize: listAjax.data?.size || 10,
+                        total: listAjax.data?.total || 0
+                    }}
+                    onChange={(pagination) => {
+                        const { current, pageSize } = pagination
+                        setQueryForm({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
+                    }}
+                    rowSelection={{
+                        type: ([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT") ? 'checkbox' : 'radio',
+                        getCheckboxProps: (record) => {
+                            // 组件化创意
+                            if (deliveryMode === "DELIVERY_MODE_COMPONENT") {
+                                if (data[selectAdz - 1]?.pageList?.length >= mainJumpInfo?.length) {
+                                    return {
+                                        disabled: data[selectAdz - 1]?.pageList?.some((item: any) => item?.id === record?.id) ? false : true
+                                    }
+                                } else {
+                                    return {
+                                        disabled: false
+                                    }
+                                }
+                            }
+                            return {
+                                disabled: false
+                            }
+                        },
+                        selectedRowKeys: data[selectAdz - 1]?.pageList?.map((item: any) => item?.id),
+                        hideSelectAll: deliveryMode === "DELIVERY_MODE_COMPONENT",
+                        onSelect: (record: any, selected: boolean) => {
+                            let newData = JSON.parse(JSON.stringify(data))
+                            if (([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT")) {
+                                let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
+                                if (selected) {
+                                    selectedRows.push({ ...record })
+                                    newData[selectAdz - 1]['pageList'] = selectedRows
+                                } else {
+                                    newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { id: number }) => item.id !== record.id)
+                                }
+                            } else {
+                                newData[selectAdz - 1]['pageList'] = [record]
+                            }
+                            setData([...newData])
+                        },
+                        onSelectAll: (selected: boolean, _: any[], changeRows: { id: number }[]) => {
+                            let newData = JSON.parse(JSON.stringify(data))
+                            let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
+                            if (selected) {
+                                let newSelectAccData = [...selectedRows]
+                                changeRows.forEach((item: { id: number }) => {
+                                    let index = newSelectAccData.findIndex((ite: { id: number }) => ite.id === item.id)
+                                    if (index === -1) {
+                                        let data: any = { ...item }
+                                        newSelectAccData.push(data)
+                                    }
+                                })
+                                newData[selectAdz - 1]['pageList'] = newSelectAccData
+                            } else {
+                                newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { id: number }) => {
+                                    let index = changeRows.findIndex((ite: { id: number }) => ite.id === item.id)
+                                    if (index !== -1) {
+                                        return false
+                                    } else {
+                                        return true
+                                    }
+                                })
+                            }
+                            setData([...newData])
+                        }
+                    }}
+                />
+            </div>
+            <div className={style.center}>
+                <Title level={5}>已选:{data[selectAdz - 1]?.pageList?.length || 0}</Title>
+                <div className={style.select_content} style={{ height: 416 }}>
+                    {data[selectAdz - 1]?.pageList?.map((item: any) => <div key={item.id}>
+                        <Text ellipsis={{ tooltip: true }} className={style.marketingAssetName}>{item.appletName}</Text>
+                        <CloseOutlined className={style.close} onClick={() => {
+                            let newData: PULLIN.AccountCreateLogsProps[] = JSON.parse(JSON.stringify(data))
+                            newData[selectAdz - 1].pageList = newData[selectAdz - 1]?.pageList?.filter((i: any) => i?.id !== item.id)
+                            setData(newData)
+                        }} />
+                    </div>)}
+                </div>
+            </div>
+        </div>
+
+    </Modal>
+}
+
+export default React.memo(MiniProgramPage)

+ 78 - 1
src/pages/launchSystemV3/components/PageModal/tableConfig.tsx

@@ -3,7 +3,7 @@ import { Badge, TableProps, Tag } from "antd"
 import React from "react"
 import QrCode from "../QrCode"
 
-let columns = (type?: 1 | 2): TableProps<any>['columns'] => {
+let columns = (type?: 1 | 2 | 3): TableProps<any>['columns'] => {
 
     if (type === 2) {  // 灵鹊落地页
         return [
@@ -50,6 +50,83 @@ let columns = (type?: 1 | 2): TableProps<any>['columns'] => {
                 width: 200
             },
         ]
+    } else if (type === 3) { // 微信小程序
+        return [
+            {
+                title: '微信小程序名称',
+                dataIndex: 'appletName',
+                key: 'appletName',
+                width: 160,
+                ellipsis: true,
+                fixed: 'left',
+                render: (a) => {
+                    return <span style={{ fontSize: "12px" }}>{a}</span>
+                }
+            },
+            {
+                title: '微信小程序原始ID',
+                dataIndex: 'appletId',
+                key: 'appletId',
+                width: 180,
+                ellipsis: true,
+                fixed: 'left',
+                render: (a) => {
+                    return <span style={{ fontSize: "12px" }}>{a}</span>
+                }
+            },
+            {
+                title: '微信小程序路径',
+                dataIndex: 'appletLink',
+                key: 'appletLink',
+                width: 380,
+                ellipsis: true,
+                render: (a) => {
+                    return <span style={{ fontSize: "12px" }}>{a}</span>
+                }
+            },
+            {
+                title: '详细描述',
+                dataIndex: 'description',
+                key: 'description',
+                ellipsis: true,
+                render: (a) => {
+                    return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+                }
+            },
+            {
+                title: '创建人',
+                dataIndex: 'createByName',
+                key: 'createByName',
+                align: 'center',
+                width: 75,
+                ellipsis: true,
+                render: (a) => {
+                    return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+                }
+            },
+            {
+                title: '更新人',
+                dataIndex: 'createByName',
+                key: 'createByName',
+                align: 'center',
+                width: 75,
+                ellipsis: true,
+                render: (a) => {
+                    return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+                }
+            },
+            {
+                title: '创建时间',
+                dataIndex: 'createTime',
+                key: 'createTime',
+                align: 'center',
+                width: 140,
+                ellipsis: true,
+                render: (a) => {
+                    return <span style={{ fontSize: "12px" }}>{a}</span>
+                }
+            }
+        ]
     }
     return [
         {

+ 5 - 3
src/pages/launchSystemV3/components/TextAideInput/index.tsx

@@ -65,8 +65,8 @@ const TextAideInput: React.FC<Props> = (props) => {
                 onFocus={() => {
                     if (isShowAjax) {
                         setDescriptionshow(true)
-                    }
-                    textList(value)
+                        textList(value)
+                    } 
                 }}
                 onBlur={() => {
                     setTimeout(() => { setDescriptionshow(false) }, 500)
@@ -74,7 +74,9 @@ const TextAideInput: React.FC<Props> = (props) => {
                 onChange={(e) => {
                     let value = e.target.value
                     setText(value)
-                    textList(value)
+                    if (isShowAjax) {
+                        textList(value)
+                    }
                     onChange && onChange(value)
                 }}
                 allowClear

+ 187 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/index.tsx

@@ -0,0 +1,187 @@
+import { useAjax } from "@/Hook/useAjax";
+import { delCorpWechatApi, getCorpWechatAllApi, getCorpWechatApi, getCorpWechatDetailApi, getWechatAppletAllApi, getWechatAppletDetailApi } from "@/services/adqV3/global";
+import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
+import { Button, Card, Divider, Input, message, Select, Table, Typography } from "antd";
+import React, { useEffect, useState } from "react"
+import '../../tencentAdPutIn/index.less'
+import Modify from "./modify";
+import columns from "./tableConfig";
+const { Text, Link, Paragraph } = Typography;
+
+/**
+ * 企业微信
+ * @returns 
+ */
+const CorpWechat: React.FC = () => {
+
+    /**********************************/
+    const [queryForm, setQueryForm] = useState<{ pageNum: number, pageSize: number, wechatName?: string, wechatIdList?: string[] }>({ pageNum: 1, pageSize: 20 })
+    const [queryFormNew, setQueryFormNew] = useState<{ pageNum: number, pageSize: number, wechatName?: string, wechatIdList?: string[] }>({ pageNum: 1, pageSize: 20 })
+    const [visible, setVisible] = useState<boolean>(false)
+
+    const getCorpWechat = useAjax((params) => getCorpWechatApi(params))
+    const delCorpWechat = useAjax((params) => delCorpWechatApi(params))
+    /**********************************/
+
+    useEffect(() => {
+        getCorpWechat.run(queryFormNew)
+    }, [queryFormNew])
+
+    const del = (id: number) => {
+        delCorpWechat.run(id).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getCorpWechat.refresh()
+            }
+        })
+    }
+
+    return <Card
+        className="cardResetCss"
+        title={<div className="flexStart" style={{ gap: 8 }}>
+            <Input style={{ width: 200 }} placeholder="请输入企业微信名称" value={queryForm?.wechatName} allowClear onChange={(e) => setQueryForm({ ...queryForm, wechatName: e.target.value, pageNum: 1 })} />
+            <Input.TextArea
+                style={{ width: 300 }}
+                placeholder="请输入企业微信ID(多个逗号隔开)"
+                allowClear
+                rows={1}
+                onChange={(e) => {
+                    let value = e.target.value
+                    let arr: string[] = []
+                    if (value) {
+                        value = value.replace(/[,,\s]/g, ',')
+                        arr = value.split(',').filter((a: string) => a)
+                    }
+                    setQueryForm({ ...queryForm, wechatIdList: arr, pageNum: 1 })
+                }}
+            />
+            <Button type="primary" icon={<SearchOutlined />} onClick={() => setQueryFormNew({ ...queryForm })}>搜索</Button>
+            <Button type="primary" icon={<PlusOutlined />} onClick={() => { setVisible(true) }}>新增企业微信</Button>
+        </div>}
+    >
+
+        <Table
+            columns={columns(del)}
+            dataSource={getCorpWechat.data?.records}
+            size="small"
+            loading={getCorpWechat?.loading}
+            scroll={{ y: 600 }}
+            bordered
+            rowKey={'id'}
+            pagination={{
+                defaultPageSize: 20,
+                current: getCorpWechat.data?.current || 1,
+                pageSize: getCorpWechat.data?.size || 10,
+                total: getCorpWechat.data?.total || 0
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryForm({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
+                setQueryFormNew({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
+            }}
+        />
+
+        {/* 新增修改 */}
+        {visible && <Modify
+            visible={visible}
+            onClose={() => {
+                setVisible(false)
+            }}
+            onChange={() => {
+                setVisible(false)
+                getCorpWechat.refresh()
+            }}
+        />}
+    </Card>
+}
+
+
+/**
+ * 选择企微
+ * @param param0 
+ * @returns 
+ */
+export const SelectCorpWechat: React.FC<{ value?: number, onChange?: (value?: number) => void }> = ({ value, onChange }) => {
+
+    /*******************************/
+    const [visible, setVisible] = useState<boolean>(false)
+    const getCorpWechatAll = useAjax((params) => getCorpWechatAllApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        getCorpWechatAll.run({})
+    }, [])
+
+    return <>
+        <Select
+            showSearch
+            allowClear
+            placeholder="请选择小程序"
+            filterOption={(input: any, option: any) => {
+                return option!.name?.toString().toLowerCase().includes(input.toLowerCase())
+            }}
+            style={{ width: 480 }}
+            dropdownRender={menu => <>
+                {menu}
+                <Divider style={{ margin: '8px 0' }} />
+                <div>
+                    <Button type="link" onClick={() => {
+                        window.location.href = '/#/launchSystemV3/tencenTasset/corpWechat'
+                    }}>前往管理</Button>
+                    <Button type="link" style={{ paddingLeft: 0, paddingRight: 0 }} onClick={() => {
+                        setVisible(true)
+                    }}>新增</Button>
+                    {/* <Link href="/#/launchSystemV3/tencenTasset/miniProgramWechat" target="_blank">
+                    前往管理
+                </Link> */}
+                </div>
+            </>}
+            value={value}
+            onChange={(e) => onChange?.(e)}
+        >
+            {getCorpWechatAll?.data?.map((item: { id: number, wechatId: string, wechatName: string }) => {
+                return <Select.Option value={item.id} key={item.id} name={item.wechatName + `(${item.wechatId})`}><Text strong>{item.wechatName}</Text><Text type="secondary">{`(${item.wechatId})`}</Text></Select.Option>
+            })}
+        </Select>
+        {/* 新增修改 */}
+        {visible && <Modify
+            visible={visible}
+            onClose={() => {
+                setVisible(false)
+            }}
+            onChange={() => {
+                setVisible(false)
+                getCorpWechatAll.refresh()
+            }}
+        />}
+    </>
+}
+
+
+/**
+ * 展示企微
+ * @param param0 
+ * @returns 
+ */
+export const ShowCorpWechatDetail: React.FC<{ id: number }> = ({ id }) => {
+
+    /*******************************/
+    const getCorpWechatDetail = useAjax((params) => getCorpWechatDetailApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        if (id) {
+            getCorpWechatDetail.run(id)
+        }
+    }, [id])
+
+    return <Paragraph style={{ fontSize: 12, wordBreak: 'break-all', marginBottom: 0 }} ellipsis={{ rows: 2 }}>
+        {getCorpWechatDetail.data ? <>
+            <Text strong>企业微信:{getCorpWechatDetail.data?.wechatName}</Text><Text type="secondary">{`(${getCorpWechatDetail.data?.wechatId}})`}</Text>
+        </> : id}
+    </Paragraph>
+}
+
+export default CorpWechat

+ 67 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/modify.tsx

@@ -0,0 +1,67 @@
+import { useAjax } from "@/Hook/useAjax"
+import { addCorpWechatApi } from "@/services/adqV3/global"
+import { Form, Input, message, Modal } from "antd"
+import React from "react"
+import '../../tencentAdPutIn/index.less'
+
+
+interface Props {
+    visible?: boolean
+    onChange?: () => void
+    onClose?: () => void
+}
+
+/**
+ * 新增企业微信
+ * @returns 
+ */
+const Modify: React.FC<Props> = ({ visible, onChange, onClose }) => {
+
+    /********************************/
+    const [form] = Form.useForm()
+    const addCorpWechat = useAjax((params) => addCorpWechatApi(params))
+    /********************************/
+
+    const handleOk = () => {
+        form.validateFields().then(valid => {
+            console.log(valid)
+            addCorpWechat.run(valid).then(res => {
+                if (res) {
+                    message.success('新增成功')
+                    onChange?.()
+                }
+            })
+        })
+    }
+
+    return <Modal
+        title={<strong>{`新增企业微信`}</strong>}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        className="modalResetCss"
+        confirmLoading={addCorpWechat.loading}
+    >
+        <Form
+            name="basicMiniProgramWechat"
+            form={form}
+            layout='vertical'
+            autoComplete="off"
+        >
+            <Form.Item label={<strong>企业微信名称</strong>} name="wechatName" rules={[{ required: true, message: '请输入企业微信名称!' }]}>
+                <Input placeholder="请输入企业微信名称" allowClear/>
+            </Form.Item>
+            <Form.Item
+                label={<strong>企业微信ID</strong>}
+                name="wechatId"
+                rules={[
+                    { required: true, message: '请输入企业微信ID!' }
+                ]}
+            >
+                <Input placeholder="请输入企业微信ID" allowClear/>
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(Modify)

+ 86 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/tableConfig.tsx

@@ -0,0 +1,86 @@
+import { copy } from "@/utils/utils"
+import { Space, Popconfirm, TableProps } from "antd"
+import React from "react"
+
+
+const columns = (del: (id: number) => void): TableProps<any>['columns'] => {
+
+
+    const data: TableProps<any>['columns'] = [
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 70,
+            render: (_, b) => {
+                return <Space>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del(b.id)}
+                        okText="是"
+                        cancelText="否"
+                    >
+                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+        {
+            title: '企业微信名称',
+            dataIndex: 'wechatName',
+            key: 'wechatName',
+            ellipsis: true,
+            width: 180,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '企业微信ID',
+            dataIndex: 'wechatId',
+            key: 'wechatId',
+            ellipsis: true,
+            render: (a) => {
+                return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a}</a>
+            }
+        },
+        {
+            title: '创建人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 75,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '更新人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 75,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 140,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        }
+    ]
+
+    return data
+}
+
+export default columns

+ 197 - 0
src/pages/launchSystemV3/tencenTasset/miniProgramWechat/index.tsx

@@ -0,0 +1,197 @@
+import { useAjax } from "@/Hook/useAjax";
+import { delWechatAppletApi, getWechatAppletAllApi, getWechatAppletApi, getWechatAppletDetailApi } from "@/services/adqV3/global";
+import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
+import { Button, Card, Divider, Input, message, Select, Table, Typography } from "antd";
+import React, { useEffect, useState } from "react"
+import '../../tencentAdPutIn/index.less'
+import Modify from "./modify";
+import columns from "./tableConfig";
+const { Text, Link, Paragraph } = Typography;
+
+/**
+ * 微信小程序
+ * @returns 
+ */
+const MiniProgramWechat: React.FC = () => {
+
+    /**********************************/
+    const [queryForm, setQueryForm] = useState<{ pageNum: number, pageSize: number, appletName?: string, appletIdList?: string[] }>({ pageNum: 1, pageSize: 20 })
+    const [queryFormNew, setQueryFormNew] = useState<{ pageNum: number, pageSize: number, appletName?: string, appletIdList?: string[] }>({ pageNum: 1, pageSize: 20 })
+    const [visible, setVisible] = useState<boolean>(false)
+    const [initialValues, setInitialValues] = useState<any>({})
+
+    const getWechatApplet = useAjax((params) => getWechatAppletApi(params))
+    const delWechatApplet = useAjax((params) => delWechatAppletApi(params))
+    /**********************************/
+
+    useEffect(() => {
+        getWechatApplet.run(queryFormNew)
+    }, [queryFormNew])
+
+    const del = (id: number) => {
+        delWechatApplet.run(id).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getWechatApplet.refresh()
+            }
+        })
+    }
+
+    const edit = (data: any) => {
+        setInitialValues(data)
+        setVisible(true)
+    }
+
+    return <Card
+        className="cardResetCss"
+        title={<div className="flexStart" style={{ gap: 8 }}>
+            <Input style={{ width: 200 }} placeholder="请输入小程序名称" value={queryForm?.appletName} allowClear onChange={(e) => setQueryForm({ ...queryForm, appletName: e.target.value, pageNum: 1 })} />
+            <Input.TextArea
+                style={{ width: 300 }}
+                placeholder="请输入小程序原始ID(多个逗号隔开)"
+                allowClear
+                rows={1}
+                onChange={(e) => {
+                    let value = e.target.value
+                    let arr: string[] = []
+                    if (value) {
+                        value = value.replace(/[,,\s]/g, ',')
+                        arr = value.split(',').filter((a: string) => a)
+                    }
+                    setQueryForm({ ...queryForm, appletIdList: arr, pageNum: 1 })
+                }}
+            />
+            <Button type="primary" icon={<SearchOutlined />} onClick={() => setQueryFormNew({ ...queryForm })}>搜索</Button>
+            <Button type="primary" icon={<PlusOutlined />} onClick={() => { setVisible(true) }}>新增小程序</Button>
+        </div>}
+    >
+
+        <Table
+            columns={columns(del, edit)}
+            dataSource={getWechatApplet.data?.records}
+            size="small"
+            loading={getWechatApplet?.loading}
+            scroll={{ y: 600 }}
+            bordered
+            rowKey={'id'}
+            pagination={{
+                defaultPageSize: 20,
+                current: getWechatApplet.data?.current || 1,
+                pageSize: getWechatApplet.data?.size || 10,
+                total: getWechatApplet.data?.total || 0
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryForm({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
+                setQueryFormNew({ ...queryForm, pageNum: current || 1, pageSize: pageSize || 10 })
+            }}
+        />
+
+        {/* 新增修改 */}
+        {visible && <Modify
+            visible={visible}
+            initialValues={initialValues}
+            onClose={() => {
+                setVisible(false)
+                setInitialValues({})
+            }}
+            onChange={() => {
+                setVisible(false)
+                getWechatApplet.refresh()
+                setInitialValues({})
+            }}
+        />}
+    </Card>
+}
+
+
+/**
+ * 选择小程序
+ * @param param0 
+ * @returns 
+ */
+export const SelectMiniProgramWechat: React.FC<{ value?: number, onChange?: (value?: number) => void }> = ({ value, onChange }) => {
+
+    /*******************************/
+    const [visible, setVisible] = useState<boolean>(false)
+    const getWechatAppletAll = useAjax((params) => getWechatAppletAllApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        getWechatAppletAll.run({})
+    }, [])
+
+    return <>
+        <Select
+            showSearch
+            allowClear
+            placeholder="请选择小程序"
+            filterOption={(input: any, option: any) => {
+                return option!.name?.toString().toLowerCase().includes(input.toLowerCase())
+            }}
+            style={{ width: 480 }}
+            dropdownRender={menu => <>
+                {menu}
+                <Divider style={{ margin: '8px 0' }} />
+                <div>
+                    <Button type="link" onClick={() => {
+                        window.location.href = '/#/launchSystemV3/tencenTasset/miniProgramWechat'
+                    }}>前往管理</Button>
+                    <Button type="link" style={{ paddingLeft: 0, paddingRight: 0 }} onClick={() => {
+                        setVisible(true)
+                    }}>新增</Button>
+                    {/* <Link href="/#/launchSystemV3/tencenTasset/miniProgramWechat" target="_blank">
+                    前往管理
+                </Link> */}
+                </div>
+            </>}
+            value={value}
+            onChange={(e) => onChange?.(e)}
+        >
+            {getWechatAppletAll?.data?.map((item: { id: number, appletId: string, appletName: string, appletLink: string }) => {
+                return <Select.Option value={item.id} key={item.id} name={item.appletName + `(${item.appletId}_${item.appletLink})`}><Text strong>{item.appletName}</Text><Text type="secondary">{`(${item.appletId}_${item.appletLink})`}</Text></Select.Option>
+            })}
+        </Select>
+        {/* 新增修改 */}
+        {visible && <Modify
+            visible={visible}
+            initialValues={{}}
+            onClose={() => {
+                setVisible(false)
+            }}
+            onChange={() => {
+                setVisible(false)
+                getWechatAppletAll.refresh()
+            }}
+        />}
+    </>
+}
+
+
+/**
+ * 展示小程序详情
+ * @param param0 
+ * @returns 
+ */
+export const ShowMiniProgramWechatDetail: React.FC<{ id: number }> = ({ id }) => {
+
+    /*******************************/
+    const getWechatAppletDetail = useAjax((params) => getWechatAppletDetailApi(params))
+    /*******************************/
+
+    // 获取列表
+    useEffect(() => {
+        if (id) {
+            getWechatAppletDetail.run(id)
+        }
+    }, [id])
+
+    return <Paragraph style={{ fontSize: 12, wordBreak: 'break-all', marginBottom: 0 }} ellipsis={{ rows: 2 }}>
+        {getWechatAppletDetail.data ? <>
+            <Text strong>微信小程序:{getWechatAppletDetail.data?.appletName}</Text><Text type="secondary">{`(${getWechatAppletDetail.data?.appletId}_${getWechatAppletDetail.data?.appletLink})`}</Text>
+        </> : id}
+    </Paragraph>
+}
+
+export default MiniProgramWechat

+ 94 - 0
src/pages/launchSystemV3/tencenTasset/miniProgramWechat/modify.tsx

@@ -0,0 +1,94 @@
+import { useAjax } from "@/Hook/useAjax"
+import { addWechatAppletApi, updateWechatAppletApi } from "@/services/adqV3/global"
+import { Form, Input, message, Modal } from "antd"
+import React from "react"
+import '../../tencentAdPutIn/index.less'
+
+
+interface Props {
+    visible?: boolean
+    onChange?: () => void
+    onClose?: () => void,
+    initialValues?: any
+}
+
+/**
+ * 新增微信小程序
+ * @returns 
+ */
+const Modify: React.FC<Props> = ({ visible, onChange, onClose, initialValues }) => {
+
+    /********************************/
+    const [form] = Form.useForm()
+    const addWechatApplet = useAjax((params) => addWechatAppletApi(params))
+    const updateWechatApplet = useAjax((params) => updateWechatAppletApi(params))
+    /********************************/
+
+    const handleOk = () => {
+        form.validateFields().then(valid => {
+            console.log(valid)
+            if (initialValues?.id) {
+                updateWechatApplet.run({ ...valid, id: initialValues.id }).then(res => {
+                    if (res) {
+                        message.success('修改成功')
+                        onChange?.()
+                    }
+                })
+            } else {
+                addWechatApplet.run(valid).then(res => {
+                    if (res) {
+                        message.success('新增成功')
+                        onChange?.()
+                    }
+                })
+            }
+        })
+    }
+
+    return <Modal
+        title={<strong>{`新增微信小程序`}</strong>}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        className="modalResetCss"
+        confirmLoading={addWechatApplet.loading || updateWechatApplet.loading}
+    >
+        <Form
+            name="basicMiniProgramWechat"
+            form={form}
+            layout='vertical'
+            autoComplete="off"
+            initialValues={{ ...initialValues }}
+        >
+            <Form.Item label={<strong>微信小程序名称</strong>} name="appletName" rules={[{ required: true, message: '请输入微信小程序名称!' }]}>
+                <Input placeholder="请输入微信小程序名称" allowClear />
+            </Form.Item>
+            <Form.Item 
+                label={<strong>微信小程序原生ID</strong>} 
+                name="appletId" 
+                rules={[
+                    { required: true, message: '请输入微信小程序原生ID!' },
+                    {
+                        required: true, message: '微信小程序原生ID以gh_开头,请修改', validator(_, value) {
+                            let reg = /^gh_/i
+                            if (!(value && reg.test(value))) {
+                                return Promise.reject()
+                            }
+                            return Promise.resolve()
+                        }
+                    }
+                ]}
+            >
+                <Input placeholder="请输入微信小程序原生ID(gh_开头)" allowClear />
+            </Form.Item>
+            <Form.Item label={<strong>微信小程序路径</strong>} name="appletLink" rules={[{ required: true, message: '请输入微信小程序路径!' }]}>
+                <Input placeholder="请输入微信小程序路径(pages/index/index)" />
+            </Form.Item>
+            <Form.Item label={<strong>详细描述</strong>} name="description">
+                <Input.TextArea placeholder="请输入详细描述" maxLength={120} allowClear />
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(Modify)

+ 107 - 0
src/pages/launchSystemV3/tencenTasset/miniProgramWechat/tableConfig.tsx

@@ -0,0 +1,107 @@
+import { copy } from "@/utils/utils"
+import { Space, Popconfirm, TableProps } from "antd"
+import React from "react"
+
+
+const columns = (del: (id: number) => void, edit: (data: any) => void): TableProps<any>['columns'] => {
+
+
+    const data: TableProps<any>['columns'] = [
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 90,
+            render: (_, b) => {
+                return <Space>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del(b.id)}
+                        okText="是"
+                        cancelText="否"
+                    >
+                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                    </Popconfirm>
+                    <a style={{ fontSize: 12 }} onClick={() => edit(b)}>修改</a>
+                </Space>
+            }
+        },
+        {
+            title: '微信小程序名称',
+            dataIndex: 'appletName',
+            key: 'appletName',
+            width: 160,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '微信小程序原始ID',
+            dataIndex: 'appletId',
+            key: 'appletId',
+            width: 180,
+            ellipsis: true,
+            render: (a) => {
+                return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a}</a>
+            }
+        },
+        {
+            title: '微信小程序路径',
+            dataIndex: 'appletLink',
+            key: 'appletLink',
+            width: 380,
+            ellipsis: true,
+            render: (a) => {
+                return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a}</a>
+            }
+        },
+        {
+            title: '详细描述',
+            dataIndex: 'description',
+            key: 'description',
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '创建人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 75,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '更新人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 75,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 140,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        }
+    ]
+
+    return data
+}
+
+export default columns

+ 11 - 8
src/pages/launchSystemV3/tencenTasset/profiles/index.tsx

@@ -1,13 +1,13 @@
 import { useAjax } from "@/Hook/useAjax";
-import { addProfilesApi, delProfilesApi, delSysBrandApi, getProfilesApi } from "@/services/adqV3/global";
+import { addProfilesApi, delProfilesApi, getProfilesApi } from "@/services/adqV3/global";
 import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
-import { Button, Card, Form, Input, Modal, Select, Space, Table, message, Image, Divider } from "antd";
+import { Button, Card, Form, Input, Modal, Select, Space, Table, message, Image, Divider, Typography } from "antd";
 import React, { useEffect, useState } from "react"
 import '../../tencentAdPutIn/index.less'
 import profilesColumns from "./tableConfig";
 import SelectCloud from "@/pages/launchSystemNew/components/selectCloud";
 import { useModel } from "umi";
-
+const { Link } = Typography;
 
 const Profiles: React.FC = () => {
 
@@ -65,7 +65,7 @@ const Profiles: React.FC = () => {
             dataSource={getProfiles?.data}
             size="small"
             loading={getProfiles?.loading}
-            scroll={{ y: 300 }}
+            scroll={{ y: 600 }}
             bordered
             rowKey={'id'}
         />
@@ -82,10 +82,10 @@ const Profiles: React.FC = () => {
                     <UploadImageOrMd5 />
                 </Form.Item>
                 <Form.Item label={<strong>昵称</strong>} name="profileName" rules={[{ required: true, message: '请输入昵称!' }]}>
-                    <Input placeholder="请输入称" maxLength={12} />
+                    <Input placeholder="请输入称" maxLength={12} />
                 </Form.Item>
                 <Form.Item label={<strong>详细描述</strong>} name="description">
-                    <Input.TextArea placeholder="请输入名称" maxLength={120} />
+                    <Input.TextArea placeholder="请输入详细描述" maxLength={120} />
                 </Form.Item>
             </Form>
         </Modal>}
@@ -145,7 +145,7 @@ const UploadImageOrMd5: React.FC<ImageProps> = (props) => {
 
 
 
-export const SelectProfiles: React.FC<{ value?: number, onChange?: (value?: number) => void }> = ({value, onChange}) => {
+export const SelectProfiles: React.FC<{ value?: number, onChange?: (value?: number) => void }> = ({ value, onChange }) => {
 
     /*******************************/
     const getProfiles = useAjax((params) => getProfilesApi(params))
@@ -169,8 +169,11 @@ export const SelectProfiles: React.FC<{ value?: number, onChange?: (value?: numb
             <Divider style={{ margin: '8px 0' }} />
             <div>
                 <Button type="link" onClick={() => {
-                    window.location.href = '/#/launchSystemV3/tencentAdPutIn/create'
+                    window.location.href = '/#/launchSystemV3/tencenTasset/profiles'
                 }}>前往管理</Button>
+                {/* <Link href="/#/launchSystemV3/tencenTasset/profiles" target="_blank">
+                    前往管理
+                </Link> */}
             </div>
         </>}
         value={value}

+ 3 - 1
src/pages/launchSystemV3/tencentAdPutIn/const.ts

@@ -80,7 +80,9 @@ export const defaultSiteSet = ['SITE_SET_MOMENTS', 'SITE_SET_WECHAT', 'SITE_SET_
 export enum MARKETING_TARGET_TYPE_ENUM {
 	MARKETING_TARGET_TYPE_FICTION = '小说',
 	MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT = '微信公众号',
-	MARKETING_TARGET_TYPE_SHORT_DRAMA = '短剧'
+	MARKETING_TARGET_TYPE_SHORT_DRAMA = '短剧',
+	MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT = '微信小程序',
+	MARKETING_TARGET_TYPE_WECHAT_WORK = '企业微信'
 }
 
 /** 营销载体类型 */

+ 8 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsMarketingContent.tsx

@@ -1,4 +1,4 @@
-import { Card, Form, Radio, Select, Space, Switch, Tooltip } from "antd"
+import { Card, Form, Select, Space, Switch, Tooltip } from "antd"
 import React, { useContext, useEffect, useState } from "react"
 import MarketingGoal from "./marketingGoal"
 import NewRadio from "@/pages/launchSystemV3/components/NewRadio"
@@ -9,6 +9,8 @@ import { useAjax } from "@/Hook/useAjax"
 import { getOptimizationGoalPermissionsV3Api } from "@/services/adqV3/global"
 import { adRules } from "../../rules"
 import { QuestionCircleFilled } from "@ant-design/icons"
+import { SelectMiniProgramWechat } from "@/pages/launchSystemV3/tencenTasset/miniProgramWechat"
+import { SelectCorpWechat } from "@/pages/launchSystemV3/tencenTasset/corpWechat"
 
 
 /**
@@ -178,6 +180,11 @@ const AdgroupsMarketingContent: React.FC<{ value?: any }> = ({ value }) => {
                 setIsUpdate()
             }} />
         </Form.Item>}
+        {marketingTargetType === 'MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT' ? <Form.Item label={<strong>微信小程序</strong>} name='sysWechatAppId' rules={[{ required: true, message: '请选择微信小程序' }]}>
+            <SelectMiniProgramWechat />
+        </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_WORK' ? <Form.Item label={<strong>企业微信</strong>} name='sysCorpWechatId' rules={[{ required: true, message: '请选择微信小程序' }]}>
+            <SelectCorpWechat />
+        </Form.Item> : null}
         {marketingCarrierTypeList?.length > 0 && <Form.Item name="marketingCarrierType" label={<strong>营销载体类型</strong>} rules={[{ required: true, message: '请选择营销载体类型!' }]}>
             <New1Radio data={marketingCarrierTypeList} onChange={(e) => { setOGPparams({ ...OGPParams, marketingCarrierType: e }); setIsUpdate() }} />
         </Form.Item>}

+ 3 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/Ad/index.tsx

@@ -8,6 +8,7 @@ import { AD_STATUS_ENUM, BID_MODE_ENUM, DEEP_CONVERSION_ENUM, GOAL_ROAS_ENUM, MA
 import TimeSeriesLook from "@/pages/launchSystemNew/adq/ad/timeSeriesLook";
 import { arraysHaveSameValues } from "@/utils/utils";
 import '../../index.less'
+import { ShowMiniProgramWechatDetail } from "@/pages/launchSystemV3/tencenTasset/miniProgramWechat";
 
 /**
  * 广告信息
@@ -20,7 +21,7 @@ const Ad: React.FC = () => {
     const { adgroups } = addelivery
     const {
         marketingGoal, marketingAssetOuterSpec, marketingCarrierType, automaticSiteEnabled, siteSet, searchExpandTargetingSwitch, bidMode, smartBidType, bidAmount, optimizationGoal, isConversion, depthConversionEnabled,
-        deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName, sceneSpec, autoDerivedCreativeEnabled
+        deepConversionSpec, autoAcquisitionEnabled, autoAcquisitionBudget, dailyBudget, endDate, beginDate, timeSeries, firstDayBeginTime, configuredStatus, adgroupName, sceneSpec, autoDerivedCreativeEnabled, sysWechatAppId
     } = adgroups
     const [newVisible, setNewVisible] = useState<boolean>(false)
     /*****************************/
@@ -36,6 +37,7 @@ const Ad: React.FC = () => {
                         <p style={{ fontWeight: 'bold', color: configuredStatus === 'AD_STATUS_NORMAL' ? '#52c41a' : '#FF4D4F' }}>广告状态:{AD_STATUS_ENUM[configuredStatus as keyof typeof AD_STATUS_ENUM]}</p>
                         <p>营销目的:{MARKETING_GOAL_ENUM[marketingGoal as keyof typeof MARKETING_GOAL_ENUM]}</p>
                         <p style={{ fontWeight: 'bold', color: '#000' }}>推广产品类型:{MARKETING_TARGET_TYPE_ENUM[marketingAssetOuterSpec?.marketingTargetType as keyof typeof MARKETING_TARGET_TYPE_ENUM]}</p>
+                        {marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT' && <ShowMiniProgramWechatDetail id={sysWechatAppId} />}
                         <p>营销载体类型:{MARKETING_CARRIER_TYPE_ENUM[marketingCarrierType as keyof typeof MARKETING_CARRIER_TYPE_ENUM]}</p>
                         <p>版位选择:{automaticSiteEnabled ? '自动版位' : '选择特定版位'}</p>
                         {!automaticSiteEnabled && <Typography.Paragraph className={style.tpP} style={{ marginBottom: 0 }} ellipsis={{ tooltip: true, rows: 2 }}>广告版位:{siteSet.map((item: string | number) => SITE_SET_ENUM[item as keyof typeof SITE_SET_ENUM]).toString()}</Typography.Paragraph>}

+ 18 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeConversionAssistant.tsx

@@ -348,7 +348,24 @@ const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean }>
                         <div className={style.newSpace_top}>
                             <div className={style.newSpace_title}>浮层卡片</div>
                             <Form.Item name={['floatingZone', 'value', 'floatingZoneSwitch']} rules={[{ required, message: '请选择浮层卡片!' }]} valuePropName="checked" style={{ marginBottom: 0 }}>
-                                <Switch disabled={required} />
+                                <Switch 
+                                    disabled={required}
+                                    onChange={(e) => {
+                                        if (e) {
+                                            let floatingZoneData: any = {
+                                                value: {
+                                                    floatingZoneSwitch: true,
+                                                    floatingZoneType: 'FLOATING_ZONE_TYPE_IMAGE_TEXT',
+                                                    floatingZoneButtonText: '了解更多'
+                                                }
+                                            }
+                                            if (floating_zone_image_id && floating_zone_button_text) {
+                                                floatingZoneData.value.floatingZoneButtonText = floatingZone.children.floating_zone_button_text?.enumProperty?.enumeration[0]?.value
+                                            }
+                                            form.setFieldsValue({ floatingZone: floatingZoneData })
+                                        }
+                                    }}
+                                />
                             </Form.Item>
                         </div>
                         {floatingZoneSwitch && <Form.Item name={['floatingZone', 'value', 'floatingZoneType']} rules={[{ required: floating_zone_type?.required, message: `请选择` + floating_zone_type?.description }]} style={{ marginTop: 10, marginBottom: 0 }}>

+ 2 - 2
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/creativeTemplateContent.tsx

@@ -39,7 +39,7 @@ const CreativeTemplateContent: React.FC<{ automaticSiteEnabled: boolean }> = ({
                     let jump_info = creativeComponents[key]
                     jumpInfoNumber = jump_info?.arrayProperty?.maxNumber || 1
                     let jumpInfoPageType = jump_info.children.page_type
-                    pageTypeList = (jumpInfoPageType.enumProperty.enumeration as { value: string, description: string }[]).filter(item => !["PAGE_TYPE_WECHAT_CANVAS_MINI_PROGRAM", "PAGE_TYPE_H5", "PAGE_TYPE_WECHAT_SIMPLE_CANVAS", "PAGE_TYPE_APP_DEEP_LINK"].includes(item.value)).map(item => ({ label: item.description, value: item.value, disabled: !["PAGE_TYPE_WECHAT_CANVAS", "PAGE_TYPE_OFFICIAL"].includes(item.value) }))
+                    pageTypeList = (jumpInfoPageType.enumProperty.enumeration as { value: string, description: string }[]).filter(item => !["PAGE_TYPE_WECHAT_CANVAS_MINI_PROGRAM", "PAGE_TYPE_H5", "PAGE_TYPE_WECHAT_SIMPLE_CANVAS", "PAGE_TYPE_APP_DEEP_LINK"].includes(item.value)).map(item => ({ label: item.description, value: item.value, disabled: !["PAGE_TYPE_WECHAT_CANVAS", "PAGE_TYPE_OFFICIAL", "PAGE_TYPE_WECHAT_MINI_PROGRAM"].includes(item.value) }))
                     break
             }
         })
@@ -47,7 +47,7 @@ const CreativeTemplateContent: React.FC<{ automaticSiteEnabled: boolean }> = ({
         return <>
             {brand && <Form.Item style={{ marginBottom: 0 }}>
                 <Form.Item name={['jumpInfo', 'pageType']} label={<strong>品牌形象跳转</strong>} rules={[{ required: true, message: '请选择品牌形象跳转!' }]} tooltip="品牌形象将在各流量版位下广告外层创意展示。此外,朋友圈广告在此基础上支持跳转,点击品牌形象可跳转到品牌简介页或搜一搜品牌主页">
-                    <New1Radio data={enumeration} />
+                    <New1Radio data={enumeration} onChange={() => form.setFieldsValue({ brand: undefined })} />
                 </Form.Item>
                 {['PAGE_TYPE_H5_PROFILE'].includes(pageType) ? <div style={{ margin: '0 0 10px', backgroundColor: '#fafafb', padding: '8px 16px 8px', borderRadius: 8 }}>
                     <Form.Item name='brand' style={{ marginBottom: 0 }} rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>

+ 35 - 18
src/pages/launchSystemV3/tencentAdPutIn/create/Dynamic/newDynamic.tsx

@@ -255,20 +255,20 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
                         break
                     case 'floating_zone':
                         let floatingZone = result[key]
-                        let floatingZoneData: any = {
-                            value: {
-                                floatingZoneSwitch: false,
-                                floatingZoneType: 'FLOATING_ZONE_TYPE_IMAGE_TEXT',
-                                floatingZoneButtonText: '了解更多'
-                            }
-                        }
-                        if (floatingZone?.children?.floating_zone_image_id && floatingZone?.children?.floating_zone_button_text) {
-                            floatingZoneData.value.floatingZoneButtonText = floatingZone.children.floating_zone_button_text?.enumProperty?.enumeration[0]?.value
-                        }
                         if (floatingZone?.required) {
+                            let floatingZoneData: any = {
+                                value: {
+                                    floatingZoneSwitch: false,
+                                    floatingZoneType: 'FLOATING_ZONE_TYPE_IMAGE_TEXT',
+                                    floatingZoneButtonText: '了解更多'
+                                }
+                            }
+                            if (floatingZone?.children?.floating_zone_image_id && floatingZone?.children?.floating_zone_button_text) {
+                                floatingZoneData.value.floatingZoneButtonText = floatingZone.children.floating_zone_button_text?.enumProperty?.enumeration[0]?.value
+                            }
                             floatingZoneData.value.floatingZoneSwitch = true
+                            values.floatingZone = floatingZoneData
                         }
-                        values.floatingZone = floatingZoneData
                         break
                 }
             })
@@ -311,15 +311,32 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
             let { pageType } = pageSpec?.[0]
             actionButtonJumpInfo.pageType = pageType
             creativeComponents.mainJumpInfo = pageSpec.map((item: { pageType: string, overrideCanvasHeadOption: string, }) => {
+                let pageSpec: { [x: string]: any } = {}
+                if (item.pageType === "PAGE_TYPE_WECHAT_CANVAS") { // 原生推广页
+                    pageSpec = {
+                        [pageSpecFieldConVert[item.pageType as keyof typeof pageSpecFieldConVert]]: {
+                            pageId: null,
+                            overrideCanvasHeadOption: item.overrideCanvasHeadOption
+                        }
+                    }
+                } else if (item.pageType === "PAGE_TYPE_OFFICIAL") {  // 官方落地页
+                    pageSpec = {
+                        [pageSpecFieldConVert[item.pageType as keyof typeof pageSpecFieldConVert]]: {
+                            pageId: null
+                        }
+                    }
+                } else if (item.pageType === "PAGE_TYPE_WECHAT_MINI_PROGRAM") { // 微信小程序
+                    pageSpec = {
+                        [pageSpecFieldConVert[item.pageType as keyof typeof pageSpecFieldConVert]]: {
+                            miniProgramId: null,
+                            miniProgramPath: null
+                        }
+                    }
+                }
                 return {
                     value: {
                         pageType: item.pageType,
-                        pageSpec: {
-                            [pageSpecFieldConVert[item.pageType as keyof typeof pageSpecFieldConVert]]: {
-                                pageId: null,
-                                overrideCanvasHeadOption: item.overrideCanvasHeadOption
-                            }
-                        }
+                        pageSpec
                     }
                 }
             })
@@ -389,7 +406,7 @@ const NewDynamic: React.FC<Props> = ({ value: newValue, visible, onClose, onChan
         if (showDataShow) {
             creativeComponents.showData = [showData]
         }
-        if (floatingZone?.value) {
+        if (floatingZone?.value?.floatingZoneSwitch) {
             let value = floatingZone.value
             let { image, ...val } = value
             if (val.floatingZoneType === "FLOATING_ZONE_TYPE_SINGLE_IMAGE") {

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

@@ -174,7 +174,7 @@ const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, delive
             <strong style={{ fontSize: 20 }}>创意素材</strong>
             {deliveryMode === 'DELIVERY_MODE_CUSTOMIZE' ? <>
                 {videoUploads && Object.keys(videoUploads)?.length > 0 && <Button type="link" onClick={() => {
-                    init({ mediaType: 'VIDEO', num: 100, cloudSize: creativeTemplateId === 1708 ? [[{ relation: '=', width: 1280, height: 720 }]] : [[{ relation: '=', width: videoUploads.restriction.videoRestriction.minWidth, height: videoUploads.restriction.videoRestriction.minHeight }]], maxSize: videoUploads.restriction.videoRestriction.fileSize * 1024 })
+                    init({ mediaType: 'VIDEO', num: 100, cloudSize: creativeTemplateId === 1708 ? [[{ relation: '>=', width: 1280, height: 720 }]] : [[{ relation: '>=', width: videoUploads.restriction.videoRestriction.minWidth, height: videoUploads.restriction.videoRestriction.minHeight }]], maxSize: videoUploads.restriction.videoRestriction.fileSize * 1024 })
                     setMaterialConfig({
                         ...materialConfig,
                         type: videoUploads.name,
@@ -226,8 +226,8 @@ const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, delive
                 <Button type="link" onClick={() => {
                     init({
                         mediaType: 'VIDEO', num: 100, cloudSize: [[
-                            { relation: '=', width: 1280, height: 720 },
-                            { relation: '=', width: 720, height: 1280 }
+                            { relation: '>=', width: 1280, height: 720 },
+                            { relation: '>=', width: 720, height: 1280 }
                         ]], maxSize: 512000 * 1024
                     })
                     setMaterialConfig({
@@ -303,8 +303,8 @@ const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, delive
                                             <Menu.Item onClick={() => {
                                                 init({
                                                     mediaType: 'VIDEO', num: 15, cloudSize: [[
-                                                        { relation: '=', width: 1280, height: 720 },
-                                                        { relation: '=', width: 720, height: 1280 }
+                                                        { relation: '>=', width: 1280, height: 720 },
+                                                        { relation: '>=', width: 720, height: 1280 }
                                                     ]], maxSize: 512000 * 1024
                                                 })
                                                 setMaterialConfig({
@@ -381,7 +381,7 @@ const AddMaterial: React.FC<Props> = ({ creativeTemplateId, materialData, delive
                                                 key={k}
                                             >
                                                 <div className={`${styles.box} ${styles.video}`} style={{ width: 300, height: 160 }} onClick={() => {
-                                                    init({ mediaType: 'VIDEO', cloudSize: creativeTemplateId === 1708 ? [[{ relation: '=', width: 1280, height: 720 }]] : [[{ relation: '=', width: item.restriction.videoRestriction.minWidth, height: item.restriction.videoRestriction.minHeight }]], maxSize: item.restriction.videoRestriction.fileSize * 1024 })
+                                                    init({ mediaType: 'VIDEO', cloudSize: creativeTemplateId === 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,

+ 71 - 70
src/pages/launchSystemV3/tencentAdPutIn/create/PageList/index.tsx

@@ -6,6 +6,7 @@ import { PlusCircleOutlined } from '@ant-design/icons';
 import PageModal from '@/pages/launchSystemV3/components/PageModal';
 import PageOfficialModal from '@/pages/launchSystemV3/components/PageModal/indexOfficial';
 import { PAGE_TYPE_ENUM } from '../../const';
+import MiniProgramPage from '@/pages/launchSystemV3/components/PageModal/MiniProgramPage';
 const { Title, Text } = Typography;
 
 /**
@@ -20,6 +21,7 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
 
     const [addVisible, setAddVisible] = useState<boolean>(false)
     const [addOfficialVisible, setAddOfficialVisible] = useState<boolean>(false)
+    const [addMiniProgramVisible, setAddMiniProgramVisible] = useState<boolean>(false)
     const [pageType, setPageType] = useState<keyof typeof PAGE_TYPE_ENUM>()
     /********************************/
 
@@ -30,9 +32,56 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
         }
     }, [dynamic])
 
+    const setPageData = ({ data, landingPageType }: { data: PULLIN.AccountCreateLogsProps[]; landingPageType: 0 | 1; }, type: 1 | 2 | 3) => {
+        if (landingPageType === 1) {
+            if (adDataGroup && Object.keys(adDataGroup).length) {
+                if (data.some(item => {
+                    let total = adDataGroup[item.accountId].length
+                    let pageLength = item.pageList.length
+                    if (total > pageLength) {
+                        message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
+                        return true
+                    }
+                    return false
+                })) {
+                    return
+                }
+            } else {
+                let targetingLength = targeting.length
+                if (data.some(item => {
+                    let productListLength = item?.productList?.length || 1
+                    let total = targetingLength * productListLength
+                    let pageLength = item.pageList.length
+                    if (total > pageLength) {
+                        message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
+                        return true
+                    }
+                    return false
+                })) {
+                    return
+                }
+            }
+        }
+        setAccountCreateLogs(data);
+        setAddelivery({ ...addelivery, dynamic: { ...dynamic, landingPageType } })
+        switch(type) {
+            case 1:
+                setAddVisible(false);
+                break
+            case 2:
+                setAddOfficialVisible(false);
+                break
+            case 3:
+                setAddMiniProgramVisible(false);
+                break
+        }
+        
+        clearData()
+    }
+
     return <div className={`${style.settingsBody_content_row} ${style.row6}`}>
         <div className={style.title}>
-            <span>{pageType === 'PAGE_TYPE_OFFICIAL' ? '灵鹊落地页(官方落地页)' : '原生推广页'}</span>
+            <span>{pageType === 'PAGE_TYPE_OFFICIAL' ? '灵鹊落地页(官方落地页)' : pageType === "PAGE_TYPE_WECHAT_MINI_PROGRAM" ? '微信小程序' : '原生推广页'}</span>
         </div>
         <div className={style.detail}>
             <div className={style.detail_body}>
@@ -55,6 +104,8 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
                     onClick={() => {
                         if (pageType === 'PAGE_TYPE_OFFICIAL') {
                             setAddOfficialVisible(true)
+                        } else if (pageType === "PAGE_TYPE_WECHAT_MINI_PROGRAM") {
+                            setAddMiniProgramVisible(true)
                         } else {
                             setAddVisible(true)
                         }
@@ -64,7 +115,7 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
                 </Button>
             </div>
         </div>
-        
+
         {/* 原生推广页 */}
         {addVisible && <PageModal
             data={accountCreateLogs}
@@ -74,40 +125,8 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
             onClose={() => {
                 setAddVisible(false)
             }}
-            onChange={({ data, landingPageType }) => {
-                if (landingPageType === 1) {
-                    if (adDataGroup && Object.keys(adDataGroup).length) {
-                        if (data.some(item => {
-                            let total = adDataGroup[item.accountId].length
-                            let pageLength = item.pageList.length
-                            if (total > pageLength) {
-                                message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
-                                return true
-                            }
-                            return false
-                        })) {
-                            return
-                        }
-                    } else {
-                        let targetingLength = targeting.length
-                        if (data.some(item => {
-                            let productListLength = item?.productList?.length || 1
-                            let total = targetingLength * productListLength
-                            let pageLength = item.pageList.length
-                            if (total > pageLength) {
-                                message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
-                                return true
-                            }
-                            return false
-                        })) {
-                            return
-                        }
-                    }
-                }
-                setAccountCreateLogs(data);
-                setAddelivery({ ...addelivery, dynamic: { ...dynamic, landingPageType } })
-                setAddVisible(false);
-                clearData()
+            onChange={(data) => {
+                setPageData(data, 1)
             }}
         />}
 
@@ -120,40 +139,22 @@ const PageList: React.FC<{ adDataGroup?: { [x: number]: any[] } }> = ({ adDataGr
             onClose={() => {
                 setAddOfficialVisible(false)
             }}
-            onChange={({ data, landingPageType }) => {
-                if (landingPageType === 1) {
-                    if (adDataGroup && Object.keys(adDataGroup).length) {
-                        if (data.some(item => {
-                            let total = adDataGroup[item.accountId].length
-                            let pageLength = item.pageList.length
-                            if (total > pageLength) {
-                                message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
-                                return true
-                            }
-                            return false
-                        })) {
-                            return
-                        }
-                    } else {
-                        let targetingLength = targeting.length
-                        if (data.some(item => {
-                            let productListLength = item?.productList?.length || 1
-                            let total = targetingLength * productListLength
-                            let pageLength = item.pageList.length
-                            if (total > pageLength) {
-                                message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
-                                return true
-                            }
-                            return false
-                        })) {
-                            return
-                        }
-                    }
-                }
-                setAccountCreateLogs(data);
-                setAddelivery({ ...addelivery, dynamic: { ...dynamic, landingPageType } })
-                setAddOfficialVisible(false);
-                clearData()
+            onChange={(data) => {
+                setPageData(data, 2)
+            }}
+        />}
+
+        {/* 微信小程序 */}
+        {addMiniProgramVisible && <MiniProgramPage
+            data={accountCreateLogs}
+            adgroups={adgroups}
+            dynamic={dynamic}
+            visible={addMiniProgramVisible}
+            onClose={() => {
+                setAddMiniProgramVisible(false)
+            }}
+            onChange={(data) => {
+                setPageData(data, 3)
             }}
         />}
     </div>

+ 7 - 5
src/pages/launchSystemV3/tencentAdPutIn/create/index.tsx

@@ -229,7 +229,7 @@ const Create: React.FC = () => {
 
                     let isConversion = false
                     setAccountCreateLogs(Object.keys(accountIdParamVOMap || {}).map(accountId => {
-                        const { productDTOS, wechatOfficialAccountsVO, pageList, landingPageVOS, userActionSetsList, conversionInfo, wechatChannelVO } = accountIdParamVOMap[accountId]
+                        const { productDTOS, wechatOfficialAccountsVO, pageList, landingPageVOS, userActionSetsList, conversionInfo, wechatChannelVO, wechatAppletList } = accountIdParamVOMap[accountId]
                         let data: PULLIN.AccountCreateLogsProps = {
                             accountId: Number(accountId),
                             productList: productDTOS
@@ -237,8 +237,8 @@ const Create: React.FC = () => {
                         if (wechatOfficialAccountsVO) {
                             data.wechatChannelList = [wechatOfficialAccountsVO]
                         }
-                        if (pageList || landingPageVOS) {
-                            data.pageList = pageList || landingPageVOS
+                        if (pageList || landingPageVOS || wechatAppletList) {
+                            data.pageList = pageList || landingPageVOS || wechatAppletList?.map((item: { appletName: any; id: any }) => ({ ...item, pageName: item.appletName, pageId: item.id }))
                         }
                         if (userActionSetsList) {
                             data.userActionSetsList = userActionSetsList
@@ -368,10 +368,12 @@ const Create: React.FC = () => {
             let adLength = 0
             accountCreateLogs.forEach(item => {
                 let productList: any[] = []
-                if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说
+                if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说 短剧
                     productList = item?.productList || []
                 } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
                     productList = item?.wechatChannelList || []
+                } else {
+                    productList = [{}]
                 }
                 adLength += productList.length * targeting.length
             })
@@ -424,7 +426,7 @@ const Create: React.FC = () => {
         }
         let accountIndex = 0, accountIndex1 = 0
         accountCreateLogs.forEach(item => {
-            let productList: any[] = []
+            let productList: any[] = [{}]
             if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说
                 productList = item?.productList || []
             } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号

+ 20 - 2
src/pages/launchSystemV3/tencentAdPutIn/create/tableConfig.tsx

@@ -46,6 +46,16 @@ const columns = (): TableProps<any>['columns'] => {
                                 <Text style={{ fontSize: 12 }}>应用:{b?.productDto?.wechatOfficialAccountName}({b?.productDto?.wechatOfficialAccountId})</Text>
                                 <Text style={{ fontSize: 12 }}>转化归因:{b?.userActionSetsList ? b?.userActionSetsList.map((item: { name: any; }) => item.name).toString() : b?.conversionList ? b?.conversionList.map((item: { conversionName: any; conversionId: any; }) => `${item?.conversionName}(${item.conversionId})`).toString() : '暂未配置'}</Text>
                             </Space>
+                        } else if (['MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT'].includes(b.adgroupsDto?.marketingAssetOuterSpec?.marketingTargetType)) {
+                            return <Space size={0} direction="vertical">
+                                <Text style={{ fontSize: 12 }}>推广产品:微信小程序(产品本地ID:{b?.adgroupsDto?.sysWechatAppId})</Text>
+                                <Text style={{ fontSize: 12 }}>转化归因:{b?.userActionSetsList ? b?.userActionSetsList.map((item: { name: any; }) => item.name).toString() : b?.conversionList ? b?.conversionList.map((item: { conversionName: any; conversionId: any; }) => `${item?.conversionName}(${item.conversionId})`).toString() : '暂未配置'}</Text>
+                            </Space>
+                        } else if (['MARKETING_TARGET_TYPE_WECHAT_WORK'].includes(b.adgroupsDto?.marketingAssetOuterSpec?.marketingTargetType)) {
+                            return <Space size={0} direction="vertical">
+                                <Text style={{ fontSize: 12 }}>推广产品:企业微信(产品本地ID:{b?.adgroupsDto?.sysCorpWechatId})</Text>
+                                <Text style={{ fontSize: 12 }}>转化归因:{b?.userActionSetsList ? b?.userActionSetsList.map((item: { name: any; }) => item.name).toString() : b?.conversionList ? b?.conversionList.map((item: { conversionName: any; conversionId: any; }) => `${item?.conversionName}(${item.conversionId})`).toString() : '暂未配置'}</Text>
+                            </Space>
                         }
                         return 'ERROR,请联系管理员'
                     },
@@ -212,7 +222,11 @@ const columns = (): TableProps<any>['columns'] => {
                     width: 200,
                     render: (_, b) => {
                         let pageListDto = b?.pageListDto
-                        return <Text style={{ fontSize: 12, wordBreak: 'break-all' }}>原生推广页:{pageListDto?.map((item: { pageName: any; }) => item?.pageName)?.join(',')}</Text>
+                        let pageType = b?.dynamicDto?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType
+                        return <Text style={{ fontSize: 12, wordBreak: 'break-all' }}>
+                            {pageType === 'PAGE_TYPE_OFFICIAL' ? '灵鹊落地页' : pageType === 'PAGE_TYPE_WECHAT_MINI_PROGRAM' ? '微信小程序' : '原生推广页'}:
+                            {pageListDto?.map((item: { pageName: any; }) => item?.pageName)?.join(',')}
+                        </Text>
                     }
                 }
             ]
@@ -374,7 +388,11 @@ export const columnsAddDynamic = (): TableProps<any>['columns'] => {
                     width: 200,
                     render: (_, b) => {
                         let pageListDto = b?.pageListDto
-                        return <Text style={{ fontSize: 12, wordBreak: 'break-all' }}>原生推广页:{pageListDto?.map((item: { pageName: any; }) => item?.pageName)?.join(',')}</Text>
+                        let pageType = b?.dynamicDto?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType
+                        return <Text style={{ fontSize: 12, wordBreak: 'break-all' }}>
+                            {pageType === 'PAGE_TYPE_OFFICIAL' ? '灵鹊落地页' : pageType === 'PAGE_TYPE_WECHAT_MINI_PROGRAM' ? '微信小程序' : '原生推广页'}:
+                            {pageListDto?.map((item: { pageName: any; }) => item?.pageName)?.join(',')}
+                        </Text>
                     }
                 }
             ]

+ 138 - 1
src/services/adqV3/global.ts

@@ -130,7 +130,7 @@ export async function getVideoChannelInfoBatchApi(data: { accountIdList?: number
  * @param data 
  * @returns 
  */
-export async function getConversionInfoApi(data: { 
+export async function getConversionInfoApi(data: {
     accountId: number,
     pageNum: number,
     pageSize: number,
@@ -439,4 +439,141 @@ export async function getVideosInfoApi(data: { adAccountId: number, videoIds: nu
         method: 'POST',
         data
     })
+}
+
+
+
+/**
+ * 新增微信小程序
+ * @param data 
+ * @returns 
+ */
+export async function addWechatAppletApi(data: { appletId: string, appletName: string, appletLink: string, description: string }) {
+    return request(api + `/adq/v3/wechatApplet/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 查询本地微信小程序列表
+ * @param data 
+ * @returns 
+ */
+export async function getWechatAppletApi(data: { pageNum: number, pageSize: number, appletName?: string, appletIdList?: string[] }) {
+    return request(api + `/adq/v3/wechatApplet/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 删除微信小程序
+ * @param data 
+ * @returns 
+ */
+export async function delWechatAppletApi(id: number) {
+    return request(api + `/adq/v3/wechatApplet/delById/${id}`, {
+        method: 'DELETE'
+    })
+}
+
+/**
+ * 修改微信小程序
+ * @param data 
+ * @returns 
+ */
+export async function updateWechatAppletApi(data: { appletId: string, appletName: string, appletLink: string, description: string, id: number }) {
+    return request(api + `/adq/v3/wechatApplet/update`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+/**
+ * 获取所有微信小程序列表
+ * @param data 
+ * @returns 
+ */
+export async function getWechatAppletAllApi(data: { appletName?: string, appletIdList?: string[] }) {
+    return request(api + `/adq/v3/wechatApplet/listAll`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 获取详情
+ * @param id 
+ * @returns 
+ */
+export async function getWechatAppletDetailApi(id: number) {
+    return request(api + `/adq/v3/wechatApplet/getById/${id}`, {
+        method: 'GET'
+    })
+}
+
+
+
+
+/**
+ * 新增本地企微微信信息
+ * @param data 
+ * @returns 
+ */
+export async function addCorpWechatApi(data: { wechatId: string, wechatName: string }) {
+    return request(api + `/adq/v3/corpWechat/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 查询本地企业微信列表
+ * @param data 
+ * @returns 
+ */
+export async function getCorpWechatApi(data: { pageNum: number, pageSize: number, wechatName?: string, wechatIdList?: string[] }) {
+    return request(api + `/adq/v3/corpWechat/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+
+/**
+ * 删除本地企业微信信息
+ * @param data 
+ * @returns 
+ */
+export async function delCorpWechatApi(id: number) {
+    return request(api + `/adq/v3/corpWechat/delById/${id}`, {
+        method: 'DELETE'
+    })
+}
+
+
+/**
+ * 获取所有微信小程序列表
+ * @param data 
+ * @returns 
+ */
+export async function getCorpWechatAllApi(data: { wechatName?: string, wechatIdList?: string[] }) {
+    return request(api + `/adq/v3/corpWechat/listAll`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 获取详情
+ * @param id 
+ * @returns 
+ */
+export async function getCorpWechatDetailApi(id: number) {
+    return request(api + `/adq/v3/corpWechat/getById/${id}`, {
+        method: 'GET'
+    })
 }