wjx 2 년 전
부모
커밋
be7dd4f25a

+ 7 - 7
config/routerConfig.ts

@@ -97,13 +97,13 @@ const launchSystem = {
                     name: '广告模板',
                     access: 'localAd',
                     component: './launchSystemNew/launchManage/localAd',  
-                }
-                // {
-                //     path: '/launchSystemNew/launchManage/weChat',
-                //     name: '微信',
-                //     access: 'weChat',
-                //     component: './launchSystemNew/launchManage/weChat',
-                // },
+                },
+                {
+                    path: '/launchSystemNew/launchManage/createAd',
+                    name: '创建广告',
+                    access: 'createAd',
+                    component: './launchSystemNew/launchManage/createAd',
+                },
                 // {
                 //     path: '/launchSystemNew/launchManage/taskList',
                 //     name: '任务列表',

+ 1 - 1
package.json

@@ -62,7 +62,7 @@
     "@types/sortablejs": "^1.10.6",
     "@types/spark-md5": "^3.0.2",
     "ahooks": "^2.9.1",
-    "antd": "^4.16.13",
+    "antd": "4.20.0",
     "antd-img-crop": "^3.13.2",
     "array-move": "^3.0.1",
     "classnames": "^2.2.6",

+ 10 - 7
src/pages/launchSystemNew/components/TableData/index.tsx

@@ -36,10 +36,12 @@ interface Prosp {
         }
     }) => void,
     ajax?: any,//接口刷新
