3 Commits 1eb3b9ad5f ... 9222d96920

Auteur SHA1 Bericht Datum
  wjx 9222d96920 Merge branch 'develop' of http://git.zanxiangnet.com/wjx/ad-manage 2 weken geleden
  wjx 27b9f32c96 fix 2 weken geleden
  wjx 42e9fff6a7 fix 3 weken geleden

+ 25 - 0
src/pages/launchSystemV3/material/cloudNew/const.ts

@@ -49,4 +49,29 @@ export const showField1List = [
     { label: '创意关联数', value: 'material_data_day.adgroup_count', field: 'adgroup_count' },
     { label: '创意关联数', value: 'material_data_day.dynamic_creative_count', field: 'dynamic_creative_count' },
     { label: '备注', value: 'description', field: 'description' }
+]
+
+
+export const showField2List = [
+    { label: '创建时间', value: 'createdTime', field: 'createdTime' },
+    { label: '组件ID', value: 'componentId', field: 'componentId' },
+    { label: '来源', value: 'generationType', field: 'generationType' },
+    { label: '消耗', value: 'cost', field: 'cost' },
+    { label: '一键起量消耗', value: 'acquisitionCost', field: 'acquisitionCost' },
+    { label: '千次展现均价', value: 'thousandDisplayPrice', field: 'thousandDisplayPrice' },
+    { label: '点击率', value: 'ctr', field: 'ctr' },
+    { label: '公众号关注成本(点击归因)', value: 'fromFollowByClickCost', field: 'fromFollowByClickCost' },
+    { label: '公众号关注人数(点击归因)', value: 'fromFollowByClickUv', field: 'fromFollowByClickUv' },
+    { label: '公众号关注成本(平台上报)', value: 'bizFollowCost', field: 'bizFollowCost' },
+    { label: '公众号关注人数(平台上报)', value: 'bizFollowUv', field: 'bizFollowUv' },
+    { label: '加企业微信客服成本(人数)', value: 'scanFollowUserCost', field: 'scanFollowUserCost' },
+    { label: '加企业微信客服人数', value: 'scanFollowUserCount', field: 'scanFollowUserCount' },
+    { label: '下单次数(点击归因)', value: 'orderByClickCount', field: 'orderByClickCount' },
+    { label: '下单成本(点击归因)', value: 'orderByClickCost', field: 'orderByClickCost' },
+    { label: '下单率(点击归因)', value: 'orderByClickRate', field: 'orderByClickRate' },
+    { label: '点击首日付费ROI', value: 'cheoutFdReward', field: 'cheoutFdReward' },
+    { label: '点击3日付费ROI', value: 'cheoutTdReward', field: 'cheoutTdReward' },
+    { label: '点击7日付费ROI', value: 'cheoutOwReward', field: 'cheoutOwReward' },
+    { label: '点击14日付费ROI', value: 'cheoutTwReward', field: 'cheoutTwReward' },
+    { label: '点击30日付费ROI', value: 'cheoutOmReward', field: 'cheoutOmReward' }
 ]

+ 117 - 15
src/pages/launchSystemV3/material/cloudNew/selectComponentsUnit.tsx

@@ -1,18 +1,19 @@
 import React, { useEffect, useState, useRef } from "react"
 import style from './index.less'
-import { Card, Checkbox, Divider, Empty, message, Pagination, Result, Space, Spin, Typography, Image } from "antd"
+import { Card, Checkbox, Divider, Empty, message, Pagination, Result, Space, Spin, Typography, Image, Popover, Form, Select, Radio, Button, Statistic } from "antd"
 import { useAjax } from "@/Hook/useAjax"
 import './global.less'
 import '../../tencentAdPutIn/index.less'
-import { EyeOutlined } from "@ant-design/icons"
+import { EyeOutlined, SortAscendingOutlined } from "@ant-design/icons"
 import PlayVideo from "./playVideo"
 import Lazyimg from "react-lazyimg-component"
-import SyncCloudSc from "./syncCloudSc"
 import { checkAccountUnitApi } from "@/services/launchAdq/adAuthorize"
-import { getCreativeComponentListApi, GetCreativeComponentProps } from "@/services/adqV3/global"
+import { getCreativeComponentDataListApi, GetCreativeComponentProps } from "@/services/adqV3/global"
 import { COMPONENT_GENERATION_TYPE_ENUM, COMPONENT_SUB_TYPE, getComponentType } from "../../tencenTasset/manageComponent/const"
-import moment from "moment"
 import SelectComponentsUnitSearch from "./selectComponentsUnitSearch"
+import { addOnlyDataApi, getOnlyDataApi } from "@/services/adqV3"
+import { showField2List } from "./const"
+import SyncCloudComponent from "./syncCloudComponent"
 
 const { Text, Paragraph } = Typography;
 
