Quellcode durchsuchen

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

wjx vor 2 Jahren
Ursprung
Commit
08eccd0278

+ 1 - 0
src/assets/copywriting.svg

@@ -0,0 +1 @@
+<svg t="1679370349268" fill="currentColor" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5402" width="1em" height="1em"><path d="M984.615 827.077c-23.63 0-39.384-15.754-39.384-39.385V157.538c0-43.323-35.446-78.769-78.77-78.769H275.693c-23.63 0-39.384-15.754-39.384-39.384S252.062 0 275.692 0h590.77C953.108 0 1024 70.892 1024 157.538v630.154c0 23.631-15.754 39.385-39.385 39.385z" p-id="5403"></path><path d="M748.308 196.923H78.769C35.446 196.923 0 232.37 0 275.693V945.23C0 988.554 35.446 1024 78.77 1024h669.538c43.323 0 78.769-35.446 78.769-78.77V275.693c0-43.323-35.446-78.769-78.77-78.769zM630.154 433.231H433.23v433.23h-78.77v-433.23H157.539v-78.77h472.616v78.77z" p-id="5404"></path></svg>

+ 1 - 0
src/assets/direct.svg

@@ -0,0 +1 @@
+<svg t="1679369864194" class="icon" viewBox="0 0 1024 1024" fill="currentColor" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3289" width="1em" height="1em"><path d="M958.848 480A448.064 448.064 0 0 0 544 65.152V160a32 32 0 0 1-64 0V65.152A448.064 448.064 0 0 0 65.152 480H160a32 32 0 0 1 0 64H65.152A448.064 448.064 0 0 0 480 958.848V864a32 32 0 1 1 64 0v94.848A448.064 448.064 0 0 0 958.848 544H864a32 32 0 1 1 0-64h94.848zM512 0a512 512 0 1 1 0 1024A512 512 0 0 1 512 0z m-0.192 512a128 128 0 1 0 0-256 128 128 0 0 0 0 256z m0-64a64 64 0 1 0 0-128 64 64 0 0 0 0 128z m-256.256 320.448c0-145.344 111.36-242.688 160.128-256.448 0 0 37.12 34.048 89.984 34.048C558.72 546.048 597.76 512 597.76 512c42.496 15.552 166.528 114.688 170.752 256.256 0 0.192-72.192 0-76.48 0-137.216-0.128-436.48 0.192-436.48 0.192zM332.8 704c0-32 19.2-96 81.472-128 0 0 27.328 32 91.328 32S596.8 576 596.8 576c40.064 16.32 75.2 64 94.4 128 0.064 0.192-358.4 0-358.4 0z" p-id="3290"></path></svg>

+ 1 - 0
src/assets/landingPage.svg

@@ -0,0 +1 @@
+<svg t="1679370410714" fill="currentColor" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6485" width="1em" height="1em"><path d="M831.247059 0c96.376471 0 168.658824 78.366118 168.658823 174.983529v122.518589c0 28.973176-24.094118 52.464941-48.188235 52.464941h-192.752941v429.417412c0 121.795765-90.352941 220.581647-204.8 220.581647h-2.95153L548.141176 999.905882v0.060236H283.105882c-150.588235 0-283.105882-132.577882-283.105882-297.502118 0-29.033412 24.094118-52.525176 48.188235-52.525176h48.188236V124.988235C96.376471 55.898353 150.588235 0 216.847059 0h614.4z m-150.588235 99.990588H216.847059c-16.082824 0-22.588235 8.854588-23.853177 20.48l-0.240941 4.517647v524.950589h192.752941c27.828706 0 50.477176 19.998118 53.790118 45.959529l0.421647 6.565647v76.920471c0 66.56 48.188235 120.591059 114.447059 120.591058 57.524706 0 109.507765-49.212235 114.085647-111.616l0.361412-8.975058V174.983529c0-26.804706 6.023529-52.284235 12.047059-74.992941zM343.341176 749.989647h-240.941176c18.070588 85.714824 96.376471 149.985882 180.705882 149.985882h90.352942c-18.070588-34.695529-30.117647-76.077176-30.117648-120.591058v-29.394824zM831.247059 99.990588c-33.912471 0-67.764706 29.515294-71.860706 67.343059l-0.421647 7.649882V249.976471H903.529412V174.983529c0-41.381647-30.117647-74.992941-72.282353-74.992941z" p-id="6486"></path><path d="M283.105882 399.962353c-24.094118 0-42.164706 22.407529-42.164706 49.995294 0 27.648 18.070588 49.995294 42.164706 49.995294h289.129412c24.094118 0 48.188235-22.347294 48.188235-49.995294 0-27.587765-24.094118-49.995294-48.188235-49.995294H283.105882zM240.941176 249.976471c0-27.587765 18.070588-49.995294 42.164706-49.995295h289.129412c24.094118 0 48.188235 22.407529 48.188235 49.995295 0 27.648-24.094118 49.995294-48.188235 49.995294H283.105882c-24.094118 0-42.164706-22.347294-42.164706-49.995294z" p-id="6487"></path></svg>

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
src/assets/originality.svg