+    hoverable?: boolean,
+    rowSelection?: any
 }
 
 function TableData(props: Prosp) {
-    const { isZj, scroll, columns, title, dataSource, expandedRowRender, className, leftChild, page = undefined, pageSize = undefined, size = 'small', total = 0, loading = false, onChange, config, configName, ajax } = props
+    const { isZj, scroll, columns, title, dataSource, expandedRowRender, className, leftChild, page = undefined, rowSelection, pageSize = undefined, size = 'small', total = 0, loading = false, onChange, config, configName, ajax, hoverable = true } = props
     const { state: userState } = useModel('useOperating.useUser')
     const { isFell } = userState
     const [visible, setVisible] = useState<boolean>(false)
@@ -55,7 +57,7 @@ function TableData(props: Prosp) {
     useEffect(() => {
         let oldConfigName = oldName.current || ''
         if (configName) {
-        // if (dataSource?.length > 0) {
+            // if (dataSource?.length > 0) {
             const defSelectData = localStorage.getItem(`myAdMonitorConfig${version}_` + configName)
             const defFixed = localStorage.getItem(`myAdMonitorConfigFixed${version}_` + configName)
             const newConfig = config?.map((item: { data: any }) => item.data)?.flat()
@@ -139,7 +141,7 @@ function TableData(props: Prosp) {
                             <Tooltip title='刷新'><RedoOutlined /></Tooltip>
                         </Button>
                     }
-                   {config && <Button
+                    {config && <Button
                         size='small'
                         type='text'
                         onClick={() => {
@@ -159,7 +161,7 @@ function TableData(props: Prosp) {
                             <Tooltip title={!isFell ? '全屏' : '退出全屏'}>{!isFell ? <FullscreenOutlined /> : <FullscreenExitOutlined />}</Tooltip>
                         }
                     </Button>
-                    {visible && <CustomListModel  version={version} config={config} configName={configName} visible={visible} onClose={() => { setVisible(false) }} onChange={(arr: any) => { setSelectData(arr) }} columns={newColumns?.length > 0 ? newColumns : columns()} />}
+                    {visible && <CustomListModel version={version} config={config} configName={configName} visible={visible} onClose={() => { setVisible(false) }} onChange={(arr: any) => { setSelectData(arr) }} columns={newColumns?.length > 0 ? newColumns : columns()} />}
                 </Space>
             </Col>
         </Row>
@@ -168,13 +170,13 @@ function TableData(props: Prosp) {
         {/**table */}
         <Col span={24}>
             <Card
-                hoverable
+                hoverable={hoverable}
                 title={title}
                 headStyle={{ textAlign: 'left' }}
             >
                 <Row gutter={[0, 20]}>
                     {header}
-                    <Tab {...{ size, newColumns, handelResize, className, isZj, columns, loading, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax }} />
+                    <Tab {...{ size, newColumns, handelResize, className, isZj, rowSelection, columns, loading, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax }} />
                 </Row>
             </Card>
         </Col>
@@ -186,7 +188,7 @@ function TableData(props: Prosp) {
 
 /**表格 */
 const Tab = React.memo((props: any) => {
-    const { size, newColumns, className, handelResize, columns, scroll, loading, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax } = props
+    const { size, newColumns, className, handelResize, columns, scroll, loading, rowSelection, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax } = props
     return < Col span={24} >
         <div className={`${style[size]} ${className ? style[className] : ''} `}>
             {dataSource || !ajax?.loading ? <Tables
@@ -205,6 +207,7 @@ const Tab = React.memo((props: any) => {
                 total={total}
                 loading={loading}
                 defaultPageSize={20}
+                rowSelection={rowSelection || {}}
                 handelResize={((columns: any) => handelResize(columns))}
             /> : <div className={style.example}>
                 <Spin />

+ 102 - 0
src/pages/launchSystemNew/components/adModal/index.tsx

@@ -0,0 +1,102 @@
+import { FnAjax, useAjax } from "@/Hook/useAjax"
+import { ListData, SysAdgroupsDTO } from "@/services/launchAdq"
+import { getSysAdgroupsList } from "@/services/launchAdq/localAd"
+import { Col, Input, Modal, Row } from "antd"
+import React, { useCallback, useEffect, useState } from "react"
+import TableData from "../TableData"
+import tableConfig from "./tableConfig"
+
+
+/** 广告选择 */
+interface Props {
+    promotedObjectType: string,
+    visible?: boolean,
+    onClose?: () => void,
+    onChange?: (data: any) => void,
+}
+const AdModal: React.FC<Props> = (props) => {
+
+    /**********************/
+    const { visible, onClose, onChange, promotedObjectType } = props
+    const [oldsearchData, setOldsearchData] = useState<any>(null)
+    const sysAdgroupsList: FnAjax<ListData<SysAdgroupsDTO>> = useAjax((params) => getSysAdgroupsList(params))
+    const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
+    /**********************/
+
+    // 初始获取列表
+    useEffect(() => {
+        getList({ pageSize: 20, pageNum: 1, promotedObjectType })
+    }, [])
+    // 获取列表
+    const getList = useCallback((arg: { pageSize: number, pageNum: number, adgroupName?: string, promotedObjectType?: string }) => {
+        Object.keys(arg).forEach(key => {
+            !arg[key] && delete arg[key]
+        })
+        if (JSON.stringify(arg) !== JSON.stringify(oldsearchData)) {
+            setOldsearchData(arg)
+            sysAdgroupsList.run(arg)
+        }
+    }, [oldsearchData])
+
+
+    // 确定
+    const handleOk = () => {
+        onChange && onChange(selectedRowKeys.toString())
+    }
+
+    return <Modal title={`选择广告`} bodyStyle={{ padding: 0 }} width={1000} visible={visible} onOk={handleOk} onCancel={() => { onClose && onClose() }}>
+        <TableData
+            hoverable={false}
+            columns={tableConfig}
+            ajax={sysAdgroupsList}
+            dataSource={sysAdgroupsList?.data?.records}
+            loading={sysAdgroupsList?.loading}
+            scroll={{ y: 600 }}
+            total={sysAdgroupsList?.data?.total}
+            page={sysAdgroupsList?.data?.current}
+            pageSize={sysAdgroupsList?.data?.size}
+            rowSelection={{
+                type: 'radio',
+                selectedRowKeys: selectedRowKeys,
+                onChange: (selectedRowKeys: React.Key[]) => {
+                    setSelectedRowKeys(selectedRowKeys)
+                }
+            }}
+            leftChild={<>
+                <Row gutter={[10, 10]}>
+                    <Col>
+                        <Input
+                            placeholder='广告名称'
+                            allowClear
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, adgroupName: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, adgroupName: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, adgroupName: value })
+                                }
+                            }}
+                        />
+                    </Col>
+                </Row>
+            </>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                getList({ pageNum: current, pageSize })
+            }}
+        />
+
+    </Modal>
+}
+
+export default React.memo(AdModal)

+ 53 - 0
src/pages/launchSystemNew/components/adModal/tableConfig.tsx

@@ -0,0 +1,53 @@
+import { PromotedObjectType } from '@/services/launchAdq/enum'
+function tableConfig(): any {
+    return [
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            align: 'center',
+            width: 55,
+        },
+        {
+            title: '广告名称',
+            dataIndex: 'adgroupName',
+            key: 'adgroupName',
+            align: 'center',
+            width: 200
+        },
+        {
+            title: '广告推广目标类型',
+            dataIndex: 'promotedObjectType',
+            key: 'promotedObjectType',
+            align: 'center',
+            width: 130,
+            render: (a: string | number) => {
+                return PromotedObjectType[a]
+            }
+        },
+        {
+            title: '投放日期',
+            dataIndex: 'beginDate',
+            key: 'beginDate',
+            align: 'center',
+            width: 165,
+            render: (a: string, b: { endDate: string }) => {
+                return b?.endDate ? a + '~' + b.endDate : a + '~' + '长期投放'
+            }
+        },
+        {
+            title: '广告出价',
+            dataIndex: 'bidAmount',
+            key: 'bidAmount',
+            align: 'center',
+            width: 120
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+        },
+    ]
+}
+export default tableConfig

+ 101 - 0
src/pages/launchSystemNew/components/targetingModal/index.tsx

@@ -0,0 +1,101 @@
+import { FnAjax, useAjax } from "@/Hook/useAjax"
+import { ListData } from "@/services/launchAdq"
+import { getsysTargetingList } from "@/services/launchAdq/targeting"
+import { Col, Input, Modal, Row } from "antd"
+import React, { useCallback, useEffect, useState } from "react"
+import TableData from "../TableData"
+import tableConfig from "./tableConfig"
+
+
+/** 定向选择 */
+interface Props {
+    visible?: boolean,
+    onClose?: () => void,
+    onChange?: (data: any) => void,
+}
+const TargetingModal: React.FC<Props> = (props) => {
+
+    /*****************************/
+    const { visible, onClose, onChange } = props
+    const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
+    const [oldsearchData, setOldsearchData] = useState<any>(null)
+
+    const list: FnAjax<ListData<any>> = useAjax((params) => getsysTargetingList(params))
+    /*****************************/
+
+    // 初始获取列表
+    useEffect(() => {
+        getList({ pageSize: 20, pageNum: 1 })
+    }, [])
+    // 获取列表
+    const getList = useCallback((arg: { pageSize: number, pageNum: number, targetingName?: string, promotedObjectType?: string }) => {
+        Object.keys(arg).forEach(key => {
+            !arg[key] && delete arg[key]
+        })
+        if (JSON.stringify(arg) !== JSON.stringify(oldsearchData)) {
+            setOldsearchData(arg)
+            list.run(arg)
+        }
+    }, [oldsearchData])
+
+    // 确定
+    const handleOk = () => {
+        onChange && onChange(selectedRowKeys.toString())
+    }
+
+    return <Modal title={`选择定向`} bodyStyle={{ padding: 0 }} width={1000} visible={visible} onOk={handleOk} onCancel={() => { onClose && onClose() }}>
+        <TableData
+            hoverable={false}
+            columns={tableConfig}
+            ajax={list}
+            dataSource={list?.data?.records}
+            loading={list?.loading}
+            scroll={{ y: 600 }}
+            total={list?.data?.total}
+            page={list?.data?.current}
+            pageSize={list?.data?.size}
+            rowSelection={{
+                type: 'radio',
+                selectedRowKeys: selectedRowKeys,
+                onChange: (selectedRowKeys: React.Key[]) => {
+                    setSelectedRowKeys(selectedRowKeys)
+                }
+            }}
+            leftChild={<>
+                <Row gutter={[10, 10]}>
+                    <Col>
+                        <Input
+                            placeholder='定向名称'
+                            allowClear
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, targetingName: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, targetingName: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, targetingName: value })
+                                }
+                            }}
+                        />
+                    </Col>
+                </Row>
+            </>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                getList({ pageNum: current, pageSize })
+            }}
+        />
+    </Modal>
+}
+
+
+export default React.memo(TargetingModal)

+ 29 - 0
src/pages/launchSystemNew/components/targetingModal/tableConfig.tsx

@@ -0,0 +1,29 @@
+function tableConfig():any{
+    return [
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            align: 'center',
+        },
+        {
+            title: '定向名称',
+            dataIndex: 'targetingName',
+            key: 'targetingName',
+            align: 'center',
+        },
+        {
+            title: '定向描述',
+            dataIndex: 'description',
+            key: 'description',
+            align: 'center',
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+        },
+    ]
+}
+export default tableConfig

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

@@ -0,0 +1,159 @@
+.createAd {
+  border-radius: 8px;
+
+  .cardTitle {
+    font-weight: 600;
+  }
+}
+
+
+.selector {
+  border: 1px solid #dcdee2;
+  border-radius: 4px;
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  transition: all .3s;
+
+  &:hover {
+    border: 1px solid #1890ff;
+  }
+
+  .selectorLabel {
+    flex: 1;
+    display: inline-block;
+    height: 100%;
+    padding: 0 10px;
+    border-right: 1px solid #dcdee2;
+    color: #333;
+  }
+
+  &:hover .selectorLabel {
+    color: #1890ff;
+    border-right: 1px solid #1890ff;
+  }
+}
+
+.cardBody {
+
+  margin-top: 24px;
+
+  .content {
+    border: 1px solid #f0f0f0;
+    box-sizing: border-box;
+
+    .conTitle {
+      height: 38px;
+      line-height: 38px;
+      border-bottom: 1px solid #f0f0f0;
+      padding: 0 10px;
+      box-sizing: border-box;
+      font-weight: 600;
+      font-size: 16px;
+    }
+
+    .conRightBorder {
+      border-right: 1px solid #f0f0f0;
+      box-sizing: border-box;
+    }
+
+    .items {
+      &>div {
+        height: 380px;
+        // padding: 10px;
+        box-sizing: border-box;
+        display: flex;
+        flex-direction: column;
+
+        .top {
+          height: 24px;
+          display: flex;
+          font-weight: 600;
+          font-size: 15px;
+          margin-bottom: 10px;
+          justify-content: space-between;
+          align-items: center;
+          padding: 10px 10px 0;
+
+          &>span {
+            font-size: 12px;
+            color: rgb(90, 90, 90);
+          }
+        }
+
+        .center {
+          height: calc(100% - 48px);
+          overflow: hidden;
+          overflow-y: auto;
+          padding: 4px 0;
+          box-sizing: border-box;
+          padding: 0 10px;
+
+          .centerContent {
+            width: 100%;
+            min-height: 200px;
+          }
+          .acc {
+            margin-bottom: 14px;
+          }
+
+          .accName {
+            margin: 0 0 4px;
+          }
+
+          .accCon {
+            padding: 8px 5px;
+            background-color: rgba(0, 0, 0, .04);
+            margin-bottom: 2px;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+
+            >span.title {
+              width: 90%;
+              overflow: hidden;
+              text-overflow: ellipsis;
+              white-space: nowrap;
+            }
+
+            .close {
+              cursor: pointer;
+              margin-right: 10px;
+              display: none;
+            }
+
+            &:hover {
+              background-color: rgba(0, 0, 0, .08);
+
+              &>.close {
+                display: inline-block;
+              }
+            }
+          }
+        }
+
+        .bottom {
+          height: 40px;
+          text-align: center;
+          line-height: 40px;
+
+          span {
+            cursor: pointer;
+            color: #108ee9;
+          }
+
+          &:hover {
+            box-shadow: 0 0 4px 1px rgba(0, 0, 0, 0.08);
+          }
+        }
+      }
+    }
+  }
+
+  .bts {
+    width: 100%;
+    margin-top: 20px;
+    margin-bottom: 10px;
+    justify-content: flex-end;
+  }
+}

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

@@ -0,0 +1,223 @@
+import { useAjax } from "@/Hook/useAjax"
+import { CreateAdProps } from "@/services/launchAdq/createAd"
+import { BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType, SiteSetEnum } from "@/services/launchAdq/enum"
+import { getSysAdgroupsInfo } from "@/services/launchAdq/localAd"
+import { getsysTargetingInfo } from "@/services/launchAdq/targeting"
+import { Card, Col, Empty, Row, Select, Space, Spin, Tooltip } from "antd"
+import React, { useEffect, useState } from "react"
+import { useModel } from "umi"
+import AdModal from "../../components/adModal"
+import TargetingModal from "../../components/targetingModal"
+import style from './index.less'
+import Selector from "./selector"
+
+const CreateAd: React.FC = () => {
+
+    /*************************/
+    const { getAdAccount } = useModel('useLaunchAdq.useAdAuthorize')
+    const [queryForm, setQueryForm] = useState<Partial<CreateAdProps>>({
+        campaignName: '',
+        campaignType: 'CAMPAIGN_TYPE_NORMAL', // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH 
+        promotedObjectType: 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT', // 推广目标类型
+        speedMode: 'SPEED_MODE_STANDARD', // 投放速度模式
+        sysAdgroupsId: undefined,  // 广告组内容
+        sysTargetingId: undefined,  // 定向包 id
+        adName: undefined,  // 广告名称
+        configuredStatus: 'AD_STATUS_SUSPEND',  // 广告状态
+        sysAdcreativeId: undefined, // 创意ID
+        sysPageId: undefined, // 落地页Id
+    })
+    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSets?: number }[]>([])  // 账户
+
+    const [adVisible, setAdVisible] = useState<boolean>(false) // 选择广告弹窗控制
+    const [dxVisible, setDxVisible] = useState<boolean>(false) // 选择定向弹窗控制
+    const getSysAdgroups = useAjax((params) => getSysAdgroupsInfo(params))
+    const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
+    /*************************/
+
+    // 获取账户列表
+    useEffect(() => {
+        getAdAccount.run()
+    }, [])
+
+    /** 获取广告详情 */
+    useEffect(() => {
+        if (queryForm?.sysAdgroupsId) {
+            getSysAdgroups.run(queryForm?.sysAdgroupsId)
+        }
+    }, [queryForm?.sysAdgroupsId])
+    /** 获取定向详情 */
+    useEffect(() => {
+        if (queryForm?.sysTargetingId) {
+            getsysTargeting.run(queryForm?.sysTargetingId)
+        }
+    }, [queryForm?.sysTargetingId])
+
+    console.log('==========>', accountCreateLogs);
+
+
+
+    return <Card title={<div className={style.cardTitle}>配置区</div>} className={style.createAd} hoverable>
+        <Space>
+            <Selector label="媒体账户">
+                <Select
+                    mode="multiple"
+                    style={{ minWidth: 200 }}
+                    placeholder="请选择媒体账户"
+                    maxTagCount={1}
+                    allowClear
+                    bordered={false}
+                    filterOption={(input: any, option: any) =>
+                        (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                    }
+                    onChange={(e, option) => {
+                        setAccountCreateLogs(option?.map((item: any) => ({ adAccountId: item?.children, id: item?.value })))
+                    }}
+                >
+                    {getAdAccount?.data?.data?.map((item: any) => <Select.Option value={item.id} key={item.id}>{item.accountId}</Select.Option>)}
+                    {/* <Select.OptGroup label="Engineer">
+                        <Select.Option value="20632113">20632113</Select.Option>
+                    </Select.OptGroup> */}
+                </Select>
+            </Selector>
+
+            <Selector label="推广目标">
+                <Select style={{ width: 200 }} value={queryForm?.promotedObjectType} placeholder="请选择推广目标" bordered={false} showSearch filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                } onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e }) }}>
+                    {Object.keys(PromotedObjectType).map(key => {
+                        return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
+                    })}
+                </Select>
+            </Selector>
+        </Space>
+
+        <div className={style.cardBody}>
+            <Row className={style.content}>
+                <Col span={18} className={style.conLeft}>
+                    <Row className={`${style.conTitle} ${style.conRightBorder}`}><Col span={24}>广告</Col></Row>
+                    <Row className={style.items}>
+                        {/* =============广告基本信息=========== */}
+                        <Col span={6} className={style.conRightBorder}>
+                            <div className={style.top}>广告基本信息</div>
+                            <div className={style.center}>
+                                <Spin spinning={getSysAdgroups.loading}>
+                                    <div className={style.centerContent}>
+                                        {getSysAdgroups?.data ? <>
+                                            <div>广告名称: {getSysAdgroups?.data?.adgroupName}</div>
+                                            <div>推广目标: {PromotedObjectType[getSysAdgroups?.data?.promotedObjectType]}</div>
+                                            <div>广告版位: {getSysAdgroups?.data?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</div>
+                                            <div>投放日期: {getSysAdgroups?.data?.endDate ? getSysAdgroups?.data?.beginDate + '~' + getSysAdgroups?.data?.endDate : getSysAdgroups?.data?.beginDate + '~' + '长期投放'}</div>
+                                            <div>出价方式: {BidModeEnum[getSysAdgroups?.data?.bidMode]}</div>
+                                            <div>优化目标: {OptimizationGoalEnum[getSysAdgroups?.data?.optimizationGoal]}</div>
+                                            <div>出价类型: {getSysAdgroups?.data?.smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</div>
+                                            <div>出价策略: {BidStrategyEnum[getSysAdgroups?.data?.bidStrategy]}</div>
+                                            <div>广告出价: {getSysAdgroups?.data?.bidAmount}</div>
+                                            <div>广告日预算: {getSysAdgroups?.data?.dailyBudget || '不限'}</div>
+                                        </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
+                                    </div>
+                                </Spin>
+                            </div>
+                            <div className={style.bottom}>
+                                {queryForm?.promotedObjectType ? <span onClick={() => { setAdVisible(true) }}>{getSysAdgroups?.data ? '修改' : '添加'}</span> : <Tooltip title="请先选择推广目标">
+                                    <span>添加</span>
+                                </Tooltip>}
+                            </div>
+                        </Col>
+                        {/* =============定向包=========== */}
+                        <Col span={6} className={style.conRightBorder}>
+                            <div className={style.top}>
+                                定向{/* <span>已选:{1}</span> */}
+                            </div>
+                            <div className={style.center}>
+                                <Spin spinning={getsysTargeting.loading}>
+                                    <div className={style.centerContent}>
+                                        {getsysTargeting?.data ? <>
+                                            <div>定向名称: {getsysTargeting?.data?.targetingName}</div>
+                                            <div>定向描述: {getsysTargeting?.data?.description}</div>
+                                            <div>定向描述: {getsysTargeting?.data?.description}</div>
+                                        </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
+                                    </div>
+                                </Spin>
+                            </div>
+                            <div className={style.bottom}><span onClick={() => { setDxVisible(true) }}>{getsysTargeting?.data ? '修改' : '添加'}</span></div>
+                        </Col>
+                        {/* =============商品=========== */}
+                        <Col span={6} className={style.conRightBorder}>
+                            <div className={style.top}>
+                                商品<span>已选:{1}</span>
+                            </div>
+                            <div className={style.center}>
+
+                            </div>
+                            <div className={style.bottom}>
+                                {accountCreateLogs?.length > 0 ? <span onClick={() => { }}>添加</span> : <Tooltip title="请先选择媒体账户">
+                                    <span>添加</span>
+                                </Tooltip>}
+                            </div>
+                        </Col>
+                        {/* 数据源 */}
+                        <Col span={6} className={style.conRightBorder}>
+                            <div className={style.top}>
+                                数据源<span>已选:{1}</span>
+                            </div>
+                            <div className={style.center}>
+
+                            </div>
+                            <div className={style.bottom}>
+                                {accountCreateLogs?.length > 0 ? <span onClick={() => { }}>添加</span> : <Tooltip title="请先选择媒体账户">
+                                    <span>添加</span>
+                                </Tooltip>}
+                            </div>
+                        </Col>
+                    </Row>
+                </Col>
+                {/* =============广告创意=========== */}
+                <Col span={6} className={style.conRight}>
+                    <Row className={style.conTitle}><Col span={24}>广告创意</Col></Row>
+                    <Row className={style.items}>
+                        <Col span={24}>
+                            <div className={style.top}>创意基本信息</div>
+                            <div className={style.center}>
+
+                            </div>
+                            <div className={style.bottom}><span onClick={() => { }}>编辑</span></div>
+                        </Col>
+                        {/* <Col span={6}>
+                                <div className={style.top}>创意素材<span>已选:0</span></div>
+                                <div className={style.center}>
+                                </div>
+                                <div className={style.bottom}><span>添加</span></div>
+                            </Col>
+                            <Col span={6}>
+                                <div className={style.top}>创意文案</div>
+                                <div className={style.center}>
+                                </div>
+                                <div className={style.bottom}><span>添加</span></div>
+                            </Col>
+                            <Col span={6}>
+                                <div className={style.top}>落地页<span>已选:0</span></div>
+                                <div className={style.center}>
+                                </div>
+                                <div className={style.bottom}><span>添加</span></div>
+                            </Col> */}
+                    </Row>
+                </Col>
+            </Row>
+            {/* =============广告底部按钮=========== */}
+            {/* <Space className={style.bts}>
+                <Button type='primary' onClick={severBd}>暂存到本地</Button>
+                <Button type='primary' onClick={preview}><SearchOutlined /> 批量预览广告</Button>
+                <Button onClick={delBdPlan}>清空本地配置</Button>
+            </Space> */}
+        </div>
+
+        {/* 选择广告 */}
+        {adVisible && <AdModal visible={adVisible} onClose={() => setAdVisible(false)} promotedObjectType={queryForm.promotedObjectType as string} onChange={(e) => { setQueryForm({ ...queryForm, sysAdgroupsId: e }); setAdVisible(false) }} />}
+        {/* 选择定向 */}
+        {dxVisible && <TargetingModal visible={dxVisible} onClose={() => setDxVisible(false)} onChange={(e) => { setQueryForm({ ...queryForm, sysTargetingId: e }); setDxVisible(false) }} />}
+    </Card>
+}
+
+
+export default CreateAd

+ 19 - 0
src/pages/launchSystemNew/launchManage/createAd/selector.tsx

@@ -0,0 +1,19 @@
+import React from "react"
+import './index.less'
+
+interface Props {
+    label: string,
+    children: React.ReactNode;
+}
+const Selector: React.FC<Props> = (props) => {
+
+    /****************/
+    const { label, children } = props
+    /****************/
+
+    return <div className="selector">
+        <span className="selectorLabel">{label}</span>{children}
+    </div>
+}
+
+export default React.memo(Selector)

+ 1 - 1
src/pages/launchSystemNew/launchManage/localAd/ad/index.tsx

@@ -16,7 +16,7 @@ export interface ModalConfig {
 function Ad() {
     // 变量
     const [modalConfig, setModalConfig] = useState<ModalConfig>({
-        visible: true,
+        visible: false,
         title: '新建'
     })
     const [oldsearchData, setOldsearchData] = useState<any>(null)

+ 3 - 3
src/pages/launchSystemNew/launchManage/localAd/creative/index.tsx

@@ -93,10 +93,10 @@ function Creative() {
                         />
                     </Col>
                     <Col>
-                        <Select placeholder='推广目标选择' style={{ minWidth: 200 }} showSearch filterOption={(input, option) =>
+                        <Select placeholder='推广目标选择' style={{ minWidth: 200 }} showSearch filterOption={(input: any, option: any) =>
                             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
-                        } allowClear onChange={(value) => {
-                            getList({ pageNum: 1, pageSize: 20, promotedObjectType: value })
+                        } allowClear onChange={(value: any) => {
+                            getList({ pageNum: 1, pageSize: 20, promotedObjectType: value as any })
                         }}>
                             {
                                 Object.keys(PromotedObjectType).map(key => {

+ 97 - 0
src/pages/launchSystemNew/launchManage/localAd/goods/addorEdit.tsx

@@ -0,0 +1,97 @@
+import { useAjax } from "@/Hook/useAjax"
+import { CatalogScaleType, CatalogType, IndustryType } from "@/services/launchAdq/enum"
+import { addSysProductCatalogApi, editSysProductCatalogApi } from "@/services/launchAdq/goods"
+import { Form, Input, message, Modal, Select } from "antd"
+import React from "react"
+
+
+interface Props {
+    visible?: boolean,
+    title?: string,
+    onClose?: () => void,
+    onChange?: () => void,
+    initialValues?: any
+}
+/**
+ * 新增 修改 商品库
+ * @returns 
+ */
+const AddorEdit: React.FC<Props> = (props) => {
+    /****************************/
+    const { visible, title = '新增', onClose, onChange, initialValues = {} } = props
+    const [form] = Form.useForm()
+
+    const addSysProductCatalog = useAjax((params) => addSysProductCatalogApi(params))
+    const editSysProductCatalog = useAjax((params) => editSysProductCatalogApi(params))
+    /****************************/
+
+    const handleOk = async () => {
+        form.submit()
+        let data = await form.validateFields()
+        if (title === '新增') {
+            addSysProductCatalog.run({ ...data }).then(res => {
+                if (res) {
+                    message.success('新增成功')
+                    onChange && onChange()
+                }
+            })
+        } else {
+            editSysProductCatalog.run({ ...data, productCatalogId: initialValues?.id }).then(res => {
+                if (res) {
+                    message.success('修改成功')
+                    onChange && onChange()
+                }
+            })
+        }
+    }
+
+    return <Modal title={`${title}商品库`} visible={visible} onOk={handleOk} onCancel={() => { onClose && onClose() }}>
+        <Form
+            name="basic"
+            form={form}
+            labelCol={{ span: 6 }}
+            wrapperCol={{ span: 18 }}
+            autoComplete="off"
+            initialValues={{ ...initialValues }}
+        >
+            <Form.Item label="商品库名称" name="catalogName" rules={[{ type: 'string', required: true, message: '请输入商品库名称!' }]}>
+                <Input placeholder="输入商品库名称" />
+            </Form.Item>
+            <Form.Item label="商品库规模" name="catalogScaleType" rules={[{ type: 'string', required: true, message: '请输入商品库规模!' }]}>
+                <Select placeholder='商品库规模' style={{ minWidth: 200 }} showSearch allowClear
+                    filterOption={(input: any, option: any) =>
+                        (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                    }
+                >
+                    {Object.keys(CatalogScaleType).map(key => {
+                        return <Select.Option value={key} key={key}>{CatalogScaleType[key]}</Select.Option>
+                    })}
+                </Select>
+            </Form.Item>
+            <Form.Item label="商品库类型" name="catalogType" rules={[{ type: 'string', required: true, message: '请输入商品库类型!' }]}>
+                <Select placeholder='商品库类型' style={{ minWidth: 200 }} showSearch allowClear
+                    filterOption={(input: any, option: any) =>
+                        (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                    }
+                >
+                    {Object.keys(CatalogType).map(key => {
+                        return <Select.Option value={key} key={key}>{CatalogType[key]}</Select.Option>
+                    })}
+                </Select>
+            </Form.Item>
+            <Form.Item label="商品库行业类型" name="industryType" rules={[{ type: 'string', required: true, message: '请输入商品库行业类型!' }]}>
+                <Select placeholder='商品库行业类型' style={{ minWidth: 200 }} showSearch allowClear
+                    filterOption={(input: any, option: any) =>
+                        (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                    }
+                >
+                    {Object.keys(IndustryType).map(key => {
+                        return <Select.Option value={key} key={key}>{IndustryType[key]}</Select.Option>
+                    })}
+                </Select>
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(AddorEdit)

+ 66 - 0
src/pages/launchSystemNew/launchManage/localAd/goods/commodity/index.tsx

@@ -0,0 +1,66 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getSysProductApi, GetSysProductProps } from "@/services/launchAdq/goods"
+import { Button, Col, Drawer, Input, Row, Select } from "antd"
+import React, { useState } from "react"
+import TableData from '../../../../components/TableData'
+
+
+
+interface Props {
+    visible?: boolean,
+    onClose?: () => void,
+    onChange?: () => void,
+    data?: any
+}
+/**
+ * 商品管理
+ * @param props 
+ * @returns 
+ */
+const Commodity: React.FC<Props> = (props) => {
+
+    /*************************/
+    const { visible, onClose, onChange, data } = props
+    const [queryForm, setQueryFrom] = useState<GetSysProductProps>({ pageNum: 1, pageSize: 20 })
+
+    const getSysProduct = useAjax((params) => getSysProductApi(params))
+    /*************************/
+
+    return <Drawer title={data?.catalogName || '' + " 商品库"} placement="right" width={1000} onClose={() => { onClose && onClose() }} visible={visible}>
+        {/* <TableData
+            columns={tableConfig(handle)}
+            ajax={getSysProduct}
+            dataSource={getSysProduct?.data?.records}
+            loading={getSysProduct?.loading}
+            scroll={{ y: 600, x: 1500 }}
+            total={getSysProduct?.data?.total}
+            page={getSysProduct?.data?.current}
+            pageSize={getSysProduct?.data?.size}
+            leftChild={<>
+                <Row gutter={[10, 10]}>
+                    <Col span={24}><Button type='primary' onClick={() => {  }}>新建商品模板</Button></Col>
+                    <Col>
+                        <Input placeholder='商品名称' allowClear value={queryForm?.productName} onChange={(e) => { setQueryFrom({ ...queryForm, productName: e.target.value, pageNum: 1 }) }} />
+                    </Col>
+                    <Col>
+                        <Input placeholder='商品简称' allowClear value={queryForm?.productShortName} onChange={(e) => { setQueryFrom({ ...queryForm, productShortName: e.target.value, pageNum: 1 }) }} />
+                    </Col>
+                    <Col>
+                        <Input placeholder='商品描述' allowClear value={queryForm?.description} onChange={(e) => { setQueryFrom({ ...queryForm, description: e.target.value, pageNum: 1 }) }} />
+                    </Col>
+                    <Col>
+                        <Input placeholder='是否视频商品' allowClear value={queryForm?.description} onChange={(e) => { setQueryFrom({ ...queryForm, description: e.target.value, pageNum: 1 }) }} />
+                    </Col>
+                </Row>
+            </>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                setQueryFrom({ ...queryForm, pageNum: current, pageSize })
+            }}
+        /> */}
+
+    </Drawer>
+}
+
+export default React.memo(Commodity)

+ 126 - 0
src/pages/launchSystemNew/launchManage/localAd/goods/index.tsx

@@ -0,0 +1,126 @@
+import React, { useEffect, useState } from "react"
+import { delSysProductCatalogApi, getSysProductCatalogApi, GetSysProductCatalogProps } from '@/services/launchAdq/goods'
+import { useAjax } from "@/Hook/useAjax"
+import TableData from '../../../components/TableData'
+import tableConfig from './tableConfig'
+import { Button, Col, Input, message, Row, Select } from "antd"
+import { CatalogScaleType, CatalogType, IndustryType } from "@/services/launchAdq/enum"
+import AddorEdit from "./addorEdit"
+import Commodity from "./commodity"
+
+/**
+ * 商品库模板
+ * @returns 
+ */
+const Goods: React.FC = () => {
+
+    /** =================== */
+    const [queryForm, setQueryFrom] = useState<GetSysProductCatalogProps>({ pageNum: 1, pageSize: 20 })
+    const [modalConfig, setModalConfig] = useState<{ visible: boolean, title?: string, initialValues?: any }>({ visible: false, title: '新建' })
+    const [commodityConfig, setCommodityConfig] = useState<{ visible: boolean, data?: any }>({ visible: false })
+
+    const getSysProductCatalog = useAjax((params) => getSysProductCatalogApi(params))
+    const delSysProductCatalog = useAjax((params) => delSysProductCatalogApi(params))
+    /** =================== */
+
+    useEffect(() => {
+        getList()
+    }, [queryForm])
+
+    const getList = () => {
+        getSysProductCatalog.run(queryForm)
+    }
+
+    const handle = (type: 1 | 2 | 3, data: any) => {
+        switch (type) {
+            case 1:
+                setModalConfig({ visible: true, title: '修改', initialValues: data })
+                break
+            case 2:
+                setCommodityConfig({ visible: true, data })
+                break
+            case 3:
+                delSysProductCatalog.run({ productCatalogId: data?.id }).then(res => {
+                    if (res) {
+                        message.success('删除成功')
+                        getSysProductCatalog.refresh()
+                    }
+                })
+                break
+        }
+    }
+
+    return <>
+        <TableData
+            columns={tableConfig(handle)}
+            ajax={getSysProductCatalog}
+            dataSource={getSysProductCatalog?.data?.records}
+            loading={getSysProductCatalog?.loading}
+            scroll={{ y: 600, x: 1500 }}
+            total={getSysProductCatalog?.data?.total}
+            page={getSysProductCatalog?.data?.current}
+            pageSize={getSysProductCatalog?.data?.size}
+            leftChild={<>
+                <Row gutter={[10, 10]}>
+                    <Col span={24}><Button type='primary' onClick={() => { setModalConfig({ visible: true, title: '新增' }) }}>新建商品库模板</Button></Col>
+                    <Col>
+                        <Input placeholder='商品库名称' allowClear value={queryForm?.catalogName} onChange={(e) => { setQueryFrom({ ...queryForm, catalogName: e.target.value, pageNum: 1 }) }} />
+                    </Col>
+                    <Col>
+                        <Select placeholder='商品库规模' style={{ minWidth: 200 }} showSearch allowClear value={queryForm?.catalogScaleType}
+                            filterOption={(input: any, option: any) =>
+                                (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                            }
+                            onChange={(value: any) => {
+                                setQueryFrom({ ...queryForm, catalogScaleType: value, pageNum: 1 })
+                            }}
+                        >
+                            {Object.keys(CatalogScaleType).map(key => {
+                                return <Select.Option value={key} key={key}>{CatalogScaleType[key]}</Select.Option>
+                            })}
+                        </Select>
+                    </Col>
+                    <Col>
+                        <Select placeholder='商品库类型' style={{ minWidth: 200 }} showSearch allowClear value={queryForm?.catalogType}
+                            filterOption={(input: any, option: any) =>
+                                (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                            }
+                            onChange={(value: any) => {
+                                setQueryFrom({ ...queryForm, catalogType: value, pageNum: 1 })
+                            }}
+                        >
+                            {Object.keys(CatalogType).map(key => {
+                                return <Select.Option value={key} key={key}>{CatalogType[key]}</Select.Option>
+                            })}
+                        </Select>
+                    </Col>
+                    <Col>
+                        <Select placeholder='商品库行业类型' style={{ minWidth: 200 }} showSearch allowClear value={queryForm?.industryType}
+                            filterOption={(input: any, option: any) =>
+                                (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                            }
+                            onChange={(value: any) => {
+                                setQueryFrom({ ...queryForm, industryType: value, pageNum: 1 })
+                            }}
+                        >
+                            {Object.keys(IndustryType).map(key => {
+                                return <Select.Option value={key} key={key}>{IndustryType[key]}</Select.Option>
+                            })}
+                        </Select>
+                    </Col>
+                </Row>
+            </>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                setQueryFrom({ ...queryForm, pageNum: current, pageSize })
+            }}
+        />
+        {/* 新增修改商品库 */}
+        {modalConfig.visible && <AddorEdit {...modalConfig} onClose={() => setModalConfig({ visible: false })} onChange={() => { getSysProductCatalog.refresh(); setModalConfig({ visible: false }) }} />}
+        {/* 商品管理 */}
+        {commodityConfig.visible && <Commodity visible={commodityConfig.visible} onClose={() => setCommodityConfig({ visible: false })} onChange={() => { getSysProductCatalog.refresh(); setCommodityConfig({ visible: false }) }} />}
+    </>
+}
+
+export default React.memo(Goods)

+ 81 - 0
src/pages/launchSystemNew/launchManage/localAd/goods/tableConfig.tsx

@@ -0,0 +1,81 @@
+import { CatalogScaleType, CatalogType, IndustryType } from '@/services/launchAdq/enum'
+import { CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons'
+import { Button, Popconfirm, Space } from 'antd'
+import React from 'react'
+function tableConfig(handle: (type: 1 | 2 | 3, data: any) => void): any {
+    return () => {
+        return [
+            {
+                title: 'ID',
+                dataIndex: 'id',
+                key: 'id',
+                align: 'center',
+                width: 60
+            },
+            {
+                title: '商品库名称',
+                dataIndex: 'catalogName',
+                key: 'catalogName',
+                align: 'center',
+                width: 150
+            },
+            {
+                title: '商品库规模',
+                dataIndex: 'catalogScaleType',
+                key: 'catalogScaleType',
+                align: 'center',
+                width: 250,
+                render: (a: string | number) => {
+                    return CatalogScaleType[a]
+                }
+            },
+            {
+                title: '商品库类型',
+                dataIndex: 'catalogType',
+                key: 'catalogType',
+                align: 'center',
+                width: 120,
+                render: (a: string | number) => {
+                    return CatalogType[a]
+                }
+            },
+            {
+                title: '商品库行业类型',
+                dataIndex: 'industryType',
+                key: 'industryType',
+                align: 'center',
+                width: 120,
+                render: (a: string | number) => {
+                    return IndustryType[a]
+                }
+            },
+            {
+                title: '创建时间',
+                dataIndex: 'createTime',
+                key: 'createTime',
+                align: 'center',
+                width: 150,
+            },
+            {
+                title: '操作',
+                dataIndex: 'cz',
+                key: 'cz',
+                render: (a: any, b: any) => {
+                    return <Space>
+                        <Button type="link" style={{ padding: 0 }} size="small" icon={<EditOutlined />} onClick={() => handle(1, b)}>修改</Button>
+                        <Button type="link" style={{ padding: 0 }} size="small" icon={<CopyOutlined />} onClick={() => handle(2, b)}>商品管理</Button>
+                        <Popconfirm
+                            title="确定删除?"
+                            onConfirm={() => handle(3, b)}
+                            okText="是"
+                            cancelText="否"
+                        >
+                            <Button type="link" style={{ padding: 0 }} size="small" danger icon={<DeleteOutlined />}>删除</Button>
+                        </Popconfirm>
+                    </Space>
+                }
+            }
+        ]
+    }
+}
+export default tableConfig

+ 7 - 5
src/pages/launchSystemNew/launchManage/localAd/index.tsx

@@ -1,20 +1,22 @@
 import React, { useState } from 'react'
-import { Card, Button, Tabs } from 'antd';
+import { Card, Tabs } from 'antd';
 import './index.less'
 import Ad from './ad';
 import Creative from './creative';
 import Targeting from './targeting';
+
 const { TabPane } = Tabs;
 const tabsConfig = [
     // { key: '1', tab: '计划模板',jsx:<Campaign/> },
-    { key: '1', tab: '广告模板',jsx:<Ad/>},
-    { key: '2', tab: '创意模板' ,jsx:<Creative/>},
-    { key: '3', tab: '定向模板' ,jsx:<Targeting/>},
+    { key: '1', tab: '广告模板', jsx: <Ad /> },
+    { key: '2', tab: '创意模板', jsx: <Creative /> },
+    { key: '3', tab: '定向模板', jsx: <Targeting /> },
+    // { key: '4', tab: '商品库模板', jsx: <Goods /> },
 ]
 function LocalAd() {
     const [activeKey, setActiveKey] = useState('1')
     return <Card>
-        <Tabs activeKey={activeKey} type="card"  onChange={(activeKey) => { setActiveKey(activeKey) }} >
+        <Tabs activeKey={activeKey} type="card" onChange={(activeKey) => { setActiveKey(activeKey) }} >
             {
                 tabsConfig?.map(item => {
                     return <TabPane tab={item.tab} key={item.key} >

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

@@ -0,0 +1,34 @@
+import { request } from 'umi';
+import { api } from '../api';
+
+/*====================创建广告======================*/
+/**
+ * 获取商品库列表
+ */
+ export interface CreateAdProps {
+    campaignName: string, // 计划名称
+    campaignType: string, // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH 
+    promotedObjectType: string, // 推广目标类型
+    dailyBudget?: number,   // 推广计划日预算
+    totalBudget?: number, // 推广计划总预算
+    speedMode: string, // 投放速度模式
+    sysAdgroupsId: number,  // 广告组内容
+    sysTargetingId: number,  // 定向包 id
+    adName: string,  // 广告名称
+    configuredStatus: string,  // 广告状态
+    sysAdcreativeId: number, // 创意ID
+    sysPageId: number, // 落地页Id
+    accountCreateLogs: {
+        adAccountId: number, // 媒体账户ID
+        userActionSets?: number,  // 数据源
+        conversionId?: number, // 广告组 转化Id
+        productId?: number,
+        enterpriseWx?: any[]  // 企业微信客服组
+    }[]
+}
+export async function getSysProductCatalogApi(data: CreateAdProps) {
+    return request(api + `/adq/sysProductCatalog/list`, {
+        method: 'POST',
+        data
+    })
+}

+ 52 - 6
src/services/launchAdq/enum.ts

@@ -97,7 +97,19 @@ export enum OptimizationGoalEnum {
 /**推广计划类型*/
 export enum CampaignTypeEnum {
   CAMPAIGN_TYPE_NORMAL = '普通展示广告',
-  CAMPAIGN_TYPE_WECHAT_MOMENTS = '微信朋友圈广告',
+  CAMPAIGN_TYPE_SEARCH = '搜索广告计划',
+}
+
+/**投放速度模式*/
+export enum SpeedMode {
+  SPEED_MODE_FAST = '加速投放',
+  SPEED_MODE_STANDARD = '标准投放',
+}
+
+/**广告状态*/
+export enum AdStatus {
+  AD_STATUS_NORMAL = '有效',
+  AD_STATUS_SUSPEND = '暂停',
 }
 /**普通展示广告创意推广目标类型*/
 export enum NormalCreativePromotedObjectTypeEnum {
@@ -240,8 +252,42 @@ export enum NetworkEnum {
   NET_5G = '5G 网络',
 }
 /**联网方式*/
-export enum WechatAdBehaviorEnum{
-  WECHAT_OFFICIAL_ACCOUNT_AD_LIKE='曾对你的公众号广告感兴趣',
-  WECHAT_MOMENTS_AD_LIKE='曾对你的朋友圈广告感兴趣',
-  MINI_GAME_WECHAT_REGISTERED='已关注过你的公众号'
- }
+export enum WechatAdBehaviorEnum {
+  WECHAT_OFFICIAL_ACCOUNT_AD_LIKE = '曾对你的公众号广告感兴趣',
+  WECHAT_MOMENTS_AD_LIKE = '曾对你的朋友圈广告感兴趣',
+  MINI_GAME_WECHAT_REGISTERED = '已关注过你的公众号'
+}
+
+/** 商品库规模  */
+export enum CatalogScaleType {
+  CATALOG_SCALE_TYPE_NORMAL = '普通商品库,商品库规模不大于 100 万',
+  CATALOG_SCALE_TYPE_HUGE = '超大商品库,商品库规模大于 100 万'
+}
+
+/** 商品库类型 */
+export enum CatalogType {
+  CATALOG_TYPE_STANDARD = "标准商品库类型"
+}
+
+/** 商品库行业类型 */
+export enum IndustryType {
+  INDUSTRY_TYPE_ECOMMERCE = '普通电商',
+  INDUSTRY_TYPE_READING = '阅读行业',
+  INDUSTRY_TYPE_EDUCATION = '教育行业',
+  INDUSTRY_TYPE_WEDDING = '婚纱行业',
+  INDUSTRY_TYPE_VIDEO = '视频行业',
+  INDUSTRY_TYPE_INSURANCE = '保险行业',
+  INDUSTRY_TYPE_LOAN = '贷款行业',
+  INDUSTRY_TYPE_FINANCIAL = '理财行业',
+  INDUSTRY_TYPE_BANKCARD = '银行卡行业',
+  INDUSTRY_TYPE_SECURITIES = '证券行业',
+  INDUSTRY_TYPE_ESTATE = '房产租售',
+  INDUSTRY_TYPE_CARRIER = '运营商',
+  INDUSTRY_TYPE_MERCHANTS = '招商加盟',
+  INDUSTRY_TYPE_BUSINESS_SERVICE = '商务服务',
+  INDUSTRY_TYPE_DECORATION_BUILDING_MATERIAL = '家居行业',
+  INDUSTRY_TYPE_HOTEL = '旅游行业',
+  INDUSTRY_TYPE_CAR_ONLINE_PLATFORM = '汽车线上平台',
+  INDUSTRY_TYPE_CAR_AFTERMARKET = '汽车后市场',
+  INDUSTRY_TYPE_CAR_TRAVEL_SERVICE = '汽车出行服务'
+}

+ 112 - 0
src/services/launchAdq/goods.ts

@@ -0,0 +1,112 @@
+import { request } from 'umi';
+import { api } from '../api';
+
+
+/*====================商品库======================*/
+/**
+ * 获取商品库列表
+ */
+export interface GetSysProductCatalogProps {
+    pageNum: number,
+    pageSize: number,
+    catalogName?: string, // 商品库名称
+    catalogScaleType?: string, // 商品库规模
+    catalogType?: string, // 商品库类型
+    industryType?: string,  // 商品库行业类型
+}
+export async function getSysProductCatalogApi(data: GetSysProductCatalogProps) {
+    return request(api + `/adq/sysProductCatalog/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 新增商品库
+ */
+export type AddSysProductCatalogProps = Required<Omit<GetSysProductCatalogProps, 'pageNum' | 'pageSize'>>;
+export async function addSysProductCatalogApi(data: AddSysProductCatalogProps) {
+    return request(api + `/adq/sysProductCatalog`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 修改商品库
+ */
+export interface EditSysProductCatalogProps extends AddSysProductCatalogProps {
+    productCatalogId: number  // 商品库ID
+}
+export async function editSysProductCatalogApi(params: EditSysProductCatalogProps) {
+    const { productCatalogId, ...data } = params
+    return request(api + `/adq/sysProductCatalog/${productCatalogId}`, {
+        method: 'PUT',
+        data
+    })
+}
+
+/**
+ * 删除商品库
+ * @param data 
+ * @returns 
+ */
+export async function delSysProductCatalogApi(data: { productCatalogId: number }) {
+    return request(api + `/adq/sysProductCatalog/${data.productCatalogId}`, {
+        method: 'DELETE'
+    })
+}
+
+/**
+ * 获取商品库详情
+ * @param data 
+ * @returns 
+ */
+export async function getSysProductCatalogDetaliApi(data: { productCatalogId: number }) {
+    return request(api + `/adq/sysProductCatalog/${data.productCatalogId}`, {
+        method: 'GET'
+    })
+}
+
+
+/*====================商品======================*/
+
+
+/**
+ * 获取商品列表
+ */
+export interface GetSysProductProps {
+    pageNum: number,
+    pageSize: number,
+    productName?: string, // 商品名称
+    productShortName?: string, // 商品简称
+    description?: string, // 商品描述
+    isVideo?: boolean,  // 是否视频商品
+}
+export async function getSysProductApi(data: GetSysProductProps) {
+    return request(api + `/adq/sysProduct/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 新增商品库
+ */
+export type AddSysProductProps = {
+    description: string, // 商品描述
+    expirationTime: string, // 商品下架时间
+    isVideo: boolean, // 是否视频商品
+    price: number, // 商品日常售价
+    productImageUrl: string, // 商品预览图
+    productName: string,  // 商品名称
+    productShortName: string,  // 商品简称
+    videoDuration?: string,  // 商品视频长度
+    videoUrl?: string,  // 商品视频链接
+}
+export async function addSysProductApi(data: AddSysProductCatalogProps) {
+    return request(api + `/adq/sysProductCatalog`, {
+        method: 'POST',
+        data
+    })
+}