wjx před 2 roky
rodič
revize
3d7dd91f6b

+ 7 - 0
src/components/InputName/index.less

@@ -0,0 +1,7 @@
+.matching {
+    font-size: 12px;
+    &>span {
+        color: #999;
+        
+    }
+}

+ 97 - 0
src/components/InputName/index.tsx

@@ -0,0 +1,97 @@
+import { txtLength } from "@/utils/utils"
+import { DiffOutlined } from "@ant-design/icons"
+import { Button, Dropdown, Input, Menu, Space } from "antd"
+import { TextAreaProps } from "antd/lib/input/TextArea"
+import React, { useEffect, useMemo, useState } from "react"
+import './index.less'
+
+interface Props extends TextAreaProps {
+    length?: number
+}
+/**
+ * 广告名称 设置 组件
+ * @returns 
+ */
+const InputName: React.FC<Props> = ({ value, onChange, length, ...props }) => {
+
+    /******************************/
+    const [newValue, setNewValue] = useState(value)
+    const [status, setStatus] = useState<{ status?: string }>({})
+    /******************************/
+
+    const menu = useMemo(() => {
+        return <Menu
+            onClick={(e) => insertMatching(e.key)}
+            items={[
+                {
+                    key: '<广告版位>',
+                    label: '广告版位',
+                    disabled: newValue?.toString()?.includes('<广告版位>')
+                },
+                {
+                    key: '<账号备注>',
+                    label: '账号备注',
+                    disabled: newValue?.toString()?.includes('<账号备注>')
+                },
+                {
+                    key: '<商品ID>',
+                    label: '商品ID',
+                    disabled: newValue?.toString()?.includes('<商品ID>')
+                },
+                {
+                    key: '<账号ID>',
+                    label: '账号ID',
+                    disabled: newValue?.toString()?.includes('<账号ID>')
+                },
+                {
+                    key: '<日期>',
+                    label: '日期',
+                    disabled: newValue?.toString()?.includes('<日期>')
+                },
+                {
+                    key: '<时分秒>',
+                    label: '时分秒',
+                    disabled: newValue?.toString()?.includes('<时分秒>')
+                },
+            ]}
+        />
+    }, [newValue])
+
+    useEffect(() => {
+        setNewValue(value)
+        if (length && txtLength(value as any) > length) {
+            setStatus({ status: 'error' })
+        } else {
+            setStatus({})
+        }
+    }, [value])
+
+    /** 插入通配符 */
+    const insertMatching = (matching: string) => {
+        onChange?.(newValue + `-${matching}` as any)
+    }
+
+    return <Space direction="vertical">
+        <Space align="end">
+            <Input.TextArea {...props} value={newValue} onChange={(e) => {
+                onChange?.(e)
+            }} />
+            {length && <span style={Object.keys(status).length > 0 ? { color: 'red' } : {}}>{txtLength(newValue as any)}/{length}</span>}
+        </Space>
+        {/* <div className="matching">
+            <span>通配符:</span>
+            <Space>
+                <Button type="link" onClick={() => insertMatching('<定向名>')} style={{ padding: 0 }} disabled={newValue?.toString()?.includes('<定向名>')}>+定向名</Button>
+                <Button type="link" onClick={() => insertMatching('<素材名>')} style={{ padding: 0 }} disabled={newValue?.toString()?.includes('<素材名>')}>+素材名</Button>
+                <Button type="link" onClick={() => insertMatching('<落地页名>')} style={{ padding: 0 }} disabled={newValue?.toString()?.includes('<落地页名>')}>+落地页名</Button>
+                <Button type="link" onClick={() => insertMatching('<优化目标>')} style={{ padding: 0 }} disabled={newValue?.toString()?.includes('<优化目标>')}>+优化目标</Button>
+
+                <Dropdown overlay={menu} trigger={['click']}>
+                    <a style={{ marginLeft: 50 }} onClick={e => e.preventDefault()}><DiffOutlined /> 插入更多</a>
+                </Dropdown>
+            </Space>
+        </div> */}
+    </Space>
+}
+
+export default React.memo(InputName)

+ 1 - 1
src/global.less

@@ -383,7 +383,7 @@ body {
   }
 }
 