+ 1 - 0
src/assets/source.svg

@@ -0,0 +1 @@
+<svg t="1679370273170" fill="currentColor" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4311" width="1em" height="1em"><path d="M975.238095 97.52381h-48.761905V48.761905a48.761905 48.761905 0 0 0-97.523809 0v48.761905h-48.761905a48.761905 48.761905 0 0 0 0 97.523809h48.761905v48.761905a48.761905 48.761905 0 0 0 97.523809 0V195.047619h48.761905a48.761905 48.761905 0 0 0 0-97.523809z" p-id="4312"></path><path d="M316.952381 414.47619m-85.333333 0a85.333333 85.333333 0 1 0 170.666666 0 85.333333 85.333333 0 1 0-170.666666 0Z" p-id="4313"></path><path d="M877.714286 414.47619a48.761905 48.761905 0 0 0-48.761905 48.761905v292.571429l-32.182857-33.158095L666.087619 585.142857l-7.314286-6.339047a102.4 102.4 0 0 0-138.483809 19.99238L430.08 707.047619l-48.761905-38.034286a83.870476 83.870476 0 0 0-115.078095-9.752381L97.52381 807.009524V243.809524a48.761905 48.761905 0 0 1 48.761904-48.761905h426.179048a48.761905 48.761905 0 0 0 0-97.523809H146.285714a146.285714 146.285714 0 0 0-146.285714 146.285714v633.904762a146.285714 146.285714 0 0 0 146.285714 146.285714h633.904762a146.285714 146.285714 0 0 0 146.285714-146.285714V463.238095a48.761905 48.761905 0 0 0-48.761904-48.761905z" p-id="4314"></path></svg>

+ 1 - 0
src/components/FileBoxAD/index.tsx

@@ -83,6 +83,7 @@ function FlieBox(props: Props) {
                 </> : <>
                     <li onClick={() => { editFile() }}>编辑</li>
                 </>}
+                {mediaType !== 'PAGE' && !rightClickPup.folder &&  <li onClick={()=>{copy(rightClickPup?.url)}}>复制链接</li>}
                 <li onClick={() => { set({ actionItem: rightClickPup, sortVisible: true }) }}>编辑排序</li>
                 <li onClick={() => { set({ fileVisible: true }) }}>新建文件夹</li>
                 <li onClick={() => { set({ imgVisrible: true }) }}>新建素材</li>

+ 0 - 144
src/pages/launchSystemNew/components/addGroup/index.tsx