@@ -40,11 +41,29 @@ const SelectComponentsUnit: React.FC<Props> = ({ num: count, defaultParams, chec
     const [unitAccountId, setUnitAccountId] = useState<number>()
     const [previewData, setPreviewData] = useState<{ visible: boolean, url?: string[] }>({ visible: false })
     const [componentSubType, setComponentSubType] = useState<string[]>([])
+    const [showField, setShowField] = useState<string[]>(['createdTime', 'cost', 'ctr'])
+    const [sortData, setSortData] = useState<{ sortField: string | undefined, sortType: boolean }>({ sortField: undefined, sortType: false })
+    const [isGetField, setIsGetField] = useState<boolean>(false)
 
-    const getCreativeComponentList = useAjax((params) => getCreativeComponentListApi(params))
+    const getCreativeComponentList = useAjax((params) => getCreativeComponentDataListApi(params))
     const checkAccountUnit = useAjax((params) => checkAccountUnitApi(params))
+    const getOnlyData = useAjax((params) => getOnlyDataApi(params))
+    const addOnlyData = useAjax((params) => addOnlyDataApi(params))
     /*****************************************/
 
+    useEffect(() => {
+        getOnlyData.run({ type: 'MODE_FIELD_ENT_COMPONENTS' }).then(res => {
+            if (res?.data) {
+                const { showField, sortData } = JSON.parse(res.data)
+                setShowField(showField)
+                setSortData(sortData)
+                setTimeout(() => setIsGetField(true), 0)
+            } else {
+                setIsGetField(true)
+            }
+        }).catch(() => setIsGetField(true))
+    }, [])
+
     // 根据内容宽度计算列数
     useEffect(() => {
         let rowNum = Math.floor(1350 / 220)
@@ -80,7 +99,7 @@ const SelectComponentsUnit: React.FC<Props> = ({ num: count, defaultParams, chec
     }, [accountCreateLogs])
 
     useEffect(() => {
-        if (unitAccountId) {
+        if (unitAccountId && isGetField) {
             const materialType = defaultParams.materialType
             let componentSubType: string[] = []
             if (isGroup) {
@@ -121,13 +140,16 @@ const SelectComponentsUnit: React.FC<Props> = ({ num: count, defaultParams, chec
                 }
             }
             setComponentSubType(componentSubType)
-            const params = { ...searchParams, ...queryParams, adAccountId: unitAccountId, isDeleted: false }
+            let params = { ...searchParams, ...queryParams, adAccountId: unitAccountId, isDeleted: false }
+            if (sortData?.sortField) {
+                params = { ...params, sortColumn: sortData.sortField, sortAsc: sortData.sortType }
+            }
             if (!params?.componentSubType?.length) {
                 params['componentSubType'] = componentSubType
             }
             getCreativeComponentList.run(params)
         }
-    }, [queryParams, defaultParams, searchParams, unitAccountId, isGroup, count])
+    }, [queryParams, defaultParams, searchParams, unitAccountId, isGroup, count, sortData, showField, isGetField])
 
     // 选择
     const onCheckboxChange = (checked: boolean, item: any, type: "IMAGE" | "VIDEO" | "IMAGE_LIST" | "OTHER") => {
@@ -257,13 +279,83 @@ const SelectComponentsUnit: React.FC<Props> = ({ num: count, defaultParams, chec
                                     >全选</Checkbox>
                                     <span>已选 <span style={{ color: '#1890FF' }}>{checkedFolderList?.length || 0}</span>/{num} 个素材</span>
                                     {checkedFolderList.length > 0 && <a style={{ color: 'red' }} onClick={() => setCheckedFolderList([])}>清除所有</a>}
-                                    {/* 同步素材 */}
-                                    <SyncCloudSc
+                                    {sortData?.sortField && <Text>「排序-{showField2List.find(item => item.value === sortData.sortField)?.label}-{sortData.sortType ? '正序' : '倒序'}」</Text>}
+                                    {/* 同步组件 */}
+                                    <SyncCloudComponent
                                         accountId={unitAccountId}
                                     />
                                 </div>
                                 <div className={style.left_bts}>
-
+                                    <Popover
+                                        content={<div style={{ width: 320 }}>
+                                            <Form
+                                                labelCol={{ span: 5 }}
+                                                labelAlign="left"
+                                                colon={false}
+                                            >
+                                                <Form.Item label={<strong>展示指标</strong>}>
+                                                    <Select
+                                                        placeholder="选择展示指标"
+                                                        showSearch
+                                                        filterOption={(input, option) =>
+                                                            (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                                                        }
+                                                        mode="multiple"
+                                                        options={showField2List}
+                                                        value={showField}
+                                                        onChange={(value) => {
+                                                            if (value.length > 6) {
+                                                                message.warn('最多只能选择6个指标')
+                                                                return
+                                                            }
+                                                            if (value.length < 1) {
+                                                                message.warn('最少选择1个指标')
+                                                                return
+                                                            }
+                                                            setSortData({ ...sortData, sortField: undefined })
+                                                            setShowField(value as any)
+                                                            addOnlyData.run({ data: JSON.stringify({ showField: value, sortData: { ...sortData, sortField: undefined } }), type: 'MODE_FIELD_ENT_COMPONENTS' })
+                                                        }}
+                                                    />
+                                                </Form.Item>
+                                                <Form.Item label={<strong>排序</strong>}>
+                                                    <Space>
+                                                        <Select
+                                                            style={{ width: 125 }}
+                                                            placeholder="选择排序指标"
+                                                            showSearch
+                                                            filterOption={(input, option) =>
+                                                                (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                                                            }
+                                                            options={showField2List.filter(item => showField.includes(item.value) && item.value !== 'description')}
+                                                            value={sortData.sortField}
+                                                            allowClear
+                                                            onChange={(value) => {
+                                                                setSortData({ ...sortData, sortField: value as any })
+                                                                addOnlyData.run({ data: JSON.stringify({ showField, sortData: { ...sortData, sortField: value as any } }), type: 'MODE_FIELD_ENT_COMPONENTS' })
+                                                            }}
+                                                        />
+                                                        <Radio.Group
+                                                            value={sortData.sortType}
+                                                            onChange={(e) => {
+                                                                setSortData({ ...sortData, sortType: e.target.value })
+                                                                addOnlyData.run({ data: JSON.stringify({ showField, sortData: { ...sortData, sortType: e.target.value } }), type: 'MODE_FIELD_ENT_COMPONENTS' })
+                                                            }}
+                                                            buttonStyle="solid"
+                                                        >
+                                                            <Radio.Button value={true}>正序</Radio.Button>
+                                                            <Radio.Button value={false}>倒序</Radio.Button>
+                                                        </Radio.Group>
+                                                    </Space>
+                                                </Form.Item>
+                                            </Form>
+                                        </div>}
+                                        trigger={['click']}
+                                        title={<strong>自定义展示指标与排序</strong>}
+                                        placement="bottomRight"
+                                    >
+                                        <Button icon={<SortAscendingOutlined />}>自定义展示指标与排序</Button>
+                                    </Popover>
                                 </div>
                             </div>
                             <div className={`${style.content} content_global`}>
@@ -334,9 +426,19 @@ const SelectComponentsUnit: React.FC<Props> = ({ num: count, defaultParams, chec
                                                             </div>
                                                             <Divider style={{ margin: '0 0 4px 0' }} />
                                                             <div style={{ padding: '0 10px 6px' }}>
-                                                                <Paragraph style={{ fontSize: 12, marginBottom: 1 }}>创建时间:{moment.unix(item?.createdTime).format('YYYY-MM-DD')}</Paragraph>
-                                                                <Paragraph style={{ fontSize: 12, marginBottom: 1 }}>组件ID:{item?.componentId}</Paragraph>
-                                                                <Paragraph style={{ fontSize: 12, marginBottom: 1 }}>来源:{COMPONENT_GENERATION_TYPE_ENUM['COMPONENT_GENERATION_TYPE_' + item.generationType as keyof typeof COMPONENT_GENERATION_TYPE_ENUM] || '--'}</Paragraph>
+                                                                {showField?.map(key => {
+                                                                    const name = showField2List.find(item => item.value === key)?.label
+                                                                    if (key === 'generationType') {
+                                                                        <Paragraph style={{ fontSize: 12, marginBottom: 1 }}>来源:{COMPONENT_GENERATION_TYPE_ENUM['COMPONENT_GENERATION_TYPE_' + item.generationType as keyof typeof COMPONENT_GENERATION_TYPE_ENUM] || '--'}</Paragraph>
+                                                                    } else if (['createdTime', 'componentId'].includes(key)) {
+                                                                        return <Paragraph style={{ fontSize: 12, marginBottom: 1 }} key={key}>{name}: {item?.[key] || '--'}</Paragraph>
+                                                                    } else if (['fromFollowByClickUv', 'bizFollowUv', 'scanFollowUserCount', 'orderByClickCount'].includes(key)) {
+                                                                        return <div style={{ fontSize: 12, marginBottom: 1, display: 'flex' }} key={key}><span>{name}:</span> <Statistic value={item?.[key] || 0} /></div>
+                                                                    } else if (['ctr', 'orderByClickRate', 'cheoutFdReward', 'cheoutTdReward', 'cheoutOwReward', 'cheoutTwReward', 'cheoutOmReward'].includes(key)) {
+                                                                        return <div style={{ fontSize: 12, marginBottom: 1, display: 'flex' }} key={key}><span>{name}:</span> <Statistic value={item?.[key] ? item?.[key] * 100 : 0} precision={2} valueStyle={item?.[key] >= 1 ? { color: 'red' } : { color: '#0f990f' }} suffix="%" /></div>
+                                                                    }
+                                                                    return <div style={{ fontSize: 12, marginBottom: 1, display: 'flex' }} key={key}><span>{name}:</span> <Statistic value={item?.[key] || 0} precision={2} /></div>
+                                                                })}
                                                             </div>
                                                         </Card>
                                                     </div>

+ 3 - 0
src/pages/launchSystemV3/material/cloudNew/selectComponentsUnitSearch.tsx

@@ -88,6 +88,9 @@ const SelectComponentsUnitSearch: React.FC<Props> = ({ type, defaultParams, acco
             onFinish={handleOk}
         >
             <Row gutter={[0, 6]}>
+                <Col><Form.Item name={'componentCustomName'}>
+                    <Input style={{ width: 100 }} allowClear placeholder="组件名称" />
+                </Form.Item></Col>
                 <Col><Form.Item name={'idList'}>
                     <Input
                         style={{ width: 220 }}

+ 86 - 0
src/pages/launchSystemV3/material/cloudNew/syncCloudComponent.tsx

@@ -0,0 +1,86 @@
+import { useAjax } from "@/Hook/useAjax"
+import { syncComponentApi } from "@/services/adqV3/global"
+import { DatePicker, Form, message, Modal } from "antd"
+import { RangePickerProps } from "antd/lib/date-picker"
+import React, { useState } from "react"
+import moment from "moment"
+import '../../tencentAdPutIn/index.less'
+
+/**
+ * 同步组件
+ * @returns 
+ */
+const SyncCloudComponent: React.FC<{ onChange?: () => void, accountId: number }> = ({ onChange, accountId }) => {
+
+    /********************************************/
+    const [form] = Form.useForm()
+
+    const syncComponent = useAjax((params) => syncComponentApi(params))
+    const [visible, setVisible] = useState<boolean>(false)
+    /********************************************/
+
+    const handleOk = () => {
+        form.validateFields().then(valid => {
+            const { createTime, ...params } = valid
+            syncComponent.run({ ...params, accountIds: [accountId], startTime: moment(createTime[0]).format('YYYY-MM-DD'), endTime: moment(createTime[1]).format('YYYY-MM-DD') }).then(res => {
+                if (res) {
+                    message.success('同步成功')
+                    onChange?.()
+                    setVisible(false)
+                }
+            })
+        })
+    }
+
+    const disabledDate: RangePickerProps['disabledDate'] = current => {
+        // 禁止选择当前日期之前的日期  
+        return current && current > moment().endOf('day');
+    };
+
+    return <>
+        <a onClick={() => setVisible(true)}>同步组件</a>
+        {visible && <Modal
+            title={<strong>同步组件</strong>}
+            open={visible}
+            onCancel={() => {
+                setVisible(false)
+            }}
+            onOk={handleOk}
+            className="modalResetCss"
+            confirmLoading={syncComponent.loading}
+            okText={'同步'}
+        >
+            <Form
+                name="basicSyncCloudSc"
+                form={form}
+                layout='vertical'
+                autoComplete="off"
+                initialValues={{ type: 'image', createTime: [moment(), moment()] }}
+            >
+                <Form.Item
+                    label={<strong>创建时间</strong>}
+                    name="createTime"
+                    rules={[
+                        { required: true, message: '请选择创建时间' },
+                        () => ({
+                            validator(_, value) {
+                                if (value) {
+                                    const [startDate, endDate] = value;
+                                    // 检查选择的日期范围是否超过30天  
+                                    if (endDate.diff(startDate, 'days') >= 30) {
+                                        return Promise.reject(new Error('选择的日期范围不能超过30天!'));
+                                    }
+                                }
+                                return Promise.resolve();
+                            },
+                        })
+                    ]}
+                >
+                    <DatePicker.RangePicker disabledDate={disabledDate} />
+                </Form.Item>
+            </Form>
+        </Modal>}
+    </>
+}
+
+export default React.memo(SyncCloudComponent)

+ 1 - 1
src/pages/launchSystemV3/material/tencent/index.tsx

@@ -316,7 +316,7 @@ const Tencent: React.FC = () => {
             totalData={totalData}
             config={columns12()}
             fixed={{ left: 1, right: 0 }}
-            scroll={{ x: 1000, y: size1.height ? size1.height - 270 : 580 }}
+            scroll={{ x: 1000, y: size1.height ? size1.height - 300 : 580 }}
             configName='云端素材'
             loading={getRemoteMaterialList.loading}
             ajax={getRemoteMaterialList}

+ 1 - 0
src/pages/launchSystemV3/tencenTasset/manageComponent/const.ts

@@ -602,6 +602,7 @@ export const COMPONENT_SUB_TYPE = [
  * @returns 
  */
 export const getComponentType = (componentSubType: string) => {
+    if (!componentSubType) return 'OTHER'
     if (['IMAGE_16X9', 'IMAGE_1X1', 'IMAGE_9X16', 'IMAGE_20X7', 'IMAGE_3X4', 'IMAGE_4X3', 'IMAGE_3X2', 'IMAGE_7X2', 'IMAGE_5X4', 'IMAGE_4X5'].includes(componentSubType)) {
         return 'IMAGE'
     } else if (componentSubType.includes('VIDEO')) {

+ 0 - 2
src/pages/launchSystemV3/tencenTasset/manageComponent/index.less

@@ -30,8 +30,6 @@
     }
 
     .manageComponent-header {
-        padding: 10px 16px;
-        border-bottom: 1px solid #e8e8e8;
         display: flex;
         gap: 8px;
         align-items: center;

+ 159 - 104
src/pages/launchSystemV3/tencenTasset/manageComponent/index.tsx

@@ -1,7 +1,7 @@
 import React, { useEffect, useRef, useState } from "react"
 import '../../tencentAdPutIn/index.less'
-import { Button, Card, DatePicker, Input, message, Pagination, Popconfirm, Select, Space, Switch, Table, Tabs } from "antd"
-import { delComponentApi, getCreativeComponentListApi, GetCreativeComponentProps, getDefaultSharingApi, updateDefaultSharingApi } from "@/services/adqV3/global"
+import { Button, Card, DatePicker, Input, message, Pagination, Popconfirm, Select, Space, Switch, Tabs } from "antd"
+import { delComponentApi, getCreativeComponentDataListApi, GetCreativeComponentProps, getDefaultSharingApi, updateDefaultSharingApi } from "@/services/adqV3/global"
 import './index.less'
 import { DeleteOutlined, PlusOutlined, SyncOutlined } from "@ant-design/icons"
 import { useAjax } from "@/Hook/useAjax"
@@ -9,9 +9,10 @@ import SelectAdAccount from "@/components/SelectAdAccount"
 import { COMMON_POTENTIAL_STATUS_ENUM, COMPONENT_GENERATION_TYPE_ENUM, DEFAULT_COMPONENT_SUB_IMAGE_TYPE, DEFAULT_COMPONENT_SUB_SHOW_IMAGE, DEFAULT_COMPONENT_SUB_SHOW_VIDEO, DEFAULT_COMPONENT_SUB_VIDEO_TYPE } from "./const"
 import moment from "moment"
 import { useDebounce, useSize } from "ahooks"
-import TableConfig from "./tableConfig"
 import AddComponents from "./addComponents"
 import { DefaultOptionType } from "antd/lib/select"
+import TablePro from "@/components/TablePro"
+import columns12 from "./tableConfig"
 
 /**
  * 创意组件
@@ -27,6 +28,9 @@ const ManageComponent: React.FC = () => {
     const debouncedComponentIdStingSting = useDebounce(componentIdSting, { wait: 500 });
     const ref = useRef<HTMLDivElement>(null);
     const size = useSize(ref);
+    const ref1 = useRef<HTMLDivElement>(null);
+    const size1 = useSize(ref1);
+    const [tableHeaderHeight, setTableHeaderHeight] = useState<number>(0)
     const [addVisible, setAddVisible] = useState<boolean>(false)
     const [loading, setLoading] = useState<boolean>(true)
     const [adUnitAccount, setAdUnitAccount] = useState<number>()
@@ -34,7 +38,7 @@ const ManageComponent: React.FC = () => {
     const [putInType, setPutInType] = useState<'NOVEL' | 'GAME'>('NOVEL')
     const [selectedRows, setSelectedRows] = useState<any[]>([])
 
-    const getCreativeComponentList = useAjax((params) => getCreativeComponentListApi(params))
+    const getCreativeComponentList = useAjax((params) => getCreativeComponentDataListApi(params))
     const getDefaultSharing = useAjax((params) => getDefaultSharingApi(params))
     const updateDefaultSharing = useAjax((params) => updateDefaultSharingApi(params))
     const delComponent = useAjax((params) => delComponentApi(params))
@@ -57,6 +61,29 @@ const ManageComponent: React.FC = () => {
         }
     }, [queryParams, debouncedIdSting, debouncedComponentIdStingSting])
 
+    useEffect(() => {
+        // 1. 通过选择器获取目标元素
+        const element = document.querySelector('.ant-table-header');
+        if (!element) return;
+
+        // 2. 创建 ResizeObserver
+        const observer = new ResizeObserver((entries) => {
+            for (const entry of entries) { // 遍历 entries 数组
+                const observedHeight = entry.contentRect.height; // 提取 contentRect
+                setTableHeaderHeight(observedHeight)
+            }
+        });
+
+        // 3. 开始观察
+        observer.observe(element);
+
+        // 4. 清理函数
+        return () => {
+            observer.unobserve(element);
+            observer.disconnect();
+        };
+    }, [])
+
     const handleGet = (adAccountId: number) => {
         getDefaultSharing.run({ adAccountId }).then(res => {
             if (res) {
@@ -75,9 +102,9 @@ const ManageComponent: React.FC = () => {
         })
     }
 
-    const handleDel = (id: number[]) => {
+    const handleDel = (id: number[], accountId: number) => {
         const hide = message.loading('正在删除...', 0)
-        delComponent.run({ componentId: id, adAccountId: queryParams?.adAccountId }).then(res => {
+        delComponent.run({ componentId: id, adAccountId: accountId }).then(res => {
             hide()
             if (res?.length === 0) {
                 message.success('删除成功')
@@ -133,7 +160,7 @@ const ManageComponent: React.FC = () => {
                 <Button icon={<SyncOutlined />} type="link" onClick={() => { getCreativeComponentList.refresh() }} loading={getCreativeComponentList.loading}></Button>
                 <Popconfirm
                     title="确定删除?"
-                    onConfirm={() => { handleDel(selectedRows.map(item => item.componentId)) }}
+                    onConfirm={() => { handleDel(selectedRows.map(item => item.componentId), queryParams?.adAccountId as number) }}
                     disabled={selectedRows.length === 0}
                 >
                     <Button icon={<DeleteOutlined />} type="primary" danger size="small" disabled={selectedRows.length === 0} loading={delComponent.loading}>删除</Button>
@@ -160,103 +187,131 @@ const ManageComponent: React.FC = () => {
                 padding: 0
             }}
         >
-            <div className="manageComponent-header">
-                <Button icon={<PlusOutlined />} type="primary" onClick={() => { setAddVisible(true) }}>新建组件</Button>
-                <Input.TextArea
-                    style={{ width: 160 }}
-                    value={idSting}
-                    rows={1}
-                    placeholder="素材ID(多个,,空格换行)"
-                    allowClear
-                    onChange={(e) => setIdSting(e.target.value)}
-                />
-                <Input.TextArea
-                    style={{ width: 160 }}
-                    value={componentIdSting}
-                    rows={1}
-                    placeholder="组件ID(多个,,空格换行)"
-                    allowClear
-                    onChange={(e) => setComponentIdSting(e.target.value)}
-                />
-                <Select
-                    showSearch
-                    placeholder="二级组件类型"
-                    filterOption={(input, option) =>
-                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                    }
-                    style={{ minWidth: 120 }}
-                    maxTagCount={1}
-                    mode="multiple"
-                    allowClear
-                    value={queryParams?.componentSubType}
-                    onChange={(e) => {
-                        setQueryParams({ ...queryParams, componentSubType: e, pageNum: 1 })
-                    }}
-                    options={(queryParams?.activeKey === 'IMAGE' ? DEFAULT_COMPONENT_SUB_IMAGE_TYPE : DEFAULT_COMPONENT_SUB_VIDEO_TYPE) as DefaultOptionType[]}
-                />
-                <Select
-                    showSearch
-                    placeholder="已删除?"
-                    filterOption={(input, option) =>
-                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                    }
-                    style={{ width: 100 }}
-                    value={queryParams?.isDeleted}
-                    allowClear
-                    onChange={(e) => {
-                        setQueryParams({ ...queryParams, isDeleted: e, pageNum: 1 })
-                    }}
-                    options={[
-                        { label: '已删除', value: true },
-                        { label: '未删除', value: false }
-                    ]}
-                />
-                <Select
-                    showSearch
-                    placeholder="组件潜力"
-                    filterOption={(input, option) =>
-                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                    }
-                    style={{ minWidth: 100 }}
-                    maxTagCount={1}
-                    mode="multiple"
-                    value={queryParams?.potentialStatus}
-                    onChange={(e) => {
-                        setQueryParams({ ...queryParams, potentialStatus: e, pageNum: 1 })
-                    }}
-                    options={Object.keys(COMMON_POTENTIAL_STATUS_ENUM).map(key => ({ label: COMMON_POTENTIAL_STATUS_ENUM[key as keyof typeof COMMON_POTENTIAL_STATUS_ENUM], value: key }))}
-                />
-                <Select
-                    showSearch
-                    placeholder="来源"
-                    filterOption={(input, option) =>
-                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                    }
-                    style={{ minWidth: 100 }}
-                    maxTagCount={1}
-                    mode="multiple"
-                    value={queryParams?.generationType}
-                    onChange={(e) => {
-                        setQueryParams({ ...queryParams, generationType: e, pageNum: 1 })
-                    }}
-                    options={Object.keys(COMPONENT_GENERATION_TYPE_ENUM).map(key => ({ label: COMPONENT_GENERATION_TYPE_ENUM[key as keyof typeof COMPONENT_GENERATION_TYPE_ENUM], value: key }))}
-                />
-                <DatePicker.RangePicker
-                    placeholder={['创建时间开始', '创建时间结束']}
-                    value={queryParams?.createTimeMin && queryParams?.createTimeMax ? [moment(queryParams?.createTimeMin), moment(queryParams?.createTimeMax)] as any : undefined}
-                    onChange={(_, option) => setQueryParams({ ...queryParams, createTimeMin: option[0], createTimeMax: option[1], pageNum: 1 })}
-                />
-            </div>
+
             <div className="manageComponent-content" ref={ref}>
-                <Table
-                    columns={TableConfig(queryParams?.activeKey as any, handleDel)}
-                    dataSource={getCreativeComponentList.data?.records || []}
+                <TablePro
+                    leftChild={<div className="manageComponent-header" ref={ref1}>
+                        <Button icon={<PlusOutlined />} type="primary" onClick={() => { setAddVisible(true) }}>新建组件</Button>
+                        <Input
+                            style={{ width: 100 }}
+                            value={queryParams?.componentCustomName}
+                            placeholder="组件名称"
+                            allowClear
+                            onChange={(e) => setQueryParams({ ...queryParams, componentCustomName: e.target.value, pageNum: 1 })}
+                        />
+                        <Input.TextArea
+                            style={{ width: 160 }}
+                            value={idSting}
+                            rows={1}
+                            placeholder="素材ID(多个,,空格换行)"
+                            allowClear
+                            onChange={(e) => setIdSting(e.target.value)}
+                        />
+                        <Input.TextArea
+                            style={{ width: 160 }}
+                            value={componentIdSting}
+                            rows={1}
+                            placeholder="组件ID(多个,,空格换行)"
+                            allowClear
+                            onChange={(e) => setComponentIdSting(e.target.value)}
+                        />
+                        <Select
+                            showSearch
+                            placeholder="二级组件类型"
+                            filterOption={(input, option) =>
+                                ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                            }
+                            style={{ minWidth: 120 }}
+                            maxTagCount={1}
+                            mode="multiple"
+                            allowClear
+                            value={queryParams?.componentSubType}
+                            onChange={(e) => {
+                                setQueryParams({ ...queryParams, componentSubType: e, pageNum: 1 })
+                            }}
+                            options={(queryParams?.activeKey === 'IMAGE' ? DEFAULT_COMPONENT_SUB_IMAGE_TYPE : DEFAULT_COMPONENT_SUB_VIDEO_TYPE) as DefaultOptionType[]}
+                        />
+                        <Select
+                            showSearch
+                            placeholder="已删除?"
+                            filterOption={(input, option) =>
+                                ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                            }
+                            style={{ width: 100 }}
+                            value={queryParams?.isDeleted}
+                            allowClear
+                            onChange={(e) => {
+                                setQueryParams({ ...queryParams, isDeleted: e, pageNum: 1 })
+                            }}
+                            options={[
+                                { label: '已删除', value: true },
+                                { label: '未删除', value: false }
+                            ]}
+                        />
+                        <Select
+                            showSearch
+                            placeholder="组件潜力"
+                            filterOption={(input, option) =>
+                                ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                            }
+                            style={{ minWidth: 100 }}
+                            maxTagCount={1}
+                            mode="multiple"
+                            value={queryParams?.potentialStatus}
+                            onChange={(e) => {
+                                setQueryParams({ ...queryParams, potentialStatus: e, pageNum: 1 })
+                            }}
+                            options={Object.keys(COMMON_POTENTIAL_STATUS_ENUM).map(key => ({ label: COMMON_POTENTIAL_STATUS_ENUM[key as keyof typeof COMMON_POTENTIAL_STATUS_ENUM], value: key }))}
+                        />
+                        <Select
+                            showSearch
+                            placeholder="来源"
+                            filterOption={(input, option) =>
+                                ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                            }
+                            style={{ minWidth: 75 }}
+                            maxTagCount={1}
+                            mode="multiple"
+                            value={queryParams?.generationType}
+                            onChange={(e) => {
+                                setQueryParams({ ...queryParams, generationType: e, pageNum: 1 })
+                            }}
+                            options={Object.keys(COMPONENT_GENERATION_TYPE_ENUM).map(key => ({ label: COMPONENT_GENERATION_TYPE_ENUM[key as keyof typeof COMPONENT_GENERATION_TYPE_ENUM], value: key }))}
+                        />
+                        <DatePicker.RangePicker
+                            placeholder={['创建时间开始', '创建时间结束']}
+                            style={{ width: 250 }}
+                            value={queryParams?.createTimeMin && queryParams?.createTimeMax ? [moment(queryParams?.createTimeMin), moment(queryParams?.createTimeMax)] as any : undefined}
+                            onChange={(_, option) => setQueryParams({ ...queryParams, createTimeMin: option[0], createTimeMax: option[1], pageNum: 1 })}
+                        />
+                    </div>}
+                    refreshAjax={() => getCreativeComponentList.refresh()}
+                    rowKey="componentId"
+                    config={columns12(handleDel)}
+                    fixed={{ left: 1, right: 0 }}
+                    scroll={{ x: 1000, y: (size?.height || 0) - ((size1?.height || 33) + (tableHeaderHeight || 45) + 20) }}
+                    configName='创意组件'
                     loading={getCreativeComponentList.loading || loading}
-                    scroll={{ x: 1000, y: (size?.height || 0) - 36 }}
+                    ajax={getCreativeComponentList}
+                    page={getCreativeComponentList?.data?.current || 1}
+                    pageSize={getCreativeComponentList?.data?.size || 20}
+                    total={getCreativeComponentList?.data?.total || 0}
+                    dataSource={getCreativeComponentList.data?.records || []}
                     pagination={false}
-                    size="small"
-                    bordered
-                    rowKey="componentId"
+                    onChange={(pagination: any, _: any, sortData: any) => {
+                        let { current, pageSize } = pagination
+                        let newQueryForm = JSON.parse(JSON.stringify(queryParams))
+                        if (sortData && sortData?.order) {
+                            newQueryForm['sortAsc'] = sortData?.order === 'ascend' ? true : false
+                            newQueryForm['sortColumn'] = sortData?.field
+                        } else {
+                            delete newQueryForm['sortAsc']
+                            delete newQueryForm['sortColumn']
+                        }
+                        newQueryForm.pageNum = current || newQueryForm.pageNum
+                        newQueryForm.pageSize = pageSize || newQueryForm.pageSize
+                        setQueryParams({ ...newQueryForm })
+                    }}
                     rowSelection={{
                         selectedRowKeys: selectedRows.map(item => item.componentId),
                         getCheckboxProps: (record: any) => ({
@@ -300,11 +355,11 @@ const ManageComponent: React.FC = () => {
             <div className="manageComponent-footer">
                 <Pagination
                     size="small"
-                    total={getCreativeComponentList?.data?.total || 0}
+                    total={getCreativeComponentList?.data?.totalRow || 0}
                     showSizeChanger
                     showQuickJumper
-                    pageSize={getCreativeComponentList?.data?.size || 30}
-                    current={getCreativeComponentList?.data?.current || 1}
+                    pageSize={getCreativeComponentList?.data?.pageSize || 30}
+                    current={getCreativeComponentList?.data?.pageNumber || 1}
                     onChange={(page: number, pageSize: number) => {
                         ref.current?.querySelector('.ant-table-body')?.scrollTo({ top: 0 })
                         setTimeout(() => setQueryParams({ ...queryParams, pageNum: page, pageSize }), 50)

+ 255 - 150
src/pages/launchSystemV3/tencenTasset/manageComponent/tableConfig.tsx

@@ -1,162 +1,267 @@
-import { ColumnsType } from "antd/es/table";
 import React from "react";
 import { COMPONENT_GENERATION_TYPE_ENUM, COMPONENT_SUB_TYPE, getComponentType } from "./const";
-import { Popconfirm, Tag } from "antd";
-import moment from "moment";
+import { Popconfirm, Statistic, Tag } from "antd";
 import Image1X1 from "../../components/AdsComponent/Image1X1";
 import ImageXXX from "../../components/AdsComponent/ImageXXX";
 
-const TableConfig = (activeKey: 'IMAGE' | 'VIDEO', del: (id: number[]) => void): ColumnsType<any> => {
 
-    const columns: ColumnsType<any> = [
-        {
-            title: activeKey === 'IMAGE' ? '图片' : '视频' + '组件',
-            dataIndex: 'componentSubType',
-            key: 'componentSubType',
-            width: 380,
-            fixed: 'left',
-            render(value, records) {
-                const type = getComponentType(value)
-                if (type === 'IMAGE') {
-                    return <Image1X1 imageUrl={records?.componentValue?.image?.value?.imageUrl} />
-                } else if (type === 'IMAGE_LIST') {
-                    return <ImageXXX imageList={records?.componentValue?.imageList?.value?.list || []} />
-                } else if (type === 'VIDEO') {
-                    return <Image1X1
-                        imageUrl={records?.componentValue?.video?.value?.coverUrl || records?.componentValue?.shortVideo?.value?.shortVideo2Url}
-                        videoUrl={records?.componentValue?.video?.value?.videoUrl || records?.componentValue?.shortVideo?.value?.shortVideo1Url}
-                    />
-                }
-                return <div className='image-9x16'>联系技术添加</div>
-            },
-        },
-        {
-            title: '二级组件类型',
-            dataIndex: 'componentSubType',
-            key: 'componentSubType',
-            width: 100,
-            ellipsis: true,
-            align: 'center',
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{COMPONENT_SUB_TYPE.find(item => item.value === value)?.label || '--'}</span>
-            },
-        },
-        {
-            title: '组件名称',
-            dataIndex: 'componentCustomName',
-            key: 'componentCustomName',
-            width: 180,
-            ellipsis: true,
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{value}</span>
-            },
-        },
-        {
-            title: '组件ID',
-            dataIndex: 'componentId',
-            key: 'componentId',
-            width: 90,
-            ellipsis: true,
-            align: 'center',
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{value}</span>
-            },
-        },
-        {
-            title: '来源',
-            dataIndex: 'generationType',
-            key: 'generationType',
-            width: 90,
-            ellipsis: true,
-            align: 'center',
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{COMPONENT_GENERATION_TYPE_ENUM['COMPONENT_GENERATION_TYPE_' + value as keyof typeof COMPONENT_GENERATION_TYPE_ENUM] || '--'}</span>
-            },
-        },
-        {
-            title: '广告账号',
-            dataIndex: 'accountId',
-            key: 'accountId',
-            width: 90,
-            ellipsis: true,
-            align: 'center',
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{value || '--'}</span>
-            },
-        },
-        {
-            title: '业务单元ID',
-            dataIndex: 'organizationId',
-            key: 'organizationId',
-            width: 90,
-            ellipsis: true,
-            align: 'center',
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{value || '--'}</span>
-            },
-        },
-        {
-            title: '创建时间',
-            dataIndex: 'createdTime',
-            key: 'createdTime',
-            width: 95,
-            ellipsis: true,
-            align: 'center',
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{value ? moment.unix(value).format('YYYY-MM-DD') : '--'}</span>
-            },
-        },
-        {
-            title: '最后修改时间',
-            dataIndex: 'lastModifiedTime',
-            key: 'lastModifiedTime',
-            width: 95,
-            ellipsis: true,
-            align: 'center',
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{value ? moment.unix(value).format('YYYY-MM-DD') : '--'}</span>
-            },
-        },
-        {
-            title: '是否删除',
-            dataIndex: 'isDeleted',
-            key: 'isDeleted',
-            width: 70,
-            ellipsis: true,
-            align: 'center',
-            render(value) {
-                return value ? <Tag color="error" style={{ margin: 0 }}>是</Tag> : <Tag color="success" style={{ margin: 0 }}>否</Tag>
-            },
-        },
+
+function columns12(del: (id: number[], accountId: number) => void): { label: string, fieldSHow?: { label: string, saveField: string, defaultValue: any[], data: any[] }, data: any[] }[] {
+
+    return [
         {
-            title: '组件状态',
-            dataIndex: 'status',
-            key: 'status',
-            width: 90,
-            ellipsis: true,
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{value || '--'}</span>
-            },
+            label: '基础字段',
+            data: [
+                {
+                    title: '组件详情',
+                    dataIndex: 'componentSubType1',
+                    key: 'componentSubType1',
+                    width: 380,
+                    fixed: 'left',
+                    label: '基础字段',
+                    default: 1,
+                    render(_: any, records: any) {
+                        const type = getComponentType(records?.componentSubType)
+                        if (type === 'IMAGE') {
+                            return <Image1X1 imageUrl={records?.componentValue?.image?.value?.imageUrl} />
+                        } else if (type === 'IMAGE_LIST') {
+                            return <ImageXXX imageList={records?.componentValue?.imageList?.value?.list || []} />
+                        } else if (type === 'VIDEO') {
+                            return <Image1X1
+                                imageUrl={records?.componentValue?.video?.value?.coverUrl || records?.componentValue?.shortVideo?.value?.shortVideo2Url}
+                                videoUrl={records?.componentValue?.video?.value?.videoUrl || records?.componentValue?.shortVideo?.value?.shortVideo1Url}
+                            />
+                        }
+                        return <div className='image-9x16'>联系技术添加</div>
+                    },
+                },
+                {
+                    title: '二级组件类型',
+                    dataIndex: 'componentSubType',
+                    key: 'componentSubType',
+                    width: 75,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 2,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{COMPONENT_SUB_TYPE.find(item => item.value === value)?.label || '--'}</span>
+                    },
+                },
+                {
+                    title: '组件名称',
+                    dataIndex: 'componentCustomName',
+                    key: 'componentCustomName',
+                    width: 180,
+                    ellipsis: true,
+                    label: '基础字段',
+                    default: 3,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{value}</span>
+                    },
+                },
+                {
+                    title: '组件ID',
+                    dataIndex: 'componentId',
+                    key: 'componentId',
+                    width: 90,
+                    ellipsis: true,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 4,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{value}</span>
+                    },
+                },
+                {
+                    title: '来源',
+                    dataIndex: 'generationType',
+                    key: 'generationType',
+                    width: 90,
+                    ellipsis: true,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 5,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{COMPONENT_GENERATION_TYPE_ENUM['COMPONENT_GENERATION_TYPE_' + value as keyof typeof COMPONENT_GENERATION_TYPE_ENUM] || '--'}</span>
+                    },
+                },
+                {
+                    title: '广告账号',
+                    dataIndex: 'accountId',
+                    key: 'accountId',
+                    width: 90,
+                    ellipsis: true,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 6,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '业务单元ID',
+                    dataIndex: 'organizationId',
+                    key: 'organizationId',
+                    width: 90,
+                    ellipsis: true,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 7,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '创建时间',
+                    dataIndex: 'createdTime',
+                    key: 'createdTime',
+                    width: 95,
+                    ellipsis: true,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 8,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '最后修改时间',
+                    dataIndex: 'lastModifiedTime',
+                    key: 'lastModifiedTime',
+                    width: 95,
+                    ellipsis: true,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 9,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '是否删除',
+                    dataIndex: 'isDeleted',
+                    key: 'isDeleted',
+                    width: 70,
+                    ellipsis: true,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 10,
+                    render(value: any) {
+                        return value ? <Tag color="error" style={{ margin: 0 }}>是</Tag> : <Tag color="success" style={{ margin: 0 }}>否</Tag>
+                    },
+                },
+                {
+                    title: '组件状态',
+                    dataIndex: 'status',
+                    key: 'status',
+                    width: 70,
+                    align: 'center',
+                    label: '基础字段',
+                    default: 11,
+                    render(value: any) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '操作',
+                    dataIndex: 'cz',
+                    key: 'cz',
+                    width: 60,
+                    label: '基础字段',
+                    align: 'center',
+                    default: 12,
+                    render(_: any, record: any) {
+                        if (!record?.isDeleted) {
+                            return <Popconfirm
+                                title="确定删除?"
+                                onConfirm={() => { del([record?.componentId], record?.accountId) }}
+                            >
+                                <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                            </Popconfirm>
+                        }
+                        return '--'
+                    },
+                },
+            ]
         },
         {
-            title: '操作',
-            dataIndex: 'cz',
-            key: 'cz',
-            width: 130,
-            render(_, record) {
-                if (!record?.isDeleted) {
-                    return <Popconfirm
-                        title="确定删除?"
-                        onConfirm={() => { del([record?.componentId]) }}
-                    >
-                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
-                    </Popconfirm>
-                }
-                return '--'
-            },
-        },
+            label: '数据指标',
+            data: [
+                {
+                    title: '消耗', dataIndex: 'cost', label: '数据指标', width: 70, default: 13, align: 'right', sorter: true,
+                    render: (a: number) => <Statistic value={a || 0} precision={2} />
+                },
+                {
+                    title: '一键起量消耗', dataIndex: 'acquisitionCost', label: '数据指标', width: 70, default: 14, align: 'center', sorter: true,
+                    render: (a: number) => <Statistic value={a || 0} precision={2} />
+                },
+                {
+                    title: '千次展现均价', dataIndex: 'thousandDisplayPrice', label: '数据指标', width: 70, align: 'center', sorter: true, default: 15,
+                    render: (a: number) => <Statistic value={a || 0} precision={2} />
+                },
+                {
+                    title: '点击率', dataIndex: 'ctr', label: '数据指标', width: 70, align: 'center', sorter: true, default: 16,
+                    render: (a: number) => <Statistic value={a ? a * 100 : 0} precision={2} valueStyle={a >= 1 ? { color: 'red' } : { color: '#0f990f' }} suffix="%" />
+                },
+                {
+                    title: '公众号关注成本(点击归因)', dataIndex: 'fromFollowByClickCost', label: '数据指标', width: 100, align: 'center', sorter: true, default: 17,
+                    render: (a: number) => <Statistic value={a || 0} precision={2} />
+                },
+                {
+                    title: '公众号关注人数(点击归因)', dataIndex: 'fromFollowByClickUv', label: '数据指标', width: 100, align: 'center', sorter: true, default: 18,
+                    render: (a: number) => <Statistic value={a || 0} />
+                },
+                {
+                    title: '公众号关注成本(平台上报)', dataIndex: 'bizFollowCost', label: '数据指标', width: 100, align: 'center', sorter: true, default: 19,
+                    render: (a: number) => <Statistic value={a || 0} precision={2} />
+                },
+                {
+                    title: '公众号关注人数(平台上报)', dataIndex: 'bizFollowUv', label: '数据指标', width: 100, align: 'center', sorter: true, default: 20,
+                    render: (a: number) => <Statistic value={a || 0} />
+                },
+                {
+                    title: '加企业微信客服成本(人数)', dataIndex: 'scanFollowUserCost', label: '数据指标', width: 100, align: 'center', sorter: true, default: 21,
+                    render: (a: number) => <Statistic value={a || 0} precision={2} />
+                },
+                {
+                    title: '加企业微信客服人数', dataIndex: 'scanFollowUserCount', label: '数据指标', width: 90, align: 'center', sorter: true, default: 22,
+                    render: (a: number) => <Statistic value={a || 0} />
+                },
+                {
+                    title: '下单次数(点击归因)', dataIndex: 'orderByClickCount', label: '数据指标', width: 90, align: 'center', sorter: true, default: 23,
+                    render: (a: number) => <Statistic value={a || 0} />
+                },
+                {
+                    title: '下单成本(点击归因)', dataIndex: 'orderByClickCost', label: '数据指标', width: 90, align: 'center', sorter: true, default: 24,
+                    render: (a: number) => <Statistic value={a || 0} precision={2} />
+                },
+                {
+                    title: '下单率(点击归因)', dataIndex: 'orderByClickRate', label: '数据指标', width: 90, align: 'center', sorter: true, default: 25,
+                    render: (a: number) => <Statistic value={a ? a * 100 : 0} precision={2} valueStyle={a >= 1 ? { color: 'red' } : { color: '#0f990f' }} suffix="%" />
+                },
+                {
+                    title: '点击首日付费ROI', dataIndex: 'cheoutFdReward', label: '数据指标', width: 90, align: 'center', sorter: true, default: 26,
+                    render: (a: number) => <Statistic value={a ? a * 100 : 0} precision={2} valueStyle={a >= 1 ? { color: 'red' } : { color: '#0f990f' }} suffix="%" />
+                },
+                {
+                    title: '点击3日付费ROI', dataIndex: 'cheoutTdReward', label: '数据指标', width: 90, align: 'center', sorter: true, default: 27,
+                    render: (a: number) => <Statistic value={a ? a * 100 : 0} precision={2} valueStyle={a >= 1 ? { color: 'red' } : { color: '#0f990f' }} suffix="%" />
+                },
+                {
+                    title: '点击7日付费ROI', dataIndex: 'cheoutOwReward', label: '数据指标', width: 90, align: 'center', sorter: true, default: 28,
+                    render: (a: number) => <Statistic value={a ? a * 100 : 0} precision={2} valueStyle={a >= 1 ? { color: 'red' } : { color: '#0f990f' }} suffix="%" />
+                },
+                {
+                    title: '点击14日付费ROI', dataIndex: 'cheoutTwReward', label: '数据指标', width: 90, align: 'center', sorter: true, default: 29,
+                    render: (a: number) => <Statistic value={a ? a * 100 : 0} precision={2} valueStyle={a >= 1 ? { color: 'red' } : { color: '#0f990f' }} suffix="%" />
+                },
+                {
+                    title: '点击30日付费ROI', dataIndex: 'cheoutOmReward', label: '数据指标', width: 90, align: 'center', sorter: true, default: 30,
+                    render: (a: number) => <Statistic value={a ? a * 100 : 0} precision={2} valueStyle={a >= 1 ? { color: 'red' } : { color: '#0f990f' }} suffix="%" />
+                },
+            ]
+        }
     ]
-    return columns
 }
 
-export default TableConfig
+export default columns12

+ 27 - 0
src/services/adqV3/global.ts

@@ -1190,6 +1190,9 @@ export interface GetCreativeComponentProps {
     isDeleted?: boolean
     potentialStatus?: string
     activeKey?: string
+    componentCustomName?: string
+    sortColumn?: string
+    sortAsc?: boolean
 }
 
 /**
@@ -1204,6 +1207,18 @@ export async function getCreativeComponentListApi(data: GetCreativeComponentProp
     })
 }
 
+/**
+ * 分页查询创意组件 + 数据
+ * @param data 
+ * @returns 
+ */
+export async function getCreativeComponentDataListApi(data: GetCreativeComponentProps) {
+    return request(api + `/material/componentData/listOfPage`, {
+        method: 'POST',
+        data
+    })
+}
+
 export interface CreativeComponentDetailDTOS {
     materialId?: string,
     componentSubType: string
@@ -1262,4 +1277,16 @@ export async function delComponentApi({componentId, ...params}: { adAccountId: n
         params,
         data: componentId
     })
+}
+
+/**
+ * 同步组件
+ * @param param0 
+ * @returns 
+ */
+export async function syncComponentApi(data: { accountIds: number[], startTime: string, endTime: string }) {
+    return request(api + `/adq/creative/component/syncBatch`, {
+        method: 'PUT',
+        data
+    })
 }