-.ant-select-selector, .ant-input, .ant-input-affix-wrapper, .ant-btn, .ant-modal-content, .ant-select-dropdown, .ant-input-number-input, .ant-tooltip-inner {
+.ant-select-selector, .ant-input, .ant-input-affix-wrapper, .ant-dropdown-menu, .ant-btn, .ant-modal-content, .ant-select-dropdown,.ant-input-number, .ant-input-number-input, .ant-tooltip-inner {
   border-radius: 4px !important;
 }
 

+ 10 - 14
src/pages/launchSystemNew/launchManage/createAd/ad/index.tsx

@@ -23,7 +23,7 @@ export interface ModalConfig {
 }
 function Ad(props: Props) {
     let { queryForm, getSysAdgroups, setQueryForm, clearData } = props
-    const queryOptimizationGoalPermissions = useAjax((params)=>getOptimizationGoalPermissions(params))
+    const queryOptimizationGoalPermissions = useAjax((params) => getOptimizationGoalPermissions(params))
     const [adVisible, setAdVisible] = useState<boolean>(false) // 选择广告弹窗控制
     const [adModalConfig, setAdModalConfig] = useState<ModalConfig>({//新建广告弹窗
         visible: false,
@@ -51,7 +51,7 @@ function Ad(props: Props) {
         <div className={style.top}>
             广告基本信息
             {queryForm.sysAdgroup && <a onClick={() => {
-                setQueryForm({ ...queryForm, sysAdgroup: undefined, sysAdgroupId: undefined, sysAdcreativeId: undefined ,pageList:[],taskMediaMaps:[]})
+                setQueryForm({ ...queryForm, sysAdgroup: undefined, sysAdgroupId: undefined, sysAdcreativeId: undefined, pageList: [], taskMediaMaps: [] })
             }}>清空</a>}
         </div>
         <div className={style.center}>
@@ -63,16 +63,12 @@ function Ad(props: Props) {
         </div>
         <div className={style.bottom}>
             <Space size={20}>
-                {
-                    queryForm?.promotedObjectType ? <span onClick={() => { setAdVisible(true) }}>{queryForm?.sysAdgroup ? '重选广告' : '选择广告'}</span> : <Tooltip title="请先选择推广目标">
-                        <span>选择广告</span>
-                    </Tooltip>
-                }
-                {
-                    queryForm?.promotedObjectType ? <span onClick={() => { handleAdModalConfig(queryForm?.sysAdgroup ? { visible: true, type: 'edit' } : { visible: true, type: 'add' }) }}>{queryForm?.sysAdgroup ? '编辑广告' : '新建广告'}</span> : <Tooltip title="请先选择推广目标">
-                        <span>新建广告</span>
-                    </Tooltip>
-                }
+                {queryForm?.promotedObjectType ? <span onClick={() => { setAdVisible(true) }}>{queryForm?.sysAdgroup ? '重选广告' : '选择广告'}</span> : <Tooltip title="请先选择推广目标">
+                    <span>选择广告</span>
+                </Tooltip>}
+                {queryForm?.promotedObjectType ? <span onClick={() => { handleAdModalConfig(queryForm?.sysAdgroup ? { visible: true, type: 'edit' } : { visible: true, type: 'add' }) }}>{queryForm?.sysAdgroup ? '编辑广告' : '新建广告'}</span> : <Tooltip title="请先选择推广目标">
+                    <span>新建广告</span>
+                </Tooltip>}
             </Space>
         </div>
         {/* 选择广告 */}
@@ -81,12 +77,12 @@ function Ad(props: Props) {
         {adModalConfig.visible && queryForm.promotedObjectType === "PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT" && <WeChatAdModal visible={adModalConfig.visible} PupFn={handleAdModalConfig} callback={(values) => {
             setQueryForm({ ...queryForm, sysAdgroup: values, sysAdcreativeId: undefined }); setAdVisible(false); clearData()
             handleAdModalConfig({ visible: false, dataInfo: null, type: 'add' })
-        }} type={adModalConfig.type} dataInfo={queryForm.sysAdgroup} queryForm={queryForm} ajax={queryOptimizationGoalPermissions}/>}
+        }} type={adModalConfig.type} dataInfo={queryForm.sysAdgroup} queryForm={queryForm} ajax={queryOptimizationGoalPermissions} />}
         {/* 收集线索广告弹窗 */}
         {adModalConfig.visible && queryForm.promotedObjectType === "PROMOTED_OBJECT_TYPE_LEAD_AD" && <LeadAdModal visible={adModalConfig.visible} PupFn={handleAdModalConfig} callback={(values) => {
             setQueryForm({ ...queryForm, sysAdgroup: values, sysAdcreativeId: undefined }); setAdVisible(false); clearData()
             handleAdModalConfig({ visible: false, dataInfo: null, type: 'add' })
-        }} type={adModalConfig.type} dataInfo={queryForm.sysAdgroup} queryForm={queryForm} ajax={queryOptimizationGoalPermissions}/>}
+        }} type={adModalConfig.type} dataInfo={queryForm.sysAdgroup} queryForm={queryForm} ajax={queryOptimizationGoalPermissions} />}
     </Col>
 }
 export default Ad