@@ -1,144 +0,0 @@
-import { PlusOutlined } from "@ant-design/icons"
-import { Button, Form, Input, message, Modal, Select, Table } from "antd"
-import React, { useEffect, useState } from "react"
-import { useModel } from "umi"
-import columnsAdd from "./tableConfigAdd"
-
-
-interface Props {
-    pitcherData: any[],
-    onChange: () => void
-}
-/**
- * 新增媒体账户组
- * @returns 
- */
-const KEY = 'ADQUSERS'
-const AddGroup: React.FC<Props> = (props) => {
-
-    /**********************/
-    const { pitcherData, onChange } = props
-    const [visible, setVisible] = useState<boolean>(false)
-    const [addVisible, setAddVisible] = useState<boolean>(false)
-    const [pitcherDB, setPitcherDB] = useState<any[]>([])
-    const [initialValues, setInitialValues] = useState<any>({})
-
-    const [form] = Form.useForm()
-    const { currentUser: { userId } }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
-    /**********************/
-    // 进入查找库
-    useEffect(() => {
-        getList()
-    }, [])
-
-    // 
-    const getList = () => {
-        let data = localStorage.getItem(KEY + userId)
-        if (data) {
-            setPitcherDB(JSON.parse(data))
-        }
-        onChange && onChange()
-    }
-
-    /** 确定新增 */
-    const handleOk = async () => {
-        form.submit()
-        let data = await form.validateFields()
-        console.log(data)
-        let length = pitcherDB.length
-        if (Object.keys(initialValues).length > 0) { // 修改
-            let newPitcherDB = pitcherDB.map((item: { id: number }) => {
-                if (initialValues.id === item.id) {
-                    return { ...data, id: initialValues.id, users: data.pitcher?.map((str: string) => str.split('_')[1]), ids: data.pitcher?.map((str: string) => str.split('_')[0]) }
-                }
-                return item
-            })
-            localStorage.setItem(KEY + userId, JSON.stringify(newPitcherDB))
-            message.success('修改成功')
-        } else { // 新增
-            data.id = length > 0 ? pitcherDB[length - 1].id + 1 : 1
-            if (pitcherDB?.some((item: { name: string }) => item.name === data?.name)) {
-                message.error('组名重复,请重新输入')
-                return
-            }
-            let newData = data
-            newData = { ...data, users: data.pitcher?.map((str: string) => str.split('_')[1]), ids: data.pitcher?.map((str: string) => str.split('_')[0]) }
-            pitcherDB.push(newData)
-            localStorage.setItem(KEY + userId, JSON.stringify(pitcherDB))
-            message.success('新增成功')
-        }
-        getList()
-        setAddVisible(false)
-        setInitialValues({})
-        form.resetFields()
-    }
-
-    /** 删除 */
-    const del = (id: number) => {
-        let newpitcherDB = pitcherDB.filter((item: { id: number }) => item.id !== id)
-        localStorage.setItem(KEY + userId, JSON.stringify(newpitcherDB?.map((item: { id: number }, index: number) => ({ ...item, id: index + 1 }))))
-        getList()
-        message.success('删除成功')
-    }
-
-    /** 修改 */
-    const edit = (value: any) => {
-        setInitialValues(value)
-        form.resetFields()
-        setAddVisible(true)
-    }
-
-    /** 清空 */
-    const clear = () => {
-        setInitialValues({})
-        form.resetFields()
-    }
-
-    return <div>
-        <Button type='primary' onClick={() => { setVisible(true) }}><PlusOutlined />新增媒体账户组</Button>
-
-        {visible && <Modal title="媒体账号组" footer={null} visible={visible} width={900} onCancel={() => { setVisible(false) }}>
-            <Button type='primary' onClick={() => { setAddVisible(true) }} style={{ marginBottom: 10 }}><PlusOutlined />新增</Button>
-            <Table columns={columnsAdd(del, edit)} dataSource={pitcherDB} size='small' bordered rowKey={(e) => { return e.id }} />
-        </Modal>}
-
-        {addVisible && <Modal title="新增媒体账号组" visible={visible} width={450} onOk={() => handleOk()} onCancel={() => { setAddVisible(false); clear() }}>
-            <Form
-                name="basic"
-                form={form}
-                labelCol={{ span: 4 }}
-                wrapperCol={{ span: 20 }}
-                autoComplete="off"
-                initialValues={initialValues}
-            >
-                <Form.Item label="组名" name="name" rules={[{ required: true, message: '请输入组名!' }]}>
-                    <Input placeholder="请输入组名" max={6} disabled={Object.keys(initialValues).length > 0} />
-                </Form.Item>
-                <Form.Item label="账号" name="pitcher" rules={[{ required: true, message: '请选择账号!' }]}>
-                    <Select
-                        placeholder='请输账号ID'
-                        showArrow
-                        showSearch
-                        allowClear
-                        maxTagCount={3}
-                        filterOption={(input: any, option: any) => {
-                            return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
-                        }}
-                        mode="multiple"
-                    >
-                        {
-                            pitcherData?.map((list: any, eq: number) => {
-                                return <Select.Option key={list?.id} value={list?.id + '_' + list.accountId}>
-                                    {list.remark ? list.accountId + '——' + list.remark : list.accountId}
-                                </Select.Option>
-                            })
-                        })
-                    </Select>
-                </Form.Item>
-            </Form>
-        </Modal>}
-    </div>
-}
-
-
-export default React.memo(AddGroup)

+ 0 - 55
src/pages/launchSystemNew/components/addGroup/tableConfigAdd.tsx

@@ -1,55 +0,0 @@
-import { Popconfirm, Space } from "antd";
-import React from "react";
-
-function columnsAdd(del: (id: number) => void, edit: (value: any) => void) {
-
-    const columns: any = [
-        {
-            title: '操作',
-            dataIndex: 'cz',
-            key: 'cz',
-            align: 'center',
-            width: 90,
-            render: (a: any[], b: any) => {
-                return <Space>
-                    <a style={{ fontSize: 12 }} onClick={() => edit(b)}>修改</a>
-                    <Popconfirm
-                        title="确定删除?"
-                        onConfirm={() => { del(b.id) }}
-                        okText="是"
-                        cancelText="否"
-                    >
-                        <a style={{ fontSize: 12, color: 'red' }}>删除</a>
-                    </Popconfirm>
-                </Space>
-            }
-        },
-        {
-            title: '序号',
-            dataIndex: 'id',
-            key: 'id',
-            align: 'center',
-            width: 50
-        },
-        {
-            title: '组名',
-            dataIndex: 'name',
-            key: 'name',
-            align: 'center',
-            width: 85,
-            ellipsis: true
-        },
-        {
-            title: '账号',
-            dataIndex: 'users',
-            key: 'users',
-            render: (a: any[]) => {
-                return <span>{a?.toString()}</span>
-            }
-        }
-    ];
-
-    return columns
-}
-
-export default columnsAdd

+ 25 - 15
src/pages/launchSystemNew/launchManage/createAd/index.tsx

@@ -7,7 +7,7 @@ import { getTagsList, get_adcreative_template } from "@/services/launchAdq/globa
 import { getSysAdgroupsInfo } from "@/services/launchAdq/localAd"
 import { getsysTargetingInfo } from "@/services/launchAdq/targeting"
 import { CheckOutlined, CloseOutlined, SearchOutlined } from "@ant-design/icons"
-import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Tabs, Popconfirm, notification, Divider, Checkbox, Modal } from "antd"
+import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Tabs, Popconfirm, notification, Divider, Checkbox, Modal, Tag } from "antd"
 import React, { useCallback, useEffect, useState } from "react"
 import { useModel } from "umi"
 import Ad from "./ad"
@@ -23,13 +23,14 @@ import SubmitModal from "./submitModal"
 import columns from "./tableConfig"
 import TargetIng from './targeting'
 import Creative from './creative'
-import AddGroup from '../../components/addGroup'
 import CustomerServiceModal from "../../components/customerServiceModal"
 import { getTaskDetailsApi } from "@/services/launchAdq/taskList"
 import CreativeCL from "./creativeCL"
 import { groupBy } from "@/utils/utils"
 import moment from "moment"
 import { getAccountListApi, getGroupListApi } from "@/services/launchAdq/subgroup"
+import TacticsS from "./tacticsS"
+import UserTactics from "./tacticsS/userTactics"
 
 const CreateAd: React.FC = () => {
 
@@ -56,7 +57,6 @@ const CreateAd: React.FC = () => {
     })
     const [launchMode, setLaunchMode] = useState<number>(Number(localStorage.getItem('LAUNCHMODE')) || 1) // 投放模式 1 现在投放模式  2 创量模式
     const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: any[], productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any, pageList?: any, coldStartAudienceList?: any[] }[]>([])  // 账户
-    const { currentUser: { userId } }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
 
     const [goodsVisible, setGoodsVisible] = useState<boolean>(false) // 选择商品弹窗控制
     const [sourceVisible, setSourceVisible] = useState<boolean>(false) // 选择数据源弹窗控制
@@ -71,7 +71,6 @@ const CreateAd: React.FC = () => {
     const [modelList, setModelList] = useState<any>({})  // 所有品牌手机
     const [targetKey, set_targetKey] = useState('0')//创意key
     const [page_checked, set_page_checked] = useState(false)//创意key
-    const [usesArr, setUsersArr] = useState<any>(localStorage.getItem('ADQUSERS' + userId) ? JSON.parse(localStorage.getItem('ADQUSERS' + userId) as any) : [])
     const { init, get } = useModel('useLaunchAdq.useBdMediaPup')
     const [cloudParams, setCloudParams] = useState<{ adcreativeTemplateId?: number }>({})
     const [accSearch, setAccSearch] = useState<string>()
@@ -87,7 +86,7 @@ const CreateAd: React.FC = () => {
     const getAdcreativeTemplate = useAjax((params) => get_adcreative_template(params))
     const getGroupList = useAjax(() => getGroupListApi())
     /*************************/
-    
+
     useEffect(() => {
         getGroupList.run()
     }, [])
@@ -843,11 +842,6 @@ const CreateAd: React.FC = () => {
             }
         }
     }, [queryForm, targetKey])