+ 43 - 6
src/pages/launchSystemNew/launchManage/createAd/ad/modal/leadAd.tsx

@@ -10,8 +10,9 @@ import { CreateAdProps } from '@/services/launchAdq/createAd';
 import { createSysAdgroups } from '@/services/launchAdq/localAd';
 import AdPositionList from './adPositionList';
 import BidAdjustment from './bidAdjustment';
-import { useModel } from 'umi';
 import { RangePickerProps } from 'antd/lib/date-picker';
+import { txtLength } from '@/utils/utils';
+import InputName from '@/components/InputName';
 const { RangePicker }: { RangePicker: any } = DatePicker;
 let DatePickers: any = DatePicker
 interface Props {
@@ -44,8 +45,7 @@ export interface SiteSetPackageDataProps {
 
 /**收集线索广告弹窗*/
 function LeadAdModal(props: Props) {
-    let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm, ajax } = props
-    const { currentUser }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
+    let { visible, PupFn, callback, type, dataInfo, queryForm, ajax } = props
     const createSysAdgroup = useAjax((params) => createSysAdgroups(params))
     let arg = type === 'look' ? { footer: null } : {}
     let [state, setState] = useState<any>({
@@ -270,7 +270,7 @@ function LeadAdModal(props: Props) {
             }
         } else {
             form.setFieldsValue({ 
-                adgroupName: '广告_销售线索_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId,
+                adgroupName: '广告_销售线索',
                 date: moment().startOf('day').add(2, 'M'),
                 optimizationGoal: "OPTIMIZATIONGOAL_ECOMMERCE_ORDER",
                 bidStrategy: "BID_STRATEGY_TARGET_COST",
@@ -414,8 +414,45 @@ function LeadAdModal(props: Props) {
         >
             {/* ============================================================基本信息============================================================= */}
             <Divider orientation='center'>基本信息</Divider>
-            <Form.Item label={<strong>广告名称</strong>} name='adgroupName' rules={[{ required: true, message: '请输入广告名称!' }]}>
-                <Input placeholder='广告名称' style={{ width: 300 }} />
+            <Form.Item
+                label={<strong>广告名称</strong>}
+                name='adgroupName'
+                tooltip="下标、日期时分秒、广告账户创建时默认自带"
+                rules={[
+                    { required: true, message: '请输入广告名称!' },
+                    {
+                        required: true, message: '广告名称不能包含特殊字符:< > & ‘ ” / 以及TAB、换行、回车键,请修改', validator(rule, value, callback) {
+                            let reg = /[&‘’“”/\n\t\f]/ig
+                            if (value && reg.test(value)) {
+                                return Promise.reject()
+                            }
+                            return Promise.resolve()
+                        },
+                    },
+                    // {
+                    //     required: true, message: '您可能使用了不支持的通配符,请您确认后再保存', validator(rule, value, callback) {
+                    //         let reg = /(<账号备注>)|(<商品ID>)|(<账号ID>)|(<日期>)|(<时分秒>)|(<定向名>)|(<素材名>)|(<落地页名>|(<优化目标>)/ig
+                    //         let reg1 = /[<>]/ig
+                    //         if (value && (reg1.test(value))) {
+                    //             if (reg.test(value)) {
+                    //                 return Promise.resolve()
+                    //             }
+                    //             return Promise.reject()
+                    //         }
+                    //         return Promise.resolve()
+                    //     },
+                    // },
+                    {
+                        required: true, message: '请确保广告名称长度不超过60个字(1个汉字等于2个字符)', validator(rule, value, callback) {
+                            if (value && txtLength(value) > 60) {
+                                return Promise.reject()
+                            }
+                            return Promise.resolve()
+                        },
+                    }
+                ]}
+            >
+                <InputName placeholder='广告名称' style={{ width: 400 }} length={60} />
             </Form.Item>
             <Form.Item label={<strong>广告版位</strong>}>
                 <Form.Item name='automaticSiteEnabled'>

+ 44 - 7
src/pages/launchSystemNew/launchManage/createAd/ad/modal/wechat.tsx

@@ -11,8 +11,9 @@ import { createSysAdgroups } from '@/services/launchAdq/localAd';
 import AdPositionList from './adPositionList';
 import { siteSetData, SiteSetPackageDataProps } from './leadAd';
 import BidAdjustment from './bidAdjustment';
-import { useModel } from 'umi';
 import { RangePickerProps } from 'antd/lib/date-picker';
+import InputName from '@/components/InputName';
+import { txtLength } from '@/utils/utils';
 const { RangePicker }: { RangePicker: any } = DatePicker;
 let DatePickers: any = DatePicker
 interface Props {
@@ -29,8 +30,7 @@ interface Props {
 function WeChatAdModal(props: Props) {
 
     /*******************************/
-    let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm, ajax } = props
-    const { currentUser }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
+    let { visible, PupFn, callback, type, dataInfo, queryForm, ajax } = props
     const createSysAdgroup = useAjax((params) => createSysAdgroups(params))
     let [state, setState] = useState<any>({ isShowTime: [] })
     let [template_checked, settemplate_checked] = useState<boolean>(dataInfo?.isTemplate || false)
@@ -251,7 +251,7 @@ function WeChatAdModal(props: Props) {
             }
         } else {
             form.setFieldsValue({
-                adgroupName: '广告_微信朋友圈_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId,
+                adgroupName: '广告_微信朋友圈', // + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId,
                 date: moment().startOf('day').add(2, 'M'),
                 optimizationGoal: "OPTIMIZATIONGOAL_ECOMMERCE_ORDER",
                 bidStrategy: "BID_STRATEGY_TARGET_COST",
@@ -395,8 +395,45 @@ function WeChatAdModal(props: Props) {
         >
             {/* ============================================================基本信息============================================================= */}
             <Divider orientation='center'>基本信息</Divider>
-            <Form.Item label={<strong>广告名称</strong>} name='adgroupName' rules={[{ required: true, message: '请输入广告名称!' }]}>
-                <Input placeholder='广告名称' style={{ width: 300 }} />
+            <Form.Item
+                label={<strong>广告名称</strong>}
+                name='adgroupName'
+                tooltip="下标、日期时分秒、广告账户创建时默认自带"
+                rules={[
+                    { required: true, message: '请输入广告名称!' },
+                    {
+                        required: true, message: '广告名称不能包含特殊字符:< > & ‘ ” / 以及TAB、换行、回车键,请修改', validator(rule, value, callback) {
+                            let reg = /[&‘’“”/\n\t\f]/ig
+                            if (value && reg.test(value)) {
+                                return Promise.reject()
+                            }
+                            return Promise.resolve()
+                        },
+                    },
+                    // {
+                    //     required: true, message: '您可能使用了不支持的通配符,请您确认后再保存', validator(rule, value, callback) {
+                    //         let reg = /(<账号备注>)|(<商品ID>)|(<账号ID>)|(<日期>)|(<时分秒>)|(<定向名>)|(<素材名>)|(<落地页名>)/ig
+                    //         let reg1 = /[<>]/ig
+                    //         if (value && (reg1.test(value))) {
+                    //             if (reg.test(value)) {
+                    //                 return Promise.resolve()
+                    //             }
+                    //             return Promise.reject()
+                    //         }
+                    //         return Promise.resolve()
+                    //     },
+                    // },
+                    {
+                        required: true, message: '请确保广告名称长度不超过60个字(1个汉字等于2个字符)', validator(rule, value, callback) {
+                            if (value && txtLength(value) > 60) {
+                                return Promise.reject()
+                            }
+                            return Promise.resolve()
+                        },
+                    }
+                ]}
+            >
+                <InputName placeholder='广告名称' style={{ width: 400 }} length={60} />
             </Form.Item>
             <Form.Item label={<strong>广告版位</strong>}>
                 <Form.Item name='automaticSiteEnabled'>
@@ -489,7 +526,7 @@ function WeChatAdModal(props: Props) {
                 dateType === '1' ? <Form.Item name='date' rules={[{ required: true, message: '请选择日期' }]}>
                     <RangePicker style={{ marginLeft: 177 }} disabledDate={disabledDate}></RangePicker>
                 </Form.Item> : <Form.Item name='date' style={{ marginLeft: 177 }} rules={[{ required: true, message: '请选择日期' }]}>
-                    <DatePickers disabledDate={disabledDate}/>
+                    <DatePickers disabledDate={disabledDate} />
                 </Form.Item>
             }
             <Form.Item label={<strong>投放时段</strong>}>

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

@@ -57,7 +57,7 @@ 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 }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
     const [goodsVisible, setGoodsVisible] = useState<boolean>(false) // 选择商品弹窗控制
     const [sourceVisible, setSourceVisible] = useState<boolean>(false) // 选择数据源弹窗控制
     const [idVisible, setIdVisible] = useState<boolean>(false) // 选择转化ID弹窗控制

+ 0 - 2
src/pages/launchSystemNew/material/cloud/DndContainer.tsx

@@ -1,2 +0,0 @@
-export const BoxClassName = 'box'
-export const BoxContainer = 'selectContainer'