-    // 媒体组更新通知
-    const usersChange = useCallback(() => {
-        let data = JSON.parse(localStorage.getItem('ADQUSERS' + userId) as any)
-        setUsersArr(data)
-    }, [])
 
     // 切换投放模式
     const switchLaunchMode = () => {
@@ -889,7 +883,6 @@ const CreateAd: React.FC = () => {
         }
     }
 
-
     return <Space direction="vertical" style={{ width: '100%' }}>
         <Card
             title={<Space>
@@ -905,7 +898,7 @@ const CreateAd: React.FC = () => {
             </Space>}
             className={style.createAd}
             hoverable
-        // extra={<AddGroup onChange={usersChange} pitcherData={getAdAccount?.data?.data} />}
+            extra={<UserTactics setQueryForm={setQueryForm} setAccountCreateLogs={setAccountCreateLogs} setLaunchMode={setLaunchMode} />}
         >
             <Space wrap>
                 <Selector label="媒体账户组">
@@ -932,9 +925,25 @@ const CreateAd: React.FC = () => {
                         maxTagCount={1}
                         allowClear
                         bordered={false}
-                        maxTagPlaceholder={<Tooltip color="#FFF" title={<span style={{ color: '#000' }}>{accountCreateLogs?.filter((item, index) => index !== 0)?.map(item => item.adAccountId).toString()}</span>}>
-                            <span>+{accountCreateLogs?.length > 1 ? accountCreateLogs.length - 1 : 0}</span>
-                        </Tooltip>}
+                        maxTagPlaceholder={
+                            <Tooltip
+                                color="#FFF"
+                                title={<span style={{ color: '#000' }}>
+                                    {accountCreateLogs?.filter((item, index) => index !== 0)
+                                        ?.map((item, index) => <Tag
+                                            key={index}
+                                            closable
+                                            onClose={() => {
+                                                setAccountCreateLogs(accountCreateLogs?.filter(item1 => item1.adAccountId !== item.adAccountId))
+                                                setQueryForm({ ...queryForm, adqPageList: [], pageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '', accountPageIdMap: {}, cropUserGroupMap: [] })) })
+                                                clearData()
+                                            }}
+                                        >{item.adAccountId}</Tag>)}</span>
+                                }
+                            >
+                                <span>+{accountCreateLogs?.length > 1 ? accountCreateLogs.length - 1 : 0}</span>
+                            </Tooltip>
+                        }
                         dropdownMatchSelectWidth={false}
                         autoClearSearchValue={false}
                         filterOption={(input: any, option: any) => {
@@ -1258,6 +1267,7 @@ const CreateAd: React.FC = () => {
                 </Row>
                 {/* =============广告底部按钮=========== */}
                 <Space className={style.bts}>
+                    <TacticsS queryForm={queryForm} accountCreateLogs={accountCreateLogs} launchMode={launchMode} />
                     <Button type='primary' onClick={severBd}>存为预设</Button>
                     <Button type='primary' onClick={preview}><SearchOutlined /> 批量预览广告</Button>
                     <Button onClick={delBdPlan}>清空配置/预设</Button>

+ 21 - 0
src/pages/launchSystemNew/launchManage/createAd/tacticsS/config.tsx

@@ -0,0 +1,21 @@
+import { ReactComponent as LaunchSvg } from '@/assets/launch.svg'
+import { ReactComponent as DirectSvg } from '@/assets/direct.svg'
+import { ReactComponent as OriginalitySvg } from '@/assets/originality.svg'
+import { ReactComponent as SourceSvg } from '@/assets/source.svg'
+import { ReactComponent as CopywritingSvg } from '@/assets/copywriting.svg'
+import { ReactComponent as LandingPageSvg } from '@/assets/landingPage.svg'
+import React from 'react'
+
+export interface CYProps {
+    label: string,
+    value: string,
+    icon: JSX.Element
+}
+export const SingleCYData: CYProps[] = [
+    { label: '广告基本信息', value: 'ad', icon: <LaunchSvg /> },
+    { label: '定向', value: 'direct', icon: <DirectSvg /> },
+    { label: '创意基本信息', value: 'originality', icon: <OriginalitySvg /> },
+    { label: '创意素材', value: 'source', icon: <SourceSvg /> },
+    { label: '文案', value: 'copywriting', icon: <CopywritingSvg /> },
+    { label: '落地页', value: 'landingPage', icon: <LandingPageSvg /> },
+]

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

@@ -0,0 +1,33 @@
+.selectRow {
+
+    .selectCol {
+        height: 80px;
+        box-shadow: 0 0 0 1px #d2cfcf;
+        border-radius: 4px;
+        box-sizing: border-box;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        gap: 6px;
+        color: #000;
+        font-size: 20px;
+        cursor: pointer;
+        transition: all 0.2;
+        user-select: none;
+
+        &>span {
+            font-size: 14px;
+            color: #363636;
+        }
+    }
+
+    .selected {
+        color: #1890ff;
+        box-shadow: 0 0 0 1px #1890ff;
+
+        &>span {
+            color: #1890ff;
+        }
+    }
+}

+ 69 - 0
src/pages/launchSystemNew/launchManage/createAd/tacticsS/index.tsx

@@ -0,0 +1,69 @@
+import { useAjax } from "@/Hook/useAjax";
+import { CreateAdProps } from "@/services/launchAdq/createAd";
+import { addStrategyApi } from "@/services/launchAdq/localAd";
+import { Button, Form, Input, message, Modal } from "antd"
+import React, { useState } from "react"
+
+
+interface Props {
+    queryForm: Partial<CreateAdProps>
+    accountCreateLogs: { adAccountId: number, id: number, userActionSetsList?: any[], productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any, pageList?: any, coldStartAudienceList?: any[] }[]
+    launchMode: number
+}
+/**
+ * 存为策略组
+ * @returns 
+ */
+const TacticsS: React.FC<Props> = ({ queryForm, accountCreateLogs, launchMode }) => {
+
+    /*********************************/
+    const [form] = Form.useForm();
+    const [visible, setVisible] = useState<boolean>(false)
+
+    const addStrategy = useAjax((params) => addStrategyApi(params))
+    /*********************************/
+
+    const seve = () => {
+        setVisible(true)
+    }
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            let strategyValue = JSON.stringify({
+                queryForm,
+                accountCreateLogs,
+                launchMode
+            })
+            addStrategy.run({ ...values, strategyValue, promotedObjectType: queryForm.promotedObjectType }).then(res => {
+                message.success('保存成功')
+                form.resetFields()
+                setVisible(false)
+            })
+        })
+    }
+
+    return <>
+        <Button type='primary' onClick={seve}>存为策略组</Button>
+        {visible && <Modal
+            title="存为策略组"
+            visible={visible}
+            onCancel={() => { form.resetFields(); setVisible(false) }}
+            onOk={handleOk}
+            confirmLoading={addStrategy.loading}
+        >
+            <Form
+                form={form}
+                layout='vertical'
+                className='ad_form_style'
+                colon={false}
+                initialValues={{}}
+            >
+                <Form.Item label={<strong>策略组名称</strong>} name='strategyKey' rules={[{ required: true, message: '请输入策略组名称' }]}>
+                    <Input placeholder="请输入策略组名称" showCount maxLength={30} />
+                </Form.Item>
+            </Form>
+        </Modal>}
+    </>
+}
+
+export default React.memo(TacticsS)

+ 43 - 0
src/pages/launchSystemNew/launchManage/createAd/tacticsS/selectCol.tsx

@@ -0,0 +1,43 @@
+import { Col, Row } from "antd"
+import React, { useEffect, useState } from "react"
+import './index.less'
+import { CYProps } from "./config"
+
+interface Props {
+    data: CYProps[],
+    value?: string[]
+    onChange?: (value: any) => void
+}
+
+const SelectCol: React.FC<Props> = ({ value = [], onChange, data = [] }) => {
+
+    /**********************/
+    const [key, setKey] = useState<string[]>([])
+    /**********************/
+
+    useEffect(() => {
+        setKey(value)
+    }, [value])
+
+    const selectHandle = (value: string) => {
+        let newKey: string[] = JSON.parse(JSON.stringify(key))
+        if (newKey?.includes(value)) { // 去除
+            newKey = newKey.filter(item => item !== value)
+        } else { // 增加
+            newKey = [...newKey, value]
+        }
+        setKey(newKey)
+        onChange?.(newKey)
+    }
+
+    return <Row gutter={10} className="selectRow">
+        {data.map((item, index) => <Col span={Math.floor(24 / data.length)} key={item.value + '_' + index}>
+            <div className={`selectCol ${key.includes(item.value) ? 'selected' : ''}`} onClick={() => selectHandle(item.value)}>
+                {item.icon}
+                <span>{item.label}</span>
+            </div>
+        </Col>)}
+    </Row>
+}
+
+export default React.memo(SelectCol)

+ 47 - 0
src/pages/launchSystemNew/launchManage/createAd/tacticsS/tableConfig.tsx

@@ -0,0 +1,47 @@
+import { PromotedObjectType } from "@/services/launchAdq/enum";
+import { Popconfirm } from "antd";
+import React from "react";
+
+export function Columns(del: (id: number) => void) {
+
+    const columns: any = [
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            width: 60,
+            align: 'center'
+        },
+        {
+            title: '策略组名称',
+            dataIndex: 'strategyKey',
+            key: 'strategyKey',
+            width: 300
+        },
+        {
+            title: '推广目标',
+            dataIndex: 'promotedObjectType',
+            key: 'promotedObjectType',
+            width: 90,
+            align: 'center',
+            render: (a: string, b: any) => {
+                return PromotedObjectType[a]
+            }
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            render: (a: any, b: any) => {
+                return <Popconfirm
+                    title="是否删除?"
+                    onConfirm={() => del(b?.id)}
+                >
+                    <a style={{ color: 'red' }}>删除</a>
+                </Popconfirm>
+            }
+        }
+    ];
+
+    return columns
+}

+ 131 - 0
src/pages/launchSystemNew/launchManage/createAd/tacticsS/userTactics.tsx

@@ -0,0 +1,131 @@
+import { useAjax } from "@/Hook/useAjax"
+import { PromotedObjectType } from "@/services/launchAdq/enum"
+import { delStrategyApi, getStrategyApi } from "@/services/launchAdq/localAd"
+import { Button, Input, message, Modal, Select, Space, Table } from "antd"
+import React, { useEffect, useState } from "react"
+import { Columns } from "./tableConfig"
+import moment from "moment"
+
+
+interface Props {
+    setQueryForm: any
+    setAccountCreateLogs: any,
+    setLaunchMode: any
+}
+/**
+ * 使用策略组
+ * @returns 
+ */
+const UserTactics: React.FC<Props> = ({ setQueryForm: setQuery, setAccountCreateLogs, setLaunchMode }) => {
+
+    /***********************/
+    const [visible, setVisible] = useState<boolean>(false)
+    const [queryForm, setQueryForm] = useState<{ promotedObjectType?: string, strategyKey?: string }>({})
+    const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>([])
+    const [loading, setLoading] = useState<boolean>(false)
+
+    const getStrategy = useAjax((params) => getStrategyApi(params))
+    const delStrategy = useAjax((params) => delStrategyApi(params))
+    /***********************/
+
+    useEffect(() => {
+        if (visible) {
+            getList()
+        }
+    }, [visible, queryForm])
+
+    const getList = () => {
+        getStrategy.run(queryForm)
+    }
+
+    const use = () => {
+        setVisible(true)
+    }
+
+    const handleOk = () => {
+        if (selectedRowKeys.length > 0) {
+            const hide = message.loading('设置中。。。', 0)
+            setLoading(true)
+            setTimeout(() => {
+                const { queryForm, accountCreateLogs, launchMode } = JSON.parse(selectedRowKeys[0].strategyValue)
+                setLaunchMode(launchMode)
+                if (queryForm?.sysAdgroup) {
+                    if (queryForm?.sysAdgroup?.beginDate && moment(queryForm?.sysAdgroup?.beginDate) < moment()) {
+                        queryForm.sysAdgroup.beginDate = moment().format('YYYY-MM-DD')
+                        message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
+                    }
+                    if (queryForm?.sysAdgroup?.endDate && moment(queryForm?.sysAdgroup?.endDate) < moment()) {
+                        queryForm.sysAdgroup.endDate = moment().format('YYYY-MM-DD')
+                        message.warning('请注意,检测投放结束日期小于今天,已自动改成今天,如需修改,请重新设置')
+                    }
+                }
+                setQuery({ ...queryForm })
+                setAccountCreateLogs(accountCreateLogs)
+                message.success('设置成功')
+                hide()
+                setLoading(false)
+                setVisible(false)
+            }, 1500)
+        } else {
+            message.error('请选择策略组')
+        }
+    }
+
+    const del = (id: number) => {
+        delStrategy.run(id).then(res => {
+            message.success('删除成功')
+            getStrategy.refresh()
+        })
+    }
+
+    return <>
+        <Button type='primary' onClick={use}>使用策略组</Button>
+        {visible && <Modal
+            title='使用策略组'
+            visible={visible}
+            onCancel={() => { setVisible(false) }}
+            onOk={handleOk}
+            width={800}
+            confirmLoading={loading}
+        >
+            <Space style={{ width: '100%' }} direction='vertical'>
+                <Space>
+                    <Input allowClear placeholder="策略组名称" value={queryForm?.strategyKey} onChange={(e) => { setQueryForm({ ...queryForm, strategyKey: e.target.value }) }} />
+                    <Select
+                        style={{ width: 150 }}
+                        allowClear
+                        value={queryForm?.promotedObjectType}
+                        placeholder="请选择推广目标"
+                        showSearch
+                        filterOption={(input: any, option: any) =>
+                            (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                        }
+                        onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e }) }}
+                    >
+                        {Object.keys(PromotedObjectType).map(key => {
+                            return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
+                        })}
+                    </Select>
+                </Space>
+                <Table
+                    dataSource={getStrategy?.data}
+                    loading={getStrategy.loading}
+                    columns={Columns(del)}
+                    size="small"
+                    bordered
+                    rowKey={'id'}
+                    rowSelection={{
+                        type: 'radio',
+                        onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => {
+                            console.log(selectedRowKeys, selectedRows);
+                            setSelectedRowKeys(selectedRows)
+                        }
+                    }}
+                />
+            </Space>
+        </Modal>}
+    </>
+}
+
+
+export default React.memo(UserTactics)

+ 64 - 22
src/services/launchAdq/localAd.ts

@@ -12,7 +12,7 @@ export async function getSysAdgroupsList(params: {
   pageSize: number;
   adgroupName?: string;
   promotedObjectType?: string;
-}):Promise<ListData<SysAdgroupsDTO>>{
+}): Promise<ListData<SysAdgroupsDTO>> {
   return request(api + '/adq/sysAdgroups/list', {
     method: 'POST',
     data: params,
@@ -21,33 +21,75 @@ export async function getSysAdgroupsList(params: {
 /**
  * 获取广告详情
  * @param adgroupsId 广告ID
- */ 
- export async function getSysAdgroupsInfo(adgroupsId: any){
-    return request(api + `/adq/sysAdgroups/${adgroupsId}`)
-  }
+ */
+export async function getSysAdgroupsInfo(adgroupsId: any) {
+  return request(api + `/adq/sysAdgroups/${adgroupsId}`)
+}
 /**
  * 新增广告
- * */ 
-export async function createSysAdgroups(params:SysAdgroupsDTO){
-    return request(api+`/adq/sysAdgroups`,{
-        method:'POST',
-        data:params
-    })
+ * */
+export async function createSysAdgroups(params: SysAdgroupsDTO) {
+  return request(api + `/adq/sysAdgroups`, {
+    method: 'POST',
+    data: params
+  })
 }
 /**
  * 编辑广告
- * */ 
-export async function editSysAdgroups(adgroupsId:string,params:SysAdgroupsDTO){
-    return request(api+`/adq/sysAdgroups/${adgroupsId}`,{
-        method:'PUT',
-        data:params
-    })
+ * */
+export async function editSysAdgroups(adgroupsId: string, params: SysAdgroupsDTO) {
+  return request(api + `/adq/sysAdgroups/${adgroupsId}`, {
+    method: 'PUT',
+    data: params
+  })
 }
 /**
  * 删除广告
- * */ 
-export async function delSysAdgroups(adgroupsId:string){
-    return request(api+`/adq/sysAdgroups/${adgroupsId}`,{
-        method:'DELETE',
-    })
+ * */
+export async function delSysAdgroups(adgroupsId: string) {
+  return request(api + `/adq/sysAdgroups/${adgroupsId}`, {
+    method: 'DELETE',
+  })
+}
+
+export interface AddStrategyProps {
+  promotedObjectType: string,
+  strategyKey: string,
+  strategyValue: string,
+  remark?: string,
+  strategyId?: number
+}
+/**
+ * 新增策略组
+ * @param data 
+ * @returns 
+ */
+export async function addStrategyApi(data: AddStrategyProps) {
+  return request(api + `/adq/strategy/add`, {
+    method: 'POST',
+    data
+  })
 }
+
+/**
+ * 策略组列表
+ * @param params 
+ * @returns 
+ */
+export async function getStrategyApi(params: { promotedObjectType?: string, strategyKey?: string }) {
+  return request(api + `/adq/strategy/list`, {
+    method: 'GET',
+    params
+  })
+}
+
+/**
+ * 策略组删除
+ * @param strategyId 
+ * @returns 
+ */
+export async function delStrategyApi(strategyId: number) {
+  return request(api + `/adq/strategy/delete/${strategyId}`, {
+    method: 'POST'
+  })
+}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.