wjx 3 days ago
parent
commit
250ad03207
52 changed files with 3931 additions and 1086 deletions
  1. 26 7
      config/routerConfig.ts
  2. 2 0
      src/app.tsx
  3. 84 0
      src/pages/launchSystemV3/monitorEWList/astraGroup.tsx
  4. 129 0
      src/pages/launchSystemV3/monitorEWList/astraGroupHourEle.tsx
  5. 151 0
      src/pages/launchSystemV3/monitorEWList/astraGroupIndexEle.tsx
  6. 85 0
      src/pages/launchSystemV3/monitorEWList/astraSupport‌.tsx
  7. 109 0
      src/pages/launchSystemV3/monitorEWList/astraSupport‌Hour.tsx
  8. 74 0
      src/pages/launchSystemV3/monitorEWList/doubleDelDetails.tsx
  9. 38 0
      src/pages/launchSystemV3/monitorEWList/index.tsx
  10. 246 0
      src/pages/launchSystemV3/monitorEWList/tableConfig.tsx
  11. 116 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/addCsGroup.tsx
  12. 66 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/groupUserEdit.tsx
  13. 290 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/groupUserInfo.tsx
  14. 169 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/index.tsx
  15. 10 14
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/settingsEnterprise.tsx
  16. 102 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/tableConfig.tsx
  17. 98 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/landingPageTieUp/addLandingPageTieUp.tsx
  18. 148 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/landingPageTieUp/index.tsx
  19. 155 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/landingPageTieUp/selectLandingPage.tsx
  20. 98 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/pageTieUp/addPageTieUp.tsx
  21. 148 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/pageTieUp/index.tsx
  22. 151 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/pageTieUp/selectPage.tsx
  23. 79 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/addTencentGroup.tsx
  24. 73 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/bandLocal.tsx
  25. 109 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/index.tsx
  26. 45 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/selectKF.tsx
  27. 172 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/settingsEnterprise.tsx
  28. 16 16
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/tableConfig.tsx
  29. 53 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/userInfo.tsx
  30. 20 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/const.tsx
  31. 0 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/index.less
  32. 7 5
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/index.tsx
  33. 0 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/nTree.less
  34. 0 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/nTree.tsx
  35. 21 38
      src/pages/launchSystemV3/tencenTasset/corpWechat/manage/index.tsx
  36. 103 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/manage/modify.tsx
  37. 27 29
      src/pages/launchSystemV3/tencenTasset/corpWechat/manage/tableConfig.tsx
  38. 0 67
      src/pages/launchSystemV3/tencenTasset/corpWechat/modify.tsx
  39. 113 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/userRotatePolicy/addUserPotatePolicy.tsx
  40. 191 0
      src/pages/launchSystemV3/tencenTasset/corpWechat/userRotatePolicy/index.tsx
  41. 0 100
      src/pages/launchSystemV3/tencenTasset/enterpriseWechat/addPageTieUp.tsx
  42. 0 77
      src/pages/launchSystemV3/tencenTasset/enterpriseWechat/addWechatTieUp.tsx
  43. 0 89
      src/pages/launchSystemV3/tencenTasset/enterpriseWechat/group.tsx
  44. 0 257
      src/pages/launchSystemV3/tencenTasset/enterpriseWechat/pageTieUp.tsx
  45. 0 120
      src/pages/launchSystemV3/tencenTasset/enterpriseWechat/selectPage.tsx
  46. 0 41
      src/pages/launchSystemV3/tencenTasset/enterpriseWechat/userInfo.tsx
  47. 0 180
      src/pages/launchSystemV3/tencenTasset/enterpriseWechat/wechatTieUp.tsx
  48. 2 2
      src/pages/launchSystemV3/tencentAdPutIn/create/Ad/adgroupsMarketingContent.tsx
  49. 1 1
      src/pages/launchSystemV3/tencentAdPutIn/create/Target/addTarget.tsx
  50. 1 1
      src/pages/launchSystemV3/tencentAdPutIn/create/tableConfig.tsx
  51. 320 42
      src/services/adqV3/global.ts
  52. 83 0
      src/services/adqV3/monitorEWList.ts

+ 26 - 7
config/routerConfig.ts

@@ -96,6 +96,12 @@ const launchSystemV3 = {
             component: './launchSystemV3/adMonitorListV3',
             access: 'adMonitorListV3',
         },
+        {
+            path: '/launchSystemV3/monitorEWList',
+            name: '企微客服组监控',
+            component: './launchSystemV3/monitorEWList',
+            access: 'monitorEWList',
+        },
         {
             path: '/launchSystemV3/adqv3',
             name: '腾讯广告',
@@ -166,7 +172,26 @@ const launchSystemV3 = {
                     name: '企业微信',
                     path: '/launchSystemV3/tencenTasset/corpWechat',
                     access: 'corpWechat',
-                    component: './launchSystemV3/tencenTasset/corpWechat',
+                    routes: [
+                        {
+                            name: '企业微信管理',
+                            path: '/launchSystemV3/tencenTasset/corpWechat/manage',
+                            access: 'manage',
+                            component: './launchSystemV3/tencenTasset/corpWechat/manage',
+                        },
+                        {
+                            name: '客服号轮换策略',
+                            path: '/launchSystemV3/tencenTasset/corpWechat/userRotatePolicy',
+                            access: 'userRotatePolicy',
+                            component: './launchSystemV3/tencenTasset/corpWechat/userRotatePolicy',
+                        },
+                        {
+                            name: '客服组',
+                            path: '/launchSystemV3/tencenTasset/corpWechat/csgroup',
+                            access: 'csgroup',
+                            component: './launchSystemV3/tencenTasset/corpWechat/csgroup',
+                        }
+                    ]
                 },
                 {
                     name: '游戏库',
@@ -197,12 +222,6 @@ const launchSystemV3 = {
                     path: '/launchSystemV3/tencenTasset/manageComponent',
                     access: 'manageComponent',
                     component: './launchSystemV3/tencenTasset/manageComponent',
-                },
-                {
-                    name: '客服管理',
-                    path: '/launchSystemV3/tencenTasset/enterpriseWechat',
-                    access: 'enterpriseWechat',
-                    component: './launchSystemV3/tencenTasset/enterpriseWechat',
                 }
             ],
         },

+ 2 - 0
src/app.tsx

@@ -15,6 +15,7 @@ import { ReactComponent as GdtSvg } from '@/assets/gdt.svg'
 import { ReactComponent as MonitorSvg } from '@/assets/monitor.svg'
 import { ReactComponent as AssetSvg } from '@/assets/asset.svg'
 import { ReactComponent as AssetLibrarySvg } from '@/assets/assetLibrary.svg'
+import { ReactComponent as WeComSvg } from '@/assets/weCom.svg'
 import versions from './utils/versions';
 
 
@@ -113,6 +114,7 @@ const IconMap = {
     user: <UserSwitchOutlined />,
     asset: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><AssetSvg /></span>,
     assetLibrary: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><AssetLibrarySvg /></span>,
+    weCom: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><WeComSvg /></span>,
 };
 //处理菜单
 const loopMenuItem = (menus: MenuDataItem[],): MenuDataItem[] => {

+ 84 - 0
src/pages/launchSystemV3/monitorEWList/astraGroup.tsx

@@ -0,0 +1,84 @@
+import { useAjax } from '@/Hook/useAjax';
+import { getLocalCsgroupDayListApi, GetLocalCsgroupDayListProps } from '@/services/adqV3/monitorEWList';
+import { Button, Card, DatePicker, Select, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import moment from 'moment';
+import { LocalCsgroupDayListTableConfig } from './tableConfig';
+import { getLocalCorpCsgroupListAllApi } from '@/services/adqV3/global';
+
+/**
+ * 客服组加粉数据
+ * @returns 
+ */
+const AstraGroup: React.FC = () => {
+
+    /*******************************************/
+    const [queryParamsNew, setQueryParamsNew] = useState<GetLocalCsgroupDayListProps>({ pageNum: 1, pageSize: 20 });
+    const [localCorpCsgroupList, setLocalCorpCsgroupList] = useState<{ label: string, value: string }[]>([]);
+
+    const getLocalCsgroupDayList = useAjax((params) => getLocalCsgroupDayListApi(params))
+    const getLocalCorpCsgroupListAll = useAjax((params) => getLocalCorpCsgroupListAllApi(params))
+    /*******************************************/
+
+    useEffect(() => {
+        getLocalCorpCsgroupListAll.run({}).then((res) => {
+            if (res && res.length > 0) {
+                setLocalCorpCsgroupList(res.map((item: { csgroupName: string; id: string; }) => ({ label: item.csgroupName, value: item.id })));
+            }
+        })
+    }, [])
+
+    useEffect(() => {
+        getLocalCsgroupDayList.run(queryParamsNew)
+    }, [queryParamsNew])
+
+    return <Card
+        title={<div style={{ display: 'flex', gap: 8 }}>
+            <DatePicker
+                value={queryParamsNew?.day ? moment(queryParamsNew.day) : undefined}
+                onChange={(_, dateString) => {
+                    setQueryParamsNew({ ...queryParamsNew, day: dateString, pageNum: 1 })
+                }}
+            />
+            <Select
+                showSearch
+                placeholder="请选择本地客服组"
+                filterOption={(input, option) =>
+                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                }
+                style={{ width: 140 }}
+                allowClear
+                value={queryParamsNew?.localCsgroupId}
+                onChange={(e) => {
+                    setQueryParamsNew({ ...queryParamsNew, localCsgroupId: e, pageNum: 1 })
+                }}
+                options={localCorpCsgroupList}
+            />
+            <Button type='primary' onClick={() => getLocalCsgroupDayList.refresh()}>刷新</Button>
+        </div>}
+        headStyle={{ padding: '0 16px' }}
+        bodyStyle={{ padding: 16 }}
+    >
+        <Table
+            dataSource={getLocalCsgroupDayList?.data?.records}
+            columns={LocalCsgroupDayListTableConfig()}
+            size='small'
+            bordered
+            rowKey={'id'}
+            loading={getLocalCsgroupDayList.loading}
+            scroll={{ x: 1200 }}
+            pagination={{
+                total: getLocalCsgroupDayList.data?.total,
+                defaultPageSize: 20,
+                current: queryParamsNew.pageNum,
+                pageSize: queryParamsNew.pageSize,
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryParamsNew({ ...queryParamsNew, pageNum: current as number, pageSize: pageSize as number || 20 })
+            }}
+        />
+    </Card>
+};
+
+export default React.memo(AstraGroup);

+ 129 - 0
src/pages/launchSystemV3/monitorEWList/astraGroupHourEle.tsx

@@ -0,0 +1,129 @@
+import { useAjax } from '@/Hook/useAjax';
+import { getLocalCsgroupDayHourListApi } from '@/services/adqV3/monitorEWList';
+import { Modal, Statistic, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../tencentAdPutIn/index.less';
+
+interface AstraGroupHourProps {
+    localCsgroupName: string;
+    data: {
+        day: string;
+        localCsgroupId: number;
+    }
+}
+const AstraGroupHourEle: React.FC<AstraGroupHourProps> = ({localCsgroupName, data}) => {
+
+    /***************************************/
+    const [visible, setVisible] = useState<boolean>(false);
+
+    const getLocalCsgroupDayHourList = useAjax((params) => getLocalCsgroupDayHourListApi(params))
+    /***************************************/
+
+    useEffect(() => {
+        if (visible) {
+            getLocalCsgroupDayHourList.run(data)
+        }
+    }, [data, visible]);
+
+    return <div>
+        <a style={{ fontSize: 12 }} onClick={() => setVisible(true)}>小时数据</a>
+        {visible && <Modal
+            title={<strong>{data.day}_{localCsgroupName} 小时数据</strong>}
+            open={visible}
+            onCancel={() => setVisible(false)}
+            footer={null}
+            className='modalResetCss'
+            width={800}
+        >
+            <Table
+                dataSource={getLocalCsgroupDayHourList?.data}
+                columns={[
+                    {
+                        title: '日期',
+                        dataIndex: 'day',
+                        key: 'day',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '小时',
+                        dataIndex: 'hour',
+                        key: 'hour',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '消耗',
+                        dataIndex: 'cost',
+                        key: 'cost',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <Statistic value={value || 0} />
+                        },
+                    },
+                    {
+                        title: '加粉次数(广告)',
+                        dataIndex: 'scanFollowCount',
+                        key: 'scanFollowCount',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '加粉人数(广告)',
+                        dataIndex: 'scanFollowUserCount',
+                        key: 'scanFollowUserCount',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '加粉人数',
+                        dataIndex: 'addUserCount',
+                        key: 'addUserCount',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '数据更新时间',
+                        dataIndex: 'dataTime',
+                        key: 'dataTime',
+                        width: 140,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    }
+                ]}
+                size='small'
+                bordered
+                rowKey={'id'}
+                loading={getLocalCsgroupDayHourList.loading}
+                scroll={{ x: 750 }}
+            />
+        </Modal>}
+    </div>;
+};
+
+export default React.memo(AstraGroupHourEle);

+ 151 - 0
src/pages/launchSystemV3/monitorEWList/astraGroupIndexEle.tsx

@@ -0,0 +1,151 @@
+import { useAjax } from '@/Hook/useAjax';
+import { getLocalCsgroupDayIndexListApi } from '@/services/adqV3/monitorEWList';
+import { Modal, Statistic, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../tencentAdPutIn/index.less';
+
+interface AstraGroupHourProps {
+    localCsgroupName: string;
+    data: {
+        day: string;
+        localCsgroupId: number;
+    }
+}
+const AstraGroupIndexEle: React.FC<AstraGroupHourProps> = ({ localCsgroupName, data }) => {
+
+    /***************************************/
+    const [visible, setVisible] = useState<boolean>(false);
+    const [queryParamsNew, setQueryParamsNew] = useState<{ pageNum: number, pageSize: number }>({ pageNum: 1, pageSize: 20 });
+
+    const getLocalCsgroupDayIndexList = useAjax((params) => getLocalCsgroupDayIndexListApi(params))
+    /***************************************/
+
+    useEffect(() => {
+        if (visible) {
+            getLocalCsgroupDayIndexList.run({ ...data, ...queryParamsNew })
+        }
+    }, [data, visible, queryParamsNew]);
+
+    return <div>
+        <a style={{ fontSize: 12 }} onClick={() => setVisible(true)}>索引数据</a>
+        {visible && <Modal
+            title={<strong>{data.day}_{localCsgroupName} 索引数据</strong>}
+            open={visible}
+            onCancel={() => setVisible(false)}
+            footer={null}
+            className='modalResetCss'
+            width={1000}
+        >
+            <Table
+                dataSource={getLocalCsgroupDayIndexList?.data?.records}
+                columns={[
+                    {
+                        title: '日期',
+                        dataIndex: 'day',
+                        key: 'day',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '更新序列号',
+                        dataIndex: 'index',
+                        key: 'index',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '消耗',
+                        dataIndex: 'cost',
+                        key: 'cost',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <Statistic value={value || 0} />
+                        },
+                    },
+                    {
+                        title: '加粉次数(广告)',
+                        dataIndex: 'scanFollowCount',
+                        key: 'scanFollowCount',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '加粉人数(广告)',
+                        dataIndex: 'scanFollowUserCount',
+                        key: 'scanFollowUserCount',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '加粉人数',
+                        dataIndex: 'addUserCount',
+                        key: 'addUserCount',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '开始时间',
+                        dataIndex: 'beginTime',
+                        key: 'beginTime',
+                        width: 140,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                        },
+                    },
+                    {
+                        title: '数据更新时间',
+                        dataIndex: 'dataTime',
+                        key: 'dataTime',
+                        width: 140,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    }
+                ]}
+                size='small'
+                bordered
+                rowKey={'id'}
+                loading={getLocalCsgroupDayIndexList.loading}
+                scroll={{ x: 950 }}
+                pagination={{
+                    total: getLocalCsgroupDayIndexList.data?.total,
+                    defaultPageSize: 20,
+                    current: queryParamsNew.pageNum,
+                    pageSize: queryParamsNew.pageSize,
+                }}
+                onChange={(pagination) => {
+                    const { current, pageSize } = pagination
+                    setQueryParamsNew({ ...queryParamsNew, pageNum: current as number, pageSize: pageSize as number || 20 })
+                }}
+            />
+        </Modal>}
+    </div>;
+};
+
+export default React.memo(AstraGroupIndexEle);

+ 85 - 0
src/pages/launchSystemV3/monitorEWList/astraSupport‌.tsx

@@ -0,0 +1,85 @@
+import { useAjax } from '@/Hook/useAjax';
+import { getCorpWechatAllApi } from '@/services/adqV3/global';
+import { getCorpUserDayListApi, GetCorpUserDayListProps } from '@/services/adqV3/monitorEWList';
+import { Button, Card, DatePicker, Select, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import moment from 'moment';
+import { CorpUserDayListTableConfig } from './tableConfig';
+
+/**
+ * 客服号加粉数据
+ * @returns 
+ */
+const AstraSupport: React.FC = () => {
+
+    /*******************************************/
+    const [corpWechatList, setCorpWechatList] = useState<{ label: string, value: string }[]>([]);
+    const [queryParamsNew, setQueryParamsNew] = useState<GetCorpUserDayListProps>({ pageNum: 1, pageSize: 20 });
+
+    const getCorpWechatAll = useAjax((params) => getCorpWechatAllApi(params))
+    const getCorpUserDayList = useAjax((params) => getCorpUserDayListApi(params))
+    /*******************************************/
+
+    useEffect(() => {
+        // getCorpWechatAll.run({}).then((res) => {
+        //     if (res && res.length > 0) {
+        //         setCorpWechatList(res.map((item: { corpName: string; localCorpId: string; }) => ({ label: item.corpName, value: item.localCorpId })));
+        //     }
+        // })
+    }, [])
+
+    useEffect(() => {
+        getCorpUserDayList.run(queryParamsNew)
+    }, [queryParamsNew])
+
+    return <Card
+        title={<div style={{ display: 'flex', gap: 8 }}>
+            <DatePicker
+                value={queryParamsNew?.day ? moment(queryParamsNew.day) : undefined}
+                onChange={(_, dateString) => {
+                    setQueryParamsNew({ ...queryParamsNew, day: dateString, pageNum: 1 })
+                }}
+            />
+            <Select
+                showSearch
+                placeholder="请选择企业"
+                filterOption={(input, option) =>
+                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                }
+                style={{ width: 140 }}
+                allowClear
+                value={queryParamsNew?.corpId}
+                loading={getCorpWechatAll.loading}
+                onChange={(e) => {
+                    setQueryParamsNew({ ...queryParamsNew, corpId: e, pageNum: 1 })
+                }}
+                options={corpWechatList}
+            />
+            <Button type='primary' onClick={() => getCorpUserDayList.refresh()}>刷新</Button>
+        </div>}
+        headStyle={{ padding: '0 16px' }}
+        bodyStyle={{ padding: 16 }}
+    >
+        <Table 
+            dataSource={getCorpUserDayList?.data?.records} 
+            columns={CorpUserDayListTableConfig()}
+            size='small'
+            bordered
+            rowKey={'id'}
+            loading={getCorpUserDayList.loading}
+            scroll={{ x: 1200 }}
+            pagination={{
+                total: getCorpUserDayList.data?.total,
+                defaultPageSize: 20,
+                current: queryParamsNew.pageNum,
+                pageSize: queryParamsNew.pageSize,
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryParamsNew({ ...queryParamsNew, pageNum: current as number, pageSize: pageSize as number || 20 })
+            }}
+        />
+    </Card>
+};
+
+export default React.memo(AstraSupport);

+ 109 - 0
src/pages/launchSystemV3/monitorEWList/astraSupport‌Hour.tsx

@@ -0,0 +1,109 @@
+import { useAjax } from '@/Hook/useAjax';
+import { getCorpUserDayHourListApi } from '@/services/adqV3/monitorEWList';
+import { Modal, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../tencentAdPutIn/index.less';
+
+interface AstraSupportHourProps {
+    corpName: string;
+    corpUserName: string;
+    data: {
+        day: string;
+        corpId: string;
+        corpUserId: string;
+    }
+}
+const AstraSupportHour: React.FC<AstraSupportHourProps> = ({ corpName, corpUserName, data }) => {
+
+    /***************************************/
+    const [visible, setVisible] = useState<boolean>(false);
+
+    const getCorpUserDayHourList = useAjax((params) => getCorpUserDayHourListApi(params))
+    /***************************************/
+
+    useEffect(() => {
+        if (visible) {
+            getCorpUserDayHourList.run(data)
+        }
+    }, [data, visible]);
+
+    return <>
+        <a style={{ fontSize: 12 }} onClick={() => setVisible(true)}>小时数据</a>
+        {visible && <Modal
+            title={<strong>{data.day}_{corpName}_{corpUserName} 小时数据</strong>}
+            open={visible}
+            onCancel={() => setVisible(false)}
+            footer={null}
+            className='modalResetCss'
+            width={800}
+        >
+            <Table
+                dataSource={getCorpUserDayHourList?.data}
+                columns={[
+                    {
+                        title: '日期',
+                        dataIndex: 'day',
+                        key: 'day',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '小时',
+                        dataIndex: 'hour',
+                        key: 'hour',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '新增用户数',
+                        dataIndex: 'addUserCount',
+                        key: 'addUserCount',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '退出人数',
+                        dataIndex: 'outUserCount',
+                        key: 'outUserCount',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'right',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '数据更新时间',
+                        dataIndex: 'dataTime',
+                        key: 'dataTime',
+                        width: 140,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    }
+                ]}
+                size='small'
+                bordered
+                rowKey={'id'}
+                loading={getCorpUserDayHourList.loading}
+                scroll={{ x: 750 }}
+            />
+        </Modal>}
+    </>;
+};
+
+export default React.memo(AstraSupportHour);

+ 74 - 0
src/pages/launchSystemV3/monitorEWList/doubleDelDetails.tsx

@@ -0,0 +1,74 @@
+import { Modal, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../tencentAdPutIn/index.less';
+
+interface DoubleDelDetailsProps {
+    day: string;
+    corpName: string;
+    corpUserName: string;
+    dnOutUserCount: { [x: string]: number };
+}
+
+const DoubleDelDetails: React.FC<DoubleDelDetailsProps> = ({ day, corpName, corpUserName, dnOutUserCount }) => {
+
+    /***************************************/
+    const [visible, setVisible] = useState<boolean>(false);
+    const [data, setData] = useState<{ day: string; count: number }[]>([]);
+    /***************************************/
+
+    useEffect(() => {
+        if (visible && dnOutUserCount) {
+            setData(Object.keys(dnOutUserCount).map((key, index) => ({
+                day: key,
+                count: dnOutUserCount[key],
+                id: index + 1
+            })))
+        }
+    }, [dnOutUserCount, visible])
+
+    return <div>
+        <a style={{ fontSize: 12 }} onClick={() => setVisible(true)}>详情</a>
+        {visible && <Modal
+            title={<strong>{day}_{corpName}_{corpUserName} 双删数据</strong>}
+            open={visible}
+            onCancel={() => setVisible(false)}
+            footer={null}
+            className='modalResetCss'
+            width={550}
+        >
+            <Table
+                dataSource={data}
+                columns={[
+                    {
+                        title: '日期',
+                        dataIndex: 'day',
+                        key: 'day',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    },
+                    {
+                        title: '人数',
+                        dataIndex: 'count',
+                        key: 'count',
+                        width: 80,
+                        ellipsis: true,
+                        align: 'center',
+                        render(value) {
+                            return <span style={{ fontSize: 12 }}>{value}</span>
+                        },
+                    }
+                ]}
+                size='small'
+                bordered
+                rowKey={'id'}
+                scroll={{ x: 500 }}
+            />
+        </Modal>}
+    </div>;
+};
+
+export default React.memo(DoubleDelDetails);

+ 38 - 0
src/pages/launchSystemV3/monitorEWList/index.tsx

@@ -0,0 +1,38 @@
+import { Tabs } from 'antd';
+import React from 'react';
+import AstraSupport from './astraSupport‌';
+import AstraGroup from './astraGroup';
+
+/**
+ * 企微客服组监控
+ * @returns 
+ */
+const MonitorEWList: React.FC = () => {
+
+    const [currentTab, setCurrentTab] = React.useState<string>('hao');
+
+    return <Tabs
+        type="card"
+        activeKey={currentTab}
+        onChange={(key) => setCurrentTab(key)}
+        tabBarStyle={{ marginBottom: 2 }}
+        items={[
+            { 
+                label: '客服号加粉数据', 
+                key: 'hao', 
+                children: <>
+                    {currentTab === 'hao' && <AstraSupport />}
+                </> 
+            },
+            { 
+                label: '客服组加粉数据', 
+                key: 'zu', 
+                children: <>
+                    {currentTab === 'zu' && <AstraGroup />}
+                </>
+            }
+        ]}
+    />
+};
+
+export default MonitorEWList;

+ 246 - 0
src/pages/launchSystemV3/monitorEWList/tableConfig.tsx

@@ -0,0 +1,246 @@
+import { ColumnsType } from "antd/lib/table";
+import React from "react";
+import AstraSupportHour from "./astraSupport‌Hour";
+import { Space, Statistic } from "antd";
+import AstraGroupHourEle from "./astraGroupHourEle";
+import AstraGroupIndexEle from "./astraGroupIndexEle";
+import DoubleDelDetails from "./doubleDelDetails";
+
+/** 客服号加粉 */
+export const CorpUserDayListTableConfig = (): ColumnsType<never> => {
+
+    return [
+        {
+            title: '日期',
+            dataIndex: 'day',
+            key: 'day',
+            width: 80,
+            ellipsis: true,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '企业名称',
+            dataIndex: 'corpName',
+            key: 'corpName',
+            width: 100,
+            ellipsis: true,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '企业ID',
+            dataIndex: 'corpId',
+            key: 'corpId',
+            width: 180,
+            ellipsis: true,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '企业用户名称',
+            dataIndex: 'corpUserName',
+            key: 'corpUserName',
+            width: 100,
+            ellipsis: true,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '企业用户ID',
+            dataIndex: 'corpUserId',
+            key: 'corpUserId',
+            width: 100,
+            ellipsis: true,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '新增用户数',
+            dataIndex: 'addUserCount',
+            key: 'addUserCount',
+            width: 100,
+            align: 'right',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '退出人数',
+            dataIndex: 'outUserCount',
+            key: 'outUserCount',
+            width: 100,
+            align: 'right',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: 'dn双删详情',
+            dataIndex: 'dnOutUserCount',
+            key: 'dnOutUserCount',
+            width: 100,
+            align: 'center',
+            render(value, record: any) {
+                return value && Object.keys(value).length > 0 ? <DoubleDelDetails
+                    dnOutUserCount={value}
+                    day={record.day}
+                    corpName={record.corpName}
+                    corpUserName={record.corpUserName}
+                /> : '--'
+            },
+        },
+        {
+            title: '数据更新时间',
+            dataIndex: 'dataTime',
+            key: 'dataTime',
+            width: 140,
+            ellipsis: true,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 210,
+            ellipsis: true,
+            render(_, record: any) {
+                return <AstraSupportHour
+                    corpName={record.corpName}
+                    corpUserName={record.corpUserName}
+                    data={{
+                        corpId: record.corpId,
+                        corpUserId: record.corpUserId,
+                        day: record.day
+                    }}
+                />
+            },
+        }
+    ]
+}
+
+
+/** 客服组加粉 */
+export const LocalCsgroupDayListTableConfig = (): ColumnsType<never> => {
+
+    return [
+        {
+            title: '日期',
+            dataIndex: 'day',
+            key: 'day',
+            width: 80,
+            ellipsis: true,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '本地客服组名称',
+            dataIndex: 'localCsgroupName',
+            key: 'localCsgroupName',
+            width: 100,
+            ellipsis: true,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '本地客服组ID',
+            dataIndex: 'localCsgroupId',
+            key: 'localCsgroupId',
+            width: 75,
+            ellipsis: true,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+
+        {
+            title: '消耗',
+            dataIndex: 'cost',
+            key: 'cost',
+            width: 100,
+            align: 'right',
+            render(value) {
+                return <Statistic value={value || 0} />
+            },
+        },
+        {
+            title: '加粉次数(广告)',
+            dataIndex: 'scanFollowCount',
+            key: 'scanFollowCount',
+            width: 100,
+            align: 'right',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '加粉人数(广告)',
+            dataIndex: 'scanFollowUserCount',
+            key: 'scanFollowUserCount',
+            width: 100,
+            align: 'right',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '加粉人数',
+            dataIndex: 'addUserCount',
+            key: 'addUserCount',
+            width: 100,
+            align: 'right',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '数据更新时间',
+            dataIndex: 'dataTime',
+            key: 'dataTime',
+            width: 140,
+            ellipsis: true,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            },
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 250,
+            ellipsis: true,
+            render(_, record: any) {
+                return <Space>
+                    <AstraGroupHourEle
+                        localCsgroupName={record.localCsgroupName}
+                        data={{
+                            day: record.day,
+                            localCsgroupId: record.localCsgroupId
+                        }}
+                    />
+                    <AstraGroupIndexEle
+                        localCsgroupName={record.localCsgroupName}
+                        data={{
+                            day: record.day,
+                            localCsgroupId: record.localCsgroupId
+                        }}
+                    />
+                </Space>
+            }
+        }
+    ]
+}

+ 116 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/addCsGroup.tsx

@@ -0,0 +1,116 @@
+import React from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import { Form, Input, message, Modal, Radio, Select } from 'antd';
+import { useAjax } from '@/Hook/useAjax';
+import { addLocalCorpCsgroupApi, modifyLocalCorpCsgroupApi } from '@/services/adqV3/global';
+import { useWatch } from 'antd/lib/form/Form';
+
+interface AddCsGroupProps {
+    corpWechatList: { label: string; value: string }[];
+    userRotatePolicy: { label: string; value: string }[];
+    localCorpCsgroupList: { label: string; value: string }[];
+    visible?: boolean;
+    onClose?: () => void;
+    onChange?: () => void;
+    initialValues?: any; // 用于表单的初始值
+}
+const AddCsGroup: React.FC<AddCsGroupProps> = ({ corpWechatList, userRotatePolicy, localCorpCsgroupList, visible, onClose, onChange, initialValues }) => {
+
+    /**************************************/
+    const [form] = Form.useForm();
+    const type = useWatch('type', form)
+
+    const addLocalCorpCsgroup = useAjax((params) => addLocalCorpCsgroupApi(params))
+    const modifyLocalCorpCsgroup = useAjax((params) => modifyLocalCorpCsgroupApi(params))
+    /**************************************/
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            console.log(values)
+            if (initialValues?.id) {
+                modifyLocalCorpCsgroup.run({ ...values, id: initialValues?.id }).then(res => {
+                    if (res) {
+                        message.success('修改成功')
+                        onChange?.()
+                    }
+                })
+            } else {
+            addLocalCorpCsgroup.run(values).then((res) => {
+                if (res) {
+                    message.success('新增成功')
+                    onChange?.()
+                }
+            })
+            }
+        })
+    }
+
+    return <Modal
+        title={<strong>新增客服组</strong>}
+        open={visible}
+        onOk={handleOk}
+        onCancel={onClose}
+        className='modalResetCss'
+        confirmLoading={addLocalCorpCsgroup.loading || modifyLocalCorpCsgroup.loading}
+        width={550}
+    >
+        <Form
+            form={form}
+            name='basicAddCsGroup'
+            autoComplete="off"
+            colon={false}
+            labelCol={{ span: 5 }}
+            labelAlign='left'
+            initialValues={initialValues || { type: 1 }} // 默认类型为客服组
+        >
+            {!initialValues?.id && <Form.Item label={<strong>类型</strong>} name="type" rules={[{ required: true, message: '请选择类型!' }]}>
+                <Radio.Group
+                    buttonStyle="solid"
+                    optionType='button'
+                    options={[
+                        { value: 1, label: '客服组' },
+                        { value: 0, label: '客服组集' }
+                    ]}
+                />
+            </Form.Item>}
+            <Form.Item label={<strong>客服组{type == 1 ? '' : '集'}名称</strong>} name="csgroupName" rules={[{ required: true, message: `请输入客服组${type == 1 ? '' : '集'}名称!` }]}>
+                <Input placeholder={`请输入客服组${type == 1 ? '' : '集'}名称`} />
+            </Form.Item>
+            <Form.Item label={<strong>腾讯企业</strong>} name="tencentCorpId" rules={[{ required: true, message: '请选择腾讯企业!' }]}>
+                <Select
+                    showSearch
+                    placeholder="请选择企业"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    allowClear
+                    options={corpWechatList}
+                />
+            </Form.Item>
+            {type == 1 && <Form.Item label={<strong>客服组集</strong>} name="parentId">
+                <Select
+                    showSearch
+                    placeholder="请选择客服组集"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    allowClear
+                    options={localCorpCsgroupList}
+                />
+            </Form.Item>}
+            <Form.Item label={<strong>切号策略</strong>} name="policyId">
+                <Select
+                    showSearch
+                    placeholder="请选择切号策略"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    allowClear
+                    options={userRotatePolicy}
+                />
+            </Form.Item>
+        </Form>
+    </Modal>
+};
+
+export default React.memo(AddCsGroup);

+ 66 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/groupUserEdit.tsx

@@ -0,0 +1,66 @@
+import React from 'react';
+import '../../../../../tencentAdPutIn/index.less';
+import { Form, InputNumber, message, Modal, Select } from 'antd';
+import { useAjax } from '@/Hook/useAjax';
+import { modifyLocalCorpCsgroupUserApi } from '@/services/adqV3/global';
+import { POOLTYPE } from '../../const';
+
+interface GroupUserEditProps {
+    idList: number[];
+    visible?: boolean;
+    onClose?: () => void;
+    onChange?: () => void;
+}
+
+const GroupUserEdit: React.FC<GroupUserEditProps> = ({ idList, visible, onClose, onChange }) => {
+
+    /**************************************/
+    const [form] = Form.useForm();
+    const modifyLocalCorpCsgroupUser = useAjax((params) => modifyLocalCorpCsgroupUserApi(params))
+    /**************************************/
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            modifyLocalCorpCsgroupUser.run({ ...values, idList }).then((res) => {
+                if (res) {
+                    message.success('修改成功');
+                    onChange?.();
+                }
+            })
+        })
+    }
+
+    return <Modal
+        title={<strong>批量客服修改</strong>}
+        open={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        className='modalResetCss'
+        confirmLoading={modifyLocalCorpCsgroupUser.loading}
+    >
+        <Form
+            form={form}
+            name='basicGroupUserEdit'
+            autoComplete="off"
+            colon={false}
+            layout='vertical'
+        >
+            <Form.Item label={<strong>归属客服池</strong>} name="poolType" rules={[{ required: true, message: '请选择归属客服池!' }]}>
+                <Select
+                    showSearch
+                    placeholder="归属客服池"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    allowClear
+                    options={Object.keys(POOLTYPE).map((key) => ({ label: POOLTYPE[key as keyof typeof POOLTYPE], value: key }))}
+                />
+            </Form.Item>
+            <Form.Item label={<strong>客服号单日限制加粉数量</strong>} name="limitAddFansOfDay">
+                <InputNumber style={{ width: '100%' }} placeholder='请输入客服号单日限制加粉数量' />
+            </Form.Item>
+        </Form>
+    </Modal>
+};
+
+export default React.memo(GroupUserEdit);

+ 290 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/groupUserInfo.tsx

@@ -0,0 +1,290 @@
+import { useAjax } from '@/Hook/useAjax';
+import { delLocalCorpCsgroupUserApi, GetCustomerServiceGroupUserListProps, getLocalCorpCsgroupUserAllListApi, getLocalCorpCsgroupUserListApi } from '@/services/adqV3/global';
+import { DeleteOutlined, EditOutlined, PlusOutlined, SearchOutlined, TeamOutlined } from '@ant-design/icons';
+import { Button, Card, Drawer, Input, message, Popconfirm, Select, Space, Table } from 'antd';
+import React, { useCallback, useEffect, useState } from 'react';
+import '../../../../../tencentAdPutIn/index.less';
+import SettingsEnterprise from './settingsEnterprise';
+import style from '@/pages/launchSystemV3/tencentAdPutIn/create/index.less';
+import { POOLTYPE, POOLTYPEEle } from '../../const';
+import GroupUserEdit from './groupUserEdit';
+
+interface GroupUserInfoProps {
+    groupData: { [x: string]: any };
+    adAccountId?: number;
+}
+
+/**
+ * 客服组成员信息组件
+ * @param param0 
+ * @returns 
+ */
+const GroupUserInfo: React.FC<GroupUserInfoProps> = ({ groupData: { id: localCsgroupId, csgroupName, tencentCorpName, tencentCorpId }, adAccountId }) => {
+
+    /********************************************/
+    const [visible, setVisible] = React.useState<boolean>(false);
+    const [addData, setAddData] = React.useState<{ visible: boolean, data: any }>({ visible: false, data: undefined });
+    const [queryForm, setQueryForm] = useState<GetCustomerServiceGroupUserListProps>({ pageNum: 1, pageSize: 20, localCsgroupId });
+    const [queryParamsNew, setQueryParamsNew] = useState<GetCustomerServiceGroupUserListProps>({ pageNum: 1, pageSize: 20, localCsgroupId });
+    const [selectedRows, setSelectedRows] = useState<any[]>([])
+    const [userEditData, setUserEditData] = useState<{ visible: boolean, idList: number[] }>();
+
+    const getLocalCorpCsgroupUserList = useAjax((params) => getLocalCorpCsgroupUserListApi(params))
+    const getLocalCorpCsgroupUserAllList = useAjax((params) => getLocalCorpCsgroupUserAllListApi(params))
+    const delLocalCorpCsgroupUser = useAjax((params) => delLocalCorpCsgroupUserApi(params))
+    /********************************************/
+
+    const handleUser = useCallback(() => {
+        setVisible(true)
+    }, [])
+
+    useEffect(() => {
+        if (visible) {
+            getLocalCorpCsgroupUserList.run({ ...queryParamsNew, localCsgroupId })
+        }
+    }, [visible, localCsgroupId, queryParamsNew])
+
+    const add = useCallback(() => {
+        getLocalCorpCsgroupUserAllList.run({ localCsgroupId }).then((res) => {
+            console.log('获取客服组成员列表', res);
+            setAddData({
+                visible: true,
+                data: {
+                    corpName: tencentCorpName,
+                    tencentCorpId,
+                    groupId: localCsgroupId,
+                    groupName: csgroupName,
+                    userInfoList: res?.map((item: { localCorpUserName: any; tencentCorpUserId: any; }) => ({ userName: item.localCorpUserName, userId: item.tencentCorpUserId })) || []
+                }
+            });
+        })
+    }, [csgroupName, tencentCorpName, tencentCorpId, localCsgroupId, csgroupName])
+
+    /** 删除客服 */
+    const delUser = (idList: number[]) => {
+        delLocalCorpCsgroupUser.run({ localCsgroupId, idList }).then((res) => {
+            if (res) {
+                message.success('删除成功');
+                getLocalCorpCsgroupUserList.refresh();
+                setSelectedRows([]);
+            }
+        })
+    }
+
+    const userEdit = () => {
+        setUserEditData({ visible: true, idList: selectedRows.map(item => item.id) });
+    }
+
+    return <>
+        <Button icon={<TeamOutlined />} style={{ border: 'none', fontSize: 12 }} size='small' onClick={() => handleUser()}>成员</Button>
+        {visible && <Drawer
+            title={<strong style={{ fontSize: 20 }}>{csgroupName} 客服组成员</strong>}
+            open={visible}
+            onClose={() => setVisible(false)}
+            width={1000}
+            headerStyle={{ padding: '10px 16px' }}
+            maskClosable={false}
+            className={`modalResetCss targetingSelect`}
+        >
+            <Card className="cardResetCss" bodyStyle={{ padding: 0, height: 'calc(100vh - 70px)', overflow: 'hidden', overflowY: 'auto' }}>
+                <div className={style.template} style={{ height: '100%' }}>
+                    <div className={style.template_center}>
+                        <div className="flexSpaceBetween" style={{ marginBottom: 10 }}>
+                            <Space>
+                                <Input
+                                    placeholder="请输入客服名称"
+                                    value={queryForm?.localCorpUserName}
+                                    allowClear
+                                    onChange={(e) => setQueryForm({ ...queryForm, localCorpUserName: e.target.value })}
+                                />
+                                <Select
+                                    showSearch
+                                    placeholder="归属客服池"
+                                    filterOption={(input, option) =>
+                                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                                    }
+                                    style={{ width: 140 }}
+                                    allowClear
+                                    value={queryForm?.poolType}
+                                    onChange={(e) => {
+                                        setQueryForm({ ...queryForm, poolType: e, pageNum: 1 })
+                                    }}
+                                    options={Object.keys(POOLTYPE).map((key) => ({ label: POOLTYPE[key as keyof typeof POOLTYPE], value: key }))}
+                                />
+                                <Button type="primary" icon={<SearchOutlined />} loading={getLocalCorpCsgroupUserList.loading} onClick={() => setQueryParamsNew({ ...queryForm, pageNum: 1 })}>搜索</Button>
+                            </Space>
+                            <Space>
+                                <Button type="primary" disabled={selectedRows.length === 0} icon={<EditOutlined />} onClick={userEdit}>修改</Button>
+                                <Popconfirm
+                                    title="确定删除?"
+                                    onConfirm={() => {
+                                        delUser(selectedRows.map(item => item.id))
+                                    }}
+                                    disabled={selectedRows.length === 0}
+                                >
+                                    <Button type="primary" disabled={selectedRows.length === 0} danger icon={<DeleteOutlined />} loading={delLocalCorpCsgroupUser.loading}>删除</Button>
+                                </Popconfirm>
+                                <Button type="primary" icon={<PlusOutlined />} loading={getLocalCorpCsgroupUserAllList.loading} onClick={add}>成员加减</Button>
+                            </Space>
+                        </div>
+                        <Table
+                            columns={[
+                                {
+                                    title: '客服名称',
+                                    dataIndex: 'localCorpUserName',
+                                    key: 'localCorpUserName',
+                                    width: 250,
+                                    ellipsis: true,
+                                    fixed: 'left',
+                                    render(value, records) {
+                                        return <span style={{ fontSize: 12 }}>{value}({records.localCorpUserId})</span>
+                                    }
+                                },
+                                {
+                                    title: '腾讯端客服ID',
+                                    dataIndex: 'tencentCorpUserId',
+                                    key: 'tencentCorpUserId',
+                                    width: 150,
+                                    ellipsis: true,
+                                    render(value) {
+                                        return <span style={{ fontSize: 12 }}>{value}</span>
+                                    }
+                                },
+                                {
+                                    title: '归属客服池',
+                                    dataIndex: 'poolType',
+                                    key: 'poolType',
+                                    width: 100,
+                                    align: 'center',
+                                    render(value) {
+                                        return <span style={{ fontSize: 12 }}>{POOLTYPEEle[value as keyof typeof POOLTYPEEle]}</span>
+                                    }
+                                },
+                                {
+                                    title: '客服号单日限制加粉数量',
+                                    dataIndex: 'limitAddFansOfDay',
+                                    key: 'limitAddFansOfDay',
+                                    width: 110,
+                                    align: 'center',
+                                    render(value) {
+                                        return <span style={{ fontSize: 12 }}>{value && value === 0 ? value : '--'}</span>
+                                    }
+                                },
+                                {
+                                    title: '创建时间',
+                                    dataIndex: 'createTime',
+                                    key: 'createTime',
+                                    width: 140,
+                                    render(value) {
+                                        return <span style={{ fontSize: 12 }}>{value}</span>
+                                    }
+                                },
+                                {
+                                    title: '更新时间',
+                                    dataIndex: 'updateTime',
+                                    key: 'updateTime',
+                                    width: 140,
+                                    render(value) {
+                                        return <span style={{ fontSize: 12 }}>{value}</span>
+                                    }
+                                },
+                                {
+                                    title: '操作',
+                                    dataIndex: 'cz',
+                                    key: 'cz',
+                                    width: 50,
+                                    align: 'center',
+                                    fixed: 'right',
+                                    render(_, records) {
+                                        return <Popconfirm
+                                            title="确定删除?"
+                                            onConfirm={() => {
+                                                delUser([records.id])
+                                            }}
+                                        >
+                                            <a style={{ color: 'red' }}>删除</a>
+                                        </Popconfirm>
+                                    }
+                                },
+                            ]}
+                            dataSource={getLocalCorpCsgroupUserList.data?.records}
+                            size="small"
+                            loading={getLocalCorpCsgroupUserList?.loading}
+                            scroll={{ y: 500 }}
+                            rowKey={'id'}
+                            pagination={{
+                                total: getLocalCorpCsgroupUserList.data?.total,
+                                defaultPageSize: 20,
+                                current: getLocalCorpCsgroupUserList.data?.current,
+                                pageSize: getLocalCorpCsgroupUserList.data?.size
+                            }}
+                            rowSelection={{
+                                selectedRowKeys: selectedRows.map(item => item.id),
+                                onSelect: (record, selected) => {
+                                    if (selected) {
+                                        selectedRows.push({ ...record })
+                                        setSelectedRows([...selectedRows])
+                                    } else {
+                                        let newSelectAccData = selectedRows.filter((item) => item.id !== record.id)
+                                        setSelectedRows([...newSelectAccData])
+                                    }
+                                },
+                                onSelectAll: (selected, _, changeRows) => {
+                                    if (selected) {
+                                        let newSelectAccData = [...selectedRows]
+                                        changeRows.forEach((item) => {
+                                            let index = newSelectAccData.findIndex((ite) => ite.id === item.id)
+                                            if (index === -1) {
+                                                let data: any = { ...item }
+                                                newSelectAccData.push(data)
+                                            }
+                                        })
+                                        setSelectedRows([...newSelectAccData])
+                                    } else {
+                                        let newSelectAccData = selectedRows.filter((item) => {
+                                            let index = changeRows.findIndex((ite) => ite.id === item.id)
+                                            if (index !== -1) {
+                                                return false
+                                            } else {
+                                                return true
+                                            }
+                                        })
+                                        setSelectedRows([...newSelectAccData])
+                                    }
+                                }
+                            }}
+                            onChange={(pagination) => {
+                                const { current, pageSize } = pagination
+                                setQueryForm({ ...queryForm, pageNum: current as number, pageSize: pageSize as number || 20 })
+                                setQueryParamsNew({ ...queryParamsNew, pageNum: current as number, pageSize: pageSize as number || 20 })
+                            }}
+                        />
+                    </div>
+                </div>
+            </Card>
+
+            {/* 设置修改 */}
+            {addData.visible && <SettingsEnterprise
+                {...addData}
+                adAccountId={adAccountId as number}
+                onClose={() => setAddData({ visible: false, data: undefined })}
+                onChange={() => {
+                    getLocalCorpCsgroupUserList.refresh()
+                    setAddData({ visible: false, data: undefined })
+                }}
+            />}
+
+            {/* 修改客服 */}
+            {userEditData?.visible && <GroupUserEdit
+                {...userEditData}
+                onChange={() => {
+                    setUserEditData(undefined)
+                    getLocalCorpCsgroupUserList.refresh();
+                }}
+                onClose={() => setUserEditData(undefined)}
+            />}
+        </Drawer>}
+    </>
+};
+
+export default React.memo(GroupUserInfo);

+ 169 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/index.tsx

@@ -0,0 +1,169 @@
+import { Button, Input, Select, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import { useAjax } from '@/Hook/useAjax';
+import { getCorpWechatAllApi, getCustomerServiceGroupListApi, GetCustomerServiceGroupListProps, getLocalCorpCsgroupListAllApi, getUserRotatePolicyAllListApi } from '@/services/adqV3/global';
+import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
+import columns from './tableConfig';
+import AddCsGroup from './addCsGroup';
+
+const Group: React.FC<{ adAccountId?: number }> = ({ adAccountId }) => {
+
+    /**********************************/
+    const [queryParamsNew, setQueryParamsNew] = useState<GetCustomerServiceGroupListProps>({ pageNum: 1, pageSize: 20 })
+
+    const [visible, setVisible] = useState<boolean>(false);
+    const [corpWechatList, setCorpWechatList] = useState<{ label: string, value: string }[]>([]);
+    const [userRotatePolicy, setUserRotatePolicy] = useState<{ label: string, value: string }[]>([]);
+    const [localCorpCsgroupList, setLocalCorpCsgroupList] = useState<{ label: string, value: string }[]>([]);
+    const [initialValues, setInitialValues] = useState<any>();
+
+    const getCustomerServiceGroupList = useAjax((params) => getCustomerServiceGroupListApi(params))
+    const getCorpWechatAll = useAjax((params) => getCorpWechatAllApi(params))
+    const getUserRotatePolicyAllList = useAjax((params) => getUserRotatePolicyAllListApi(params))
+    const getLocalCorpCsgroupListAll = useAjax((params) => getLocalCorpCsgroupListAllApi(params))
+    /**********************************/
+
+    useEffect(() => {
+        getCorpWechatAll.run({}).then((res) => {
+            if (res && res.length > 0) {
+                setCorpWechatList(res.map((item: { corpName: string; tencentCorpId: string; }) => ({ label: item.corpName, value: item.tencentCorpId })));
+            }
+        })
+        getUserRotatePolicyAllList.run({}).then((res) => {
+            if (res && res.length > 0) {
+                setUserRotatePolicy(res.map((item: { policyName: string; id: string; }) => ({ label: item.policyName, value: item.id })));
+            }
+        })
+        getList()
+    }, [])
+
+    const getList = () => {
+        getLocalCorpCsgroupListAll.run({ type: 0 }).then((res) => {
+            if (res && res.length > 0) {
+                setLocalCorpCsgroupList(res.map((item: { csgroupName: string; id: string; }) => ({ label: item.csgroupName, value: item.id })));
+            }
+        })
+    }
+
+    useEffect(() => {
+        if (adAccountId) {
+            getCustomerServiceGroupList.run({ ...queryParamsNew, adAccountId })
+        }
+    }, [queryParamsNew, adAccountId])
+
+    const handleEdit = (data: any) => {
+        setInitialValues(data)
+        setVisible(true);
+    }
+
+    return <>
+        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
+            <Input.Search style={{ width: 200 }} enterButton placeholder='请输入客服组名称' onSearch={(e) => setQueryParamsNew({ ...queryParamsNew, csgroupName: e, pageNum: 1 })} allowClear />
+            <Select
+                showSearch
+                placeholder="请选择企业"
+                filterOption={(input, option) =>
+                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                }
+                style={{ width: 140 }}
+                allowClear
+                value={queryParamsNew?.tencentCorpId}
+                loading={getCorpWechatAll.loading}
+                onChange={(e) => {
+                    setQueryParamsNew({ ...queryParamsNew, tencentCorpId: e, pageNum: 1 })
+                }}
+                options={corpWechatList}
+            />
+            <Select
+                showSearch
+                placeholder="请选择轮换策略"
+                filterOption={(input, option) =>
+                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                }
+                style={{ width: 140 }}
+                allowClear
+                value={queryParamsNew?.policyId}
+                onChange={(e) => {
+                    setQueryParamsNew({ ...queryParamsNew, policyId: e, pageNum: 1 })
+                }}
+                options={userRotatePolicy}
+            />
+            <Select
+                showSearch
+                placeholder="请选择归属集"
+                filterOption={(input, option) =>
+                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                }
+                style={{ width: 140 }}
+                allowClear
+                value={queryParamsNew?.parentId}
+                onChange={(e) => {
+                    setQueryParamsNew({ ...queryParamsNew, parentId: e, pageNum: 1 })
+                }}
+                options={localCorpCsgroupList}
+            />
+            <Select
+                showSearch
+                placeholder="是否组集"
+                filterOption={(input, option) =>
+                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                }
+                style={{ width: 100 }}
+                allowClear
+                value={queryParamsNew?.type}
+                onChange={(e) => {
+                    setQueryParamsNew({ ...queryParamsNew, type: e, pageNum: 1 })
+                }}
+                options={[{ label: '是', value: 0 }, { label: '否', value: 1 }]}
+            />
+            <Button type="primary" icon={<SearchOutlined />} loading={getCustomerServiceGroupList.loading} onClick={() => getCustomerServiceGroupList.refresh()}>刷新</Button>
+            <Button
+                type="primary"
+                icon={<PlusOutlined />}
+                onClick={() => {
+                    setVisible(true)
+                }}
+            >新增</Button>
+        </div>
+        <Table
+            columns={columns(handleEdit, adAccountId)}
+            dataSource={getCustomerServiceGroupList.data?.records}
+            size="small"
+            loading={getCustomerServiceGroupList?.loading}
+            scroll={{ y: 600, x: 1000 }}
+            rowKey={'id'}
+            pagination={{
+                total: getCustomerServiceGroupList.data?.total,
+                defaultPageSize: 20,
+                current: getCustomerServiceGroupList.data?.current,
+                pageSize: getCustomerServiceGroupList.data?.size
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryParamsNew({ ...queryParamsNew, pageNum: current as number, pageSize: pageSize as number || 20 })
+            }}
+        />
+
+        {/* 新增客服组 */}
+        {visible && <AddCsGroup
+            visible={visible}
+            initialValues={initialValues}
+            corpWechatList={corpWechatList}
+            userRotatePolicy={userRotatePolicy}
+            localCorpCsgroupList={localCorpCsgroupList}
+            onChange={() => {
+                getCustomerServiceGroupList.refresh();
+                getList()
+                setInitialValues(undefined);
+                setVisible(false);
+            }}
+            onClose={() => {
+                setInitialValues(undefined);
+                setVisible(false);
+            }}
+        />}
+    </>
+};
+
+export default React.memo(Group);

+ 10 - 14
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/settingsEnterprise.tsx → src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/settingsEnterprise.tsx

@@ -1,12 +1,12 @@
 import { Modal, Spin, Typography } from 'antd';
 import React, { useEffect, useState } from 'react';
-import '../../tencentAdPutIn/index.less';
-import './index.less';
-import { getCorpDepartmentListApi, getWechatPagesCsgroupUserApi, updateCustomerServiceGroupApi } from '@/services/adqV3/global';
+import '../../../../../tencentAdPutIn/index.less';
+import '../../index.less';
+import { addLocalCorpCsgroupUserListApi, getCorpDepartmentListApi, getWechatPagesCsgroupUserApi } from '@/services/adqV3/global';
 import { CloseOutlined, UserOutlined } from '@ant-design/icons';
 import { useAjax } from '@/Hook/useAjax';
 import { DataNode } from 'antd/lib/tree';
-import NTree from './nTree';
+import NTree from '../../nTree';
 
 interface IProps {
     adAccountId: number;
@@ -75,7 +75,7 @@ const SettingsEnterprise: React.FC<IProps> = ({ adAccountId, data, visible, onCl
     const [userInfoList, setUserInfoList] = useState<{ userId: string, userName: string, state?: 0 | 1, msg?: string }[]>(data.userInfoList || [])
 
     const getCorpDepartmentList = useAjax((params) => getCorpDepartmentListApi(params))
-    const updateCustomerServiceGroup = useAjax((params) => updateCustomerServiceGroupApi(params))
+    const addLocalCorpCsgroupUserList = useAjax((params) => addLocalCorpCsgroupUserListApi(params))
     /*********************************/
 
     useEffect(() => {
@@ -106,13 +106,10 @@ const SettingsEnterprise: React.FC<IProps> = ({ adAccountId, data, visible, onCl
     }, [adAccountId])
 
     const handleOk = () => {
-
-        updateCustomerServiceGroup.run({
-            adAccountId,
+        addLocalCorpCsgroupUserList.run({
             tencentCorpId: data.tencentCorpId,
-            groupId: data.groupId,
-            groupName: data.groupName,
-            userIdList: userInfoList.map(item => item.userId)
+            localCsgroupId: data.groupId,
+            tencentCorpUserIdList: userInfoList.map(item => item.userId)
         }).then((res) => {
             if (res) {
                 onChange?.()
@@ -138,14 +135,14 @@ const SettingsEnterprise: React.FC<IProps> = ({ adAccountId, data, visible, onCl
 
 
     return <Modal
-        title={<strong>设置客服</strong>}
+        title={<strong>设置客服</strong>}
         open={visible}
         onCancel={onClose}
         onOk={handleOk}
         className='modalResetCss'
         width={750}
         bodyStyle={{ padding: 0 }}
-        confirmLoading={updateCustomerServiceGroup.loading}
+        confirmLoading={addLocalCorpCsgroupUserList.loading}
     >
         <Spin spinning={getCorpDepartmentList.loading || loading}>
             <div className='settingsEnterprise'>
@@ -155,7 +152,6 @@ const SettingsEnterprise: React.FC<IProps> = ({ adAccountId, data, visible, onCl
                             treeData={treeData}
                             loadData={onLoadData}
                             onSelect={(selectedKeys) => {
-                                console.log('selected', selectedKeys);
                                 setUserInfoList(selectedKeys.map((item: any) => ({ userId: item.value, userName: item.label, ...item })))
                             }}
                             selectedKeys={userInfoList.map(({ userId, userName, ...item }) => ({ value: userId, label: userName, ...item }))}

+ 102 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/group/tableConfig.tsx

@@ -0,0 +1,102 @@
+import { Button, Space, TableProps } from "antd"
+import React from "react"
+import { EditOutlined } from "@ant-design/icons"
+import GroupUserInfo from "./groupUserInfo"
+
+const columns = (handleEdit: (data: any) => void, adAccountId?: number): TableProps<any>['columns'] => {
+
+    return [
+        {
+            title: '客服组名称',
+            dataIndex: 'csgroupName',
+            key: 'csgroupName',
+            width: 280,
+            render(value, records) {
+                return <span style={{ fontSize: 12 }}>{value}(ID:{records.id})</span>
+            }
+        },
+        {
+            title: '是否组集',
+            dataIndex: 'type',
+            key: 'type',
+            width: 80,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value == 1 ? '否' : '是'}</span>
+            }
+        },
+        {
+            title: '归属组集',
+            dataIndex: 'parentName',
+            key: 'parentName',
+            width: 100,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+            }
+        },
+
+        {
+            title: '企业名称',
+            dataIndex: 'tencentCorpName',
+            key: 'tencentCorpName',
+            width: 110,
+            align: 'center',
+            ellipsis: true,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            }
+        },
+        {
+            title: '企业ID',
+            dataIndex: 'tencentCorpId',
+            key: 'tencentCorpId',
+            width: 280,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            }
+        },
+        {
+            title: '切号策略',
+            dataIndex: 'policyName',
+            key: 'policyName',
+            width: 100,
+            align: 'center',
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            width: 150,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            }
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            width: 150,
+            render(value) {
+                return <span style={{ fontSize: 12 }}>{value}</span>
+            }
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 200,
+            render(_, record) {
+                return <Space>
+                    {/* <UserInfo userInfoList={record.userInfoList} createTime={record.createTime} groupMemberCnt={record.groupMemberCnt} /> */}
+                    {record.type == 1 && <GroupUserInfo adAccountId={adAccountId} groupData={record} />} 
+                    <Button icon={<EditOutlined />} style={{ border: 'none', fontSize: 12 }} size='small' onClick={() => handleEdit(record)}>修改</Button>
+                </Space>
+            }
+        }
+    ]
+}
+export default columns

+ 98 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/landingPageTieUp/addLandingPageTieUp.tsx

@@ -0,0 +1,98 @@
+import { useAjax } from '@/Hook/useAjax';
+import { addLandingPageCustomerGroupApi, getLocalCorpCsgroupListAllApi, modifyLandingPageCustomerGroupApi } from '@/services/adqV3/global';
+import { Form, message, Modal, Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import SelectLandingPage from './selectLandingPage';
+
+interface Props {
+    adAccountId: number
+    initialValues?: any
+    visible?: boolean
+    onChange?: () => void
+    onClose?: () => void
+}
+
+/**
+ * 新增原生落地页与客服组关系
+ * @param param0 
+ * @returns 
+ */
+const AddLandingPageTieUp: React.FC<Props> = ({ adAccountId, initialValues, visible, onChange, onClose }) => {
+
+    /*******************************************/
+    const [form] = Form.useForm();
+    const [groupList, setGroupList] = useState<{ label: string, value: number }[]>([])
+
+    const addLandingPageCustomerGroup = useAjax((params) => addLandingPageCustomerGroupApi(params))
+    const modifyLandingPageCustomerGroup = useAjax((params) => modifyLandingPageCustomerGroupApi(params))
+    const getLocalCorpCsgroupListAll = useAjax((params) => getLocalCorpCsgroupListAllApi(params))
+    // const getCorpListAll = useAjax(() => getCorpListAllApi())
+    /*******************************************/
+
+
+    useEffect(() => {
+        getLocalCorpCsgroupListAll.run({}).then(res => {
+            if (res && res.length > 0) {
+                const list = res.map((item: { csgroupName: string; id: string; type: 0 | 1 }) => ({ label: item.csgroupName, value: item.id }))
+                setGroupList(list)
+            }
+        })
+    }, [])
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            if (initialValues?.pageId) {
+                modifyLandingPageCustomerGroup.run({ ...values, pageId: initialValues.pageId, pageType: initialValues.pageType }).then((res) => {
+                    if (res) {
+                        message.success('修改成功')
+                        onChange?.()
+                    }
+                })
+            } else {
+                addLandingPageCustomerGroup.run(values).then((res) => {
+                    if (res) {
+                        message.success('新增成功')
+                        onChange?.()
+                    }
+                })
+            }
+        })
+    };
+
+    return <Modal
+        title={<strong>{initialValues?.pageId ? '修改' : '新增'}官方落地页与客服组关系</strong>}
+        open={visible}
+        onOk={handleOk}
+        onCancel={onClose}
+        className='modalResetCss'
+        confirmLoading={addLandingPageCustomerGroup.loading || modifyLandingPageCustomerGroup.loading}
+        width={800}
+    >
+        <Form
+            form={form}
+            name='basicysWechatTieUp'
+            autoComplete="off"
+            colon={false}
+            layout='vertical'
+            initialValues={initialValues}
+        >
+            {!initialValues?.pageId && <Form.Item label={<strong>原生落地页</strong>} name="landingPageList" rules={[{ required: true, message: '请选择官方落地页!' }]}>
+                <SelectLandingPage accountId={adAccountId} />
+            </Form.Item>}
+            <Form.Item label={<strong>本地客服组</strong>} name="localCsgroupId" rules={[{ required: !!initialValues?.pageId, message: '请选择本地客服组!' }]}>
+                <Select
+                    showSearch
+                    placeholder="请选择客服组"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    allowClear
+                    options={groupList}
+                />
+            </Form.Item>
+        </Form>
+    </Modal>;
+};
+
+export default React.memo(AddLandingPageTieUp);

+ 148 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/landingPageTieUp/index.tsx

@@ -0,0 +1,148 @@
+import { useAjax } from '@/Hook/useAjax';
+import { getLandingPageCustomerGroupListApi, GetPageCustomerGroupListProps } from '@/services/adqV3/global';
+import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
+import { Button, Input, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import AddLandingPageTieUp from './addLandingPageTieUp';
+
+/**
+ * 原生落地页与客服组关系
+ * @returns 
+ */
+const LandingPageTieUp: React.FC<{ adAccountId?: number }> = ({ adAccountId }) => {
+
+    /*******************************/
+    const [queryParamsNew, setQueryParamsNew] = useState<GetPageCustomerGroupListProps>({ pageNum: 1, pageSize: 50 })
+    const [visible, setVisible] = useState<boolean>(false)
+    const [initialValues, setInitialValues] = useState<any>();
+
+    const getLandingPageCustomerGroupList = useAjax((params) => getLandingPageCustomerGroupListApi(params))
+    /*******************************/
+
+    /** 获取关系列表 */
+    useEffect(() => {
+        if (adAccountId) {
+            getLandingPageCustomerGroupList.run({ ...queryParamsNew, adAccountId })
+        }
+    }, [adAccountId, queryParamsNew])
+
+
+    return <>
+        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
+            <Input.Search
+                style={{ width: 220 }}
+                enterButton
+                placeholder='请输入原生推广页名称'
+                onSearch={(e) => setQueryParamsNew({ ...queryParamsNew, pageName: e, pageNum: 1 })}
+                allowClear
+            />
+            <Input.Search
+                style={{ width: 220 }}
+                enterButton
+                placeholder='请输入原生推广页ID'
+                onSearch={(e) => setQueryParamsNew({ ...queryParamsNew, pageId: e, pageNum: 1 })}
+                allowClear
+            />
+            <Button type="primary" icon={<SearchOutlined />} loading={getLandingPageCustomerGroupList.loading} onClick={() => getLandingPageCustomerGroupList.refresh()}>刷新</Button>
+            <Button
+                type="primary"
+                icon={<PlusOutlined />}
+                onClick={() => {
+                    setVisible(true)
+                }}
+            >新增</Button>
+        </div>
+
+        <Table
+            columns={[
+                {
+                    title: '原生推广页名称',
+                    dataIndex: 'pageName',
+                    key: 'pageName',
+                    width: 120,
+                    ellipsis: true,
+                    render(value) {
+                        return <span style={{ fontSize: 12 }}>{value}</span>
+                    },
+                },
+                {
+                    title: '原生推广页ID',
+                    dataIndex: 'pageId',
+                    key: 'pageId',
+                    width: 100,
+                    ellipsis: true,
+                    align: 'center',
+                    render(value) {
+                        return <span style={{ fontSize: 12 }}>{value}</span>
+                    },
+                },
+                {
+                    title: '客服组名称',
+                    dataIndex: 'localCsgroupName',
+                    key: 'localCsgroupName',
+                    width: 120,
+                    ellipsis: true,
+                    render(value) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '客服组ID',
+                    dataIndex: 'localCsgroupId',
+                    key: 'localCsgroupId',
+                    align: 'center',
+                    width: 100,
+                    ellipsis: true,
+                    render(value) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '操作',
+                    dataIndex: 'cz',
+                    key: 'cz',
+                    width: 300,
+                    render(_, record) {
+                        return <a
+                            style={{ fontSize: 12 }}
+                            onClick={() => {
+                                setInitialValues(record)
+                                setVisible(true)
+                            }}
+                        >修改</a>
+                    },
+                },
+            ]}
+            dataSource={getLandingPageCustomerGroupList.data?.records || []}
+            loading={getLandingPageCustomerGroupList.loading}
+            scroll={{ x: 1000 }}
+            pagination={{
+                current: queryParamsNew.pageNum,
+                pageSize: queryParamsNew.pageSize,
+                total: getLandingPageCustomerGroupList.data?.total,
+                onChange: (pageNum, pageSize) => setQueryParamsNew({ ...queryParamsNew, pageNum, pageSize }),
+            }}
+            size="small"
+            bordered
+            rowKey="id"
+        />
+
+        {/* 新增 */}
+        {visible && <AddLandingPageTieUp
+            adAccountId={adAccountId as number}
+            visible={visible}
+            initialValues={initialValues}
+            onChange={() => {
+                getLandingPageCustomerGroupList.refresh()
+                setInitialValues(undefined)
+                setVisible(false)
+            }}
+            onClose={() => {
+                setInitialValues(undefined)
+                setVisible(false)
+            }}
+        />}
+    </>;
+};
+
+export default React.memo(LandingPageTieUp);

+ 155 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/landingPageTieUp/selectLandingPage.tsx

@@ -0,0 +1,155 @@
+import { Badge, Button, Checkbox, Input, Select, Space, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import { getAdqLandingPageListApi, getWXDownPageAuthInfoListApi } from '@/services/adqV3/global';
+import { useAjax } from '@/Hook/useAjax';
+import { SyncOutlined } from '@ant-design/icons';
+import { PageStatusEnum } from '@/services/launchAdq/enum';
+
+/**
+ * 选择落地页
+ * @returns 
+ */
+const SelectLandingPage: React.FC<{ accountId: number, value?: any, onChange?: (e: any) => void }> = ({ accountId, value, onChange }) => {
+
+    /***********************************/
+    const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>([])
+    const [queryForm, setQueryForm] = useState<{ accountId?: number, pageName?: string, ownerUid?: number, pageSize: number, pageNum: number, isSqDownPage: boolean, isCanvasType?: boolean }>({ pageNum: 1, pageSize: 20, isSqDownPage: false })
+    const [tableData, setTableData] = useState<any>({})
+    const [wXDownPageAuthInfo, setWXDownPageAuthInfo] = useState<any[]>([])
+
+    const listAjax = useAjax((params) => getAdqLandingPageListApi(params))
+    const getWXDownPageAuthInfoList = useAjax((params) => getWXDownPageAuthInfoListApi(params))
+    /***********************************/
+
+    useEffect(() => {
+        getWXDownPageAuthInfoList.run(accountId).then(res => {
+            if (res && res.length > 0) {
+                setWXDownPageAuthInfo(res)
+            }
+        })
+    }, [accountId])
+
+    useEffect(() => {
+        if (value && JSON.stringify(value) !== JSON.stringify(selectedRowKeys)) {
+            setSelectedRowKeys(value)
+        }
+    }, [value])
+
+    useEffect(() => {
+        if (accountId) {
+            const params: any = { ...queryForm, pageStatus: 'NORMAL', accountId, marketingTargetType: "MARKETING_TARGET_TYPE_WECHAT_WORK" }
+            if (params.isSqDownPage) {
+                if (!params?.ownerUid) {
+                    setTableData({ total: 0, records: [] })
+                    return
+                }
+            } else {
+                delete params?.ownerUid
+            }
+            if (params?.isCanvasType) {
+                params.canvasType = 'CANVAS_TYPE_COMMON_PAGE'
+            }
+            delete params?.isCanvasType
+            delete params.isSqDownPage
+            listAjax.run(params).then(res => {
+                setTableData(res || {})
+            })
+        }
+    }, [accountId, queryForm])
+
+    return <div style={{ padding: 4, border: '1px solid rgb(229, 229, 229)', borderRadius: 8 }}>
+        <Space style={{ marginBottom: 10 }}>
+            <Checkbox
+                onChange={(e) => {
+                    setQueryForm({ ...queryForm, pageNum: 1, isCanvasType: e.target.checked })
+                }}
+                checked={queryForm.isCanvasType}
+            ><span style={{ fontSize: 12 }}>展示外换内组件原生页</span></Checkbox>
+            <Checkbox
+                onChange={(e) => {
+                    if (e.target.checked && wXDownPageAuthInfo && Object.keys(wXDownPageAuthInfo).length) {
+                        setQueryForm({ ...queryForm, pageNum: 1, ownerUid: wXDownPageAuthInfo?.[0]?.accountId, isSqDownPage: true })
+                    } else {
+                        setQueryForm({ ...queryForm, pageNum: 1, ownerUid: undefined, isSqDownPage: false })
+                    }
+                }}
+                checked={queryForm.isSqDownPage}
+            ><span style={{ fontSize: 12 }}>被授权落地页</span></Checkbox>
+            {queryForm.isSqDownPage && <Select
+                placeholder='选择原生页授权方信息'
+                style={{ width: 200 }}
+                showSearch
+                filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                }
+                allowClear
+                onChange={(value) => {
+                    setQueryForm({ ...queryForm, pageNum: 1, ownerUid: value })
+                }}
+                value={queryForm?.ownerUid}
+                dropdownMatchSelectWidth={false}
+            >
+                {wXDownPageAuthInfo?.map((item: { accountId: number; accountName: string }) => {
+                    return <Select.Option value={item.accountId} key={item.accountId}>{item.accountName}({item.accountId})</Select.Option>
+                })}
+            </Select>}
+            <Input value={queryForm?.pageName} style={{ width: 180 }} allowClear placeholder='请输入落地页名称' onChange={(e) => setQueryForm({ ...queryForm, pageNum: 1, pageName: e.target.value })} />
+            <Button style={{ padding: 0, margin: 0 }} icon={<SyncOutlined />} type='link' loading={listAjax?.loading} onClick={() => { listAjax?.refresh() }}><span style={{ fontSize: 12 }}>刷新</span></Button>
+        </Space>
+
+        <Table
+            columns={[
+                {
+                    title: '落地页ID',
+                    dataIndex: 'pageId',
+                    key: 'pageId',
+                    align: 'center',
+                    width: 85
+                },
+                {
+                    title: '落地页名称',
+                    dataIndex: 'pageName',
+                    key: 'pageName',
+                    ellipsis: true,
+                    width: 300
+                },
+                {
+                    title: '落地页状态',
+                    dataIndex: 'pageStatus',
+                    key: 'pageStatus',
+                    align: 'center',
+                    width: 90,
+                    render: (a: string | number) => {
+                        return <Badge status={a === 'NORMAL' ? "success" : a === 'DELETED' ? "error" : 'processing'} text={PageStatusEnum[a as keyof typeof PageStatusEnum]} />
+                    }
+                }
+            ]}
+            dataSource={tableData?.records}
+            size="small"
+            loading={listAjax?.loading}
+            scroll={{ y: 280 }}
+            bordered
+            rowKey={'pageId'}
+            pagination={{
+                total: tableData?.total,
+                defaultPageSize: 20,
+                current: tableData?.current,
+                pageSize: tableData?.size
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryForm({ ...queryForm, pageNum: current as number, pageSize: pageSize as number || 20 })
+            }}
+            rowSelection={{
+                selectedRowKeys: selectedRowKeys?.map((item: any) => item?.pageId),
+                onChange(_, selectedRows) {
+                    setSelectedRowKeys(selectedRows)
+                    onChange?.(selectedRows)
+                },
+            }}
+        />
+    </div>;
+};
+
+export default SelectLandingPage;

+ 98 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/pageTieUp/addPageTieUp.tsx

@@ -0,0 +1,98 @@
+import { useAjax } from '@/Hook/useAjax';
+import { addPageCustomerGroupApi, getLocalCorpCsgroupListAllApi, modifyPageCustomerGroupApi } from '@/services/adqV3/global';
+import { Form, message, Modal, Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import SelectPage from './selectPage';
+
+interface Props {
+    adAccountId: number
+    initialValues?: any
+    visible?: boolean
+    onChange?: () => void
+    onClose?: () => void
+}
+
+/**
+ * 新增官方落地页与客服组关系
+ * @param param0 
+ * @returns 
+ */
+const AddPageTieUp: React.FC<Props> = ({ adAccountId, initialValues, visible, onChange, onClose }) => {
+
+    /*******************************************/
+    const [form] = Form.useForm();
+    const [groupList, setGroupList] = useState<{ label: string, value: number }[]>([])
+
+    const addPageCustomerGroup = useAjax((params) => addPageCustomerGroupApi(params))
+    const modifyPageCustomerGroup = useAjax((params) => modifyPageCustomerGroupApi(params))
+    const getLocalCorpCsgroupListAll = useAjax((params) => getLocalCorpCsgroupListAllApi(params))
+    // const getCorpListAll = useAjax(() => getCorpListAllApi())
+    /*******************************************/
+
+
+    useEffect(() => {
+        getLocalCorpCsgroupListAll.run({}).then(res => {
+            if (res && res.length > 0) {
+                const list = res.map((item: { csgroupName: string; id: string; type: 0 | 1 }) => ({ label: item.csgroupName, value: item.id }))
+                setGroupList(list)
+            }
+        })
+    }, [])
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            if (initialValues?.pageId) {
+                modifyPageCustomerGroup.run({ ...values, pageId: initialValues.pageId, pageType: initialValues.pageType }).then((res) => {
+                    if (res) {
+                        message.success('修改成功')
+                        onChange?.()
+                    }
+                })
+            } else {
+                addPageCustomerGroup.run(values).then((res) => {
+                    if (res) {
+                        message.success('新增成功')
+                        onChange?.()
+                    }
+                })
+            }
+        })
+    };
+
+    return <Modal
+        title={<strong>{initialValues?.pageId ? '修改' : '新增'}官方落地页与客服组关系</strong>}
+        open={visible}
+        onOk={handleOk}
+        onCancel={onClose}
+        className='modalResetCss'
+        confirmLoading={addPageCustomerGroup.loading || modifyPageCustomerGroup.loading}
+        width={750}
+    >
+        <Form
+            form={form}
+            name='basicWechatTieUp'
+            autoComplete="off"
+            colon={false}
+            layout='vertical'
+            initialValues={initialValues}
+        >
+            {!initialValues?.pageId && <Form.Item label={<strong>官方落地页</strong>} name="landingPageList" rules={[{ required: true, message: '请选择官方落地页!' }]}>
+                <SelectPage accountId={adAccountId} />
+            </Form.Item>}
+            <Form.Item label={<strong>本地客服组</strong>} name="localCsgroupId" rules={[{ required: !!initialValues?.pageId, message: '请选择本地客服组!' }]}>
+                <Select
+                    showSearch
+                    placeholder="请选择客服组"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    allowClear
+                    options={groupList}
+                />
+            </Form.Item>
+        </Form>
+    </Modal>;
+};
+
+export default React.memo(AddPageTieUp);

+ 148 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/pageTieUp/index.tsx

@@ -0,0 +1,148 @@
+import { useAjax } from '@/Hook/useAjax';
+import { getPageCustomerGroupListApi, GetPageCustomerGroupListProps } from '@/services/adqV3/global';
+import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
+import { Button, Input, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import AddPageTieUp from './addPageTieUp';
+
+/**
+ * 落地页与客服组关系
+ * @returns 
+ */
+const PageTieUp: React.FC<{ adAccountId?: number }> = ({ adAccountId }) => {
+
+    /*******************************/
+    const [queryParamsNew, setQueryParamsNew] = useState<GetPageCustomerGroupListProps>({ pageNum: 1, pageSize: 50 })
+    const [visible, setVisible] = useState<boolean>(false)
+    const [initialValues, setInitialValues] = useState<any>();
+
+    const getPageCustomerGroupList = useAjax((params) => getPageCustomerGroupListApi(params))
+    /*******************************/
+
+    /** 获取关系列表 */
+    useEffect(() => {
+        if (adAccountId) {
+            getPageCustomerGroupList.run({ ...queryParamsNew, adAccountId })
+        }
+    }, [adAccountId, queryParamsNew])
+
+
+    return <>
+        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
+            <Input.Search
+                style={{ width: 220 }}
+                enterButton
+                placeholder='请输入官方落地页名称'
+                onSearch={(e) => setQueryParamsNew({ ...queryParamsNew, pageName: e, pageNum: 1 })}
+                allowClear
+            />
+            <Input.Search
+                style={{ width: 220 }}
+                enterButton
+                placeholder='请输入官方落地页ID'
+                onSearch={(e) => setQueryParamsNew({ ...queryParamsNew, pageId: e, pageNum: 1 })}
+                allowClear
+            />
+            <Button type="primary" icon={<SearchOutlined />} loading={getPageCustomerGroupList.loading} onClick={() => getPageCustomerGroupList.refresh()}>刷新</Button>
+            <Button
+                type="primary"
+                icon={<PlusOutlined />}
+                onClick={() => {
+                    setVisible(true)
+                }}
+            >新增</Button>
+        </div>
+
+        <Table
+            columns={[
+                {
+                    title: '官方落地页名称',
+                    dataIndex: 'pageName',
+                    key: 'pageName',
+                    width: 120,
+                    ellipsis: true,
+                    render(value) {
+                        return <span style={{ fontSize: 12 }}>{value}</span>
+                    },
+                },
+                {
+                    title: '官方落地页ID',
+                    dataIndex: 'pageId',
+                    key: 'pageId',
+                    width: 100,
+                    ellipsis: true,
+                    align: 'center',
+                    render(value) {
+                        return <span style={{ fontSize: 12 }}>{value}</span>
+                    },
+                },
+                {
+                    title: '客服组名称',
+                    dataIndex: 'localCsgroupName',
+                    key: 'localCsgroupName',
+                    width: 120,
+                    ellipsis: true,
+                    render(value) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '客服组ID',
+                    dataIndex: 'localCsgroupId',
+                    key: 'localCsgroupId',
+                    align: 'center',
+                    width: 100,
+                    ellipsis: true,
+                    render(value) {
+                        return <span style={{ fontSize: 12 }}>{value || '--'}</span>
+                    },
+                },
+                {
+                    title: '操作',
+                    dataIndex: 'cz',
+                    key: 'cz',
+                    width: 300,
+                    render(_, record) {
+                        return <a 
+                            style={{ fontSize: 12 }}
+                            onClick={() => {
+                                setInitialValues(record)
+                                setVisible(true)
+                            }}
+                        >修改</a>
+                    },
+                },
+            ]}
+            dataSource={getPageCustomerGroupList.data?.records || []}
+            loading={getPageCustomerGroupList.loading}
+            scroll={{ x: 1000 }}
+            pagination={{
+                current: queryParamsNew.pageNum,
+                pageSize: queryParamsNew.pageSize,
+                total: getPageCustomerGroupList.data?.total,
+                onChange: (pageNum, pageSize) => setQueryParamsNew({ ...queryParamsNew, pageNum, pageSize }),
+            }}
+            size="small"
+            bordered
+            rowKey="id"
+        />
+
+        {/* 新增 */}
+        {visible && <AddPageTieUp
+            adAccountId={adAccountId as number}
+            visible={visible}
+            initialValues={initialValues}
+            onChange={() => {
+                getPageCustomerGroupList.refresh()
+                setInitialValues(undefined)
+                setVisible(false)
+            }}
+            onClose={() => {
+                setInitialValues(undefined)
+                setVisible(false)
+            }}
+        />}
+    </>;
+};
+
+export default React.memo(PageTieUp);

+ 151 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/pageTieUp/selectPage.tsx

@@ -0,0 +1,151 @@
+import { Badge, Button, Checkbox, Input, Select, Space, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import { getAdqLandingPageOfficialListApi, getWXDownPageAuthInfoListApi } from '@/services/adqV3/global';
+import { useAjax } from '@/Hook/useAjax';
+import { SyncOutlined } from '@ant-design/icons';
+import { PageStatusEnum } from '@/services/launchAdq/enum';
+
+/**
+ * 选择落地页
+ * @returns 
+ */
+const SelectPage: React.FC<{ accountId: number, value?: any, onChange?: (e: any) => void }> = ({ accountId, value, onChange }) => {
+
+    /***********************************/
+    const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>([])
+    const [queryForm, setQueryForm] = useState<{ accountId?: number, pageName?: string, ownerUid?: number, pageSize: number, pageNum: number, isSqDownPage: boolean }>({ pageNum: 1, pageSize: 20, isSqDownPage: false })
+    const [wXDownPageAuthInfo, setWXDownPageAuthInfo] = useState<any[]>([])
+    const [tableData, setTableData] = useState<any>({})
+
+    const listAjax = useAjax((params) => getAdqLandingPageOfficialListApi(params))
+    const getWXDownPageAuthInfoList = useAjax((params) => getWXDownPageAuthInfoListApi(params))
+    /***********************************/
+
+    useEffect(() => {
+        getWXDownPageAuthInfoList.run(accountId).then(res => {
+            if (res && res.length > 0) {
+                setWXDownPageAuthInfo(res)
+            }
+        })
+    }, [accountId])
+
+    useEffect(() => {
+        if (value && JSON.stringify(value) !== JSON.stringify(selectedRowKeys)) {
+            setSelectedRowKeys(value)
+        }
+    }, [value])
+
+    useEffect(() => {
+        if (accountId) {
+            const params: any = {
+                ...queryForm,
+                pageStatus: 'NORMAL',
+                pageType: 'PAGE_TYPE_OFFICIAL',
+                accountId,
+                // marketingTargetType: "MARKETING_TARGET_TYPE_WECHAT_WORK"
+            }
+            if (params.isSqDownPage) {
+                if (!params?.ownerUid) {
+                    setTableData({ total: 0, records: [] })
+                    return
+                }
+            } else {
+                delete params?.ownerUid
+            }
+            delete params.isSqDownPage
+            listAjax.run(params).then(res => {
+                setTableData(res || {})
+            })
+        }
+    }, [accountId, queryForm])
+
+    return <div>
+        <Space style={{ marginBottom: 10 }}>
+            <Checkbox
+                onChange={(e) => {
+                    if (e.target.checked && wXDownPageAuthInfo && Object.keys(wXDownPageAuthInfo).length) {
+                        setQueryForm({ ...queryForm, pageNum: 1, ownerUid: wXDownPageAuthInfo?.[0]?.accountId, isSqDownPage: true })
+                    } else {
+                        setQueryForm({ ...queryForm, pageNum: 1, ownerUid: undefined, isSqDownPage: false })
+                    }
+                }}
+                checked={queryForm.isSqDownPage}
+            ><span style={{ fontSize: 12 }}>被授权落地页</span></Checkbox>
+            {queryForm.isSqDownPage && <Select
+                placeholder='选择原生页授权方信息'
+                style={{ width: 200 }}
+                showSearch
+                filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                }
+                allowClear
+                onChange={(value) => {
+                    setQueryForm({ ...queryForm, pageNum: 1, ownerUid: value })
+                }}
+                value={queryForm?.ownerUid}
+                dropdownMatchSelectWidth={false}
+            >
+                {wXDownPageAuthInfo?.map((item: { accountId: number; accountName: string }) => {
+                    return <Select.Option value={item.accountId} key={item.accountId}>{item.accountName}({item.accountId})</Select.Option>
+                })}
+            </Select>}
+            <Input value={queryForm?.pageName} style={{ width: 180 }} allowClear placeholder='请输入官方落地页名称' onChange={(e) => setQueryForm({ ...queryForm, pageNum: 1, pageName: e.target.value })} />
+            <Button style={{ padding: 0, margin: 0 }} icon={<SyncOutlined />} type='link' loading={listAjax?.loading} onClick={() => { listAjax?.refresh() }}><span style={{ fontSize: 12 }}>刷新</span></Button>
+        </Space>
+
+        <Table
+            columns={[
+                {
+                    title: '官方落地页ID',
+                    dataIndex: 'pageId',
+                    key: 'pageId',
+                    align: 'center',
+                    width: 85
+                },
+                {
+                    title: '官方落地页名称',
+                    dataIndex: 'pageName',
+                    key: 'pageName',
+                    ellipsis: true,
+                    width: 300
+                },
+                {
+                    title: '官方落地页状态',
+                    dataIndex: 'pageStatus',
+                    key: 'pageStatus',
+                    align: 'center',
+                    width: 90,
+                    render: (a: string | number) => {
+                        return <Badge status={a === 'NORMAL' ? "success" : a === 'DELETED' ? "error" : 'processing'} text={PageStatusEnum[a as keyof typeof PageStatusEnum]} />
+                    }
+                },
+            ]}
+            dataSource={tableData?.records}
+            size="small"
+            loading={listAjax?.loading}
+            scroll={{ y: 280 }}
+            bordered
+            rowKey={'pageId'}
+            pagination={{
+                total: tableData?.total,
+                defaultPageSize: 20,
+                current: tableData?.current,
+                pageSize: tableData?.size
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryForm({ ...queryForm, pageNum: current as number, pageSize: pageSize as number || 20 })
+            }}
+            rowSelection={{
+                selectedRowKeys: selectedRowKeys?.map((item: any) => item?.pageId),
+                onChange(_, selectedRows) {
+                    setSelectedRowKeys(selectedRows)
+                    onChange?.(selectedRows)
+                },
+            }}
+        />
+    </div>;
+};
+
+export default SelectPage;

+ 79 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/addTencentGroup.tsx

@@ -0,0 +1,79 @@
+import React from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import { Form, Input, message, Modal, Select } from 'antd';
+import { useAjax } from '@/Hook/useAjax';
+import { addTencentCorpCsgroupApi, modifyLocalCorpCsgroupApi } from '@/services/adqV3/global';
+import SelectKF from './selectKF';
+
+interface AddCsGroupProps {
+    corpWechatList: { label: string; value: string }[];
+    adAccountId?: number; // 广告账号ID
+    visible?: boolean;
+    onClose?: () => void;
+    onChange?: () => void;
+    initialValues?: any; // 用于表单的初始值
+}
+const AddTencentGroup: React.FC<AddCsGroupProps> = ({ corpWechatList, adAccountId, visible, onClose, onChange, initialValues }) => {
+
+    /**************************************/
+    const [form] = Form.useForm();
+    const tencentCorpId = Form.useWatch('tencentCorpId', form);
+
+    const addTencentCorpCsgroup = useAjax((params) => addTencentCorpCsgroupApi(params))
+    /**************************************/
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            const { tencentCorpUserIds, ...params } = values;
+            addTencentCorpCsgroup.run({ ...params, accountId: adAccountId, tencentCorpUserIds: tencentCorpUserIds.map((item: { userId: any; }) => item.userId) }).then((res) => {
+                if (res) {
+                    message.success('新增成功')
+                    onChange?.()
+                }
+            })
+        })
+    }
+
+    return <Modal
+        title={<strong>新增客服组</strong>}
+        open={visible}
+        onOk={handleOk}
+        onCancel={onClose}
+        className='modalResetCss'
+        confirmLoading={addTencentCorpCsgroup.loading}
+        width={550}
+    >
+        <Form
+            form={form}
+            name='basicAddGroup'
+            autoComplete="off"
+            colon={false}
+            labelCol={{ span: 5 }}
+            labelAlign='left'
+            initialValues={initialValues} // 默认类型为客服组
+        >
+            <Form.Item label={<strong>客服组名称</strong>} name="groupName" rules={[{ required: true, message: `请输入客服组名称!` }]}>
+                <Input placeholder={`请输入客服组名称`} />
+            </Form.Item>
+            <Form.Item label={<strong>腾讯企业</strong>} name="tencentCorpId" rules={[{ required: true, message: '请选择腾讯企业!' }]}>
+                <Select
+                    showSearch
+                    placeholder="请选择企业"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    allowClear
+                    options={corpWechatList}
+                />
+            </Form.Item>
+            {tencentCorpId && <Form.Item label={<strong>客服</strong>} name="tencentCorpUserIds" rules={[{ required: true, message: '请选择客服!' }]}>
+                <SelectKF
+                    data={corpWechatList.find(item => item.value === tencentCorpId) as any}
+                    adAccountId={adAccountId as number}
+                />
+            </Form.Item>}
+        </Form>
+    </Modal>
+};
+
+export default React.memo(AddTencentGroup);

+ 73 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/bandLocal.tsx

@@ -0,0 +1,73 @@
+import React, { useEffect } from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import { Form, message, Modal, Select } from 'antd';
+import { bandTencentCorpCsgroupApi, getLocalCorpCsgroupListAllApi } from '@/services/adqV3/global';
+import { useAjax } from '@/Hook/useAjax';
+
+
+interface BandLocalProps {
+    data: { [x: string]: any };
+    visible?: boolean;
+    onClose?: () => void;
+    onChange?: () => void;
+}
+
+
+const BandLocal: React.FC<BandLocalProps> = ({ data, visible, onClose, onChange }) => {
+
+    /*********************************/
+    const [form] = Form.useForm();
+
+    const getLocalCorpCsgroupListAll = useAjax((params) => getLocalCorpCsgroupListAllApi(params))
+    const bandTencentCorpCsgroup = useAjax((params) => bandTencentCorpCsgroupApi(params))
+    /*********************************/
+
+    useEffect(() => {
+        getLocalCorpCsgroupListAll.run({ type: 1 })
+    }, [])
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            bandTencentCorpCsgroup.run({ ...values, groupId: data.groupId }).then((res) => {
+                if (res) {
+                    message.success('绑定成功')
+                    onChange?.()
+                }
+            })
+        })
+    }
+
+    return <Modal
+        title={<strong>{data.groupName}与本地客服组绑定关系</strong>}
+        open={visible}
+        onOk={handleOk}
+        onCancel={onClose}
+        className='modalResetCss'
+        width={550}
+        confirmLoading={bandTencentCorpCsgroup.loading}
+    >
+        <Form
+            form={form}
+            name='basicbandGroup'
+            autoComplete="off"
+            colon={false}
+            labelCol={{ span: 5 }}
+            labelAlign='left'
+            initialValues={data}
+        >
+            <Form.Item label={<strong>本地客服组</strong>} name="localCsgroupId">
+                <Select
+                    showSearch
+                    placeholder="请选择本地客服组"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
+                    }
+                    allowClear
+                    options={getLocalCorpCsgroupListAll?.data?.map((item: { csgroupName: string; id: string; }) => ({ label: item.csgroupName, value: item.id })) || []}
+                />
+            </Form.Item>
+        </Form>
+    </Modal>
+};
+
+export default BandLocal;

+ 109 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/index.tsx

@@ -0,0 +1,109 @@
+import { Button, Input, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../../../../../tencentAdPutIn/index.less'
+import { useAjax } from '@/Hook/useAjax';
+import { getCorpWechatAllApi, getTencentCorpCsgroupListApi, GetTencentCorpCsgroupListProps } from '@/services/adqV3/global';
+import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
+import columns from './tableConfig';
+import AddTencentGroup from './addTencentGroup';
+import BandLocal from './bandLocal';
+
+const TencentGroup: React.FC<{ adAccountId?: number }> = ({ adAccountId }) => {
+
+    /**********************************/
+    const [queryParamsNew, setQueryParamsNew] = useState<GetTencentCorpCsgroupListProps>({ pageNum: 1, pageSize: 20 })
+    const [corpWechatList, setCorpWechatList] = useState<{ label: string, value: string }[]>([]);
+    const [addData, setAddData] = useState<{ visible: boolean, initialValues: any }>({ visible: false, initialValues: undefined });
+    const [bandData, setBandData] = useState<{ visible: boolean, data: any }>({ visible: false, data: undefined });
+
+    const getTencentCorpCsgroupList = useAjax((params) => getTencentCorpCsgroupListApi(params))
+    const getCorpWechatAll = useAjax((params) => getCorpWechatAllApi(params))
+    /**********************************/
+
+    useEffect(() => {
+        getCorpWechatAll.run({}).then((res) => {
+            if (res && res.length > 0) {
+                setCorpWechatList(res.map((item: { corpName: string; tencentCorpId: string; }) => ({ label: item.corpName, value: item.tencentCorpId })));
+            }
+        })
+    }, [])
+
+    useEffect(() => {
+        if (adAccountId) {
+            getTencentCorpCsgroupList.run({ ...queryParamsNew })
+        }
+    }, [queryParamsNew, adAccountId])
+
+    const handleBand = (record: any) => {
+        setBandData({ visible: true, data: record })
+        // setAddData({ visible: true, initialValues: record })
+    };
+
+
+    return <>
+        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
+            <Input.Search
+                style={{ width: 220 }}
+                enterButton
+                placeholder='请输入客服组名称'
+                onSearch={(e) => setQueryParamsNew({ ...queryParamsNew, groupName: e, pageNum: 1 })}
+                allowClear
+            />
+            <Button type="primary" icon={<SearchOutlined />} loading={getTencentCorpCsgroupList.loading} onClick={() => getTencentCorpCsgroupList.refresh()}>刷新</Button>
+            <Button
+                type="primary"
+                icon={<PlusOutlined />}
+                onClick={() => {
+                    setAddData({ visible: true, initialValues: undefined })
+                }}
+            >新增</Button>
+        </div>
+        <Table
+            columns={columns(handleBand)}
+            dataSource={getTencentCorpCsgroupList.data?.records}
+            size="small"
+            loading={getTencentCorpCsgroupList?.loading}
+            scroll={{ y: 600, x: 1000 }}
+            rowKey={'groupId'}
+            pagination={{
+                total: getTencentCorpCsgroupList.data?.total,
+                defaultPageSize: 20,
+                current: getTencentCorpCsgroupList.data?.current,
+                pageSize: getTencentCorpCsgroupList.data?.size
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryParamsNew({ ...queryParamsNew, pageNum: current as number, pageSize: pageSize as number || 20 })
+            }}
+
+        />
+
+        {/* 设置修改腾讯组 */}
+        {addData?.visible && <AddTencentGroup
+            {...addData}
+            adAccountId={adAccountId as number}
+            corpWechatList={corpWechatList}
+            onClose={() => {
+                setAddData({ visible: false, initialValues: undefined })
+            }}
+            onChange={() => {
+                getTencentCorpCsgroupList.refresh();
+                setAddData({ visible: false, initialValues: undefined });
+            }}
+        />}
+
+        {/* 绑定本地客服组 */}
+        {bandData?.visible && <BandLocal
+            {...bandData}
+            onClose={() => {
+                setBandData({ visible: false, data: undefined })
+            }}
+            onChange={() => {
+                getTencentCorpCsgroupList.refresh();
+                setBandData({ visible: false, data: undefined });
+            }}
+        />}
+    </>
+};
+
+export default React.memo(TencentGroup);

+ 45 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/selectKF.tsx

@@ -0,0 +1,45 @@
+import { Button } from 'antd';
+import React, { useState } from 'react';
+import SettingsEnterprise from './settingsEnterprise';
+
+
+interface SelectKFProps {
+    adAccountId: number; // 广告账号ID
+    data: {
+        label: string;
+        value: string;
+    };
+    value?: { userId: string, userName: string }[]; // 选中的客服ID
+    onChange?: (value?: { userId: string, userName: string }[]) => void;
+}
+
+const SelectKF: React.FC<SelectKFProps> = ({ adAccountId, data, value, onChange }) => {
+    
+    /*************************************/
+    const [visible, setVisible] = useState<boolean>(false);
+    /*************************************/
+
+    return <>
+        <Button type="primary" onClick={() => setVisible(true)}>{(value && value?.length > 0) ? '重新选择' : '选择客服'}</Button>
+        {(value && value?.length > 0) && <div style={{ display: 'flex', marginTop: 8, flexDirection: 'column' }}>
+            <p style={{ marginBottom: 2 }}>已选客服:</p>
+            <div>{value.map(item => {
+                return <div key={item.userId}>{item.userName + `(${item.userId})`}</div>
+            })}</div>
+        </div>}
+
+        {visible && <SettingsEnterprise
+            adAccountId={adAccountId}
+            visible={visible}
+            value={value}
+            data={{ corpName: data.label, tencentCorpId: data.value }}
+            onClose={() => setVisible(false)}
+            onChange={(value) => {
+                onChange?.(value?.map(item => ({ userId: item.userId, userName: item.userName })));
+                setVisible(false)
+            }}
+        />}
+    </>
+};
+
+export default React.memo(SelectKF);

+ 172 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/settingsEnterprise.tsx

@@ -0,0 +1,172 @@
+import { Modal, Spin, Typography } from 'antd';
+import React, { useEffect, useState } from 'react';
+import '../../../../../tencentAdPutIn/index.less';
+import '../../index.less';
+import { getCorpDepartmentListApi, getWechatPagesCsgroupUserApi } from '@/services/adqV3/global';
+import { CloseOutlined, UserOutlined } from '@ant-design/icons';
+import { useAjax } from '@/Hook/useAjax';
+import { DataNode } from 'antd/lib/tree';
+import NTree from '../../nTree';
+
+interface IProps {
+    adAccountId: number;
+    data: {
+        corpName: string;
+        tencentCorpId: string;
+    };
+    value?: { userId: string, userName: string }[]
+    visible?: boolean;
+    onClose?: () => void;
+    onChange?: (value?: { userId: string, userName: string }[]) => void;
+}
+
+// 定义接口
+interface Department {
+    corpId: string;
+    departmentId: number;
+    name: string;
+    parentid: number;
+    orderIn: number;
+    createTime: string;
+    children: Department[];
+}
+
+const updateTreeData = (list: DataNode[], key: React.Key, children: DataNode[], childrenLen?: number): DataNode[] => list.map(node => {
+    if (node.key === key) {
+        if (childrenLen && node?.children) {
+            return { ...node, children: [...node.children, ...children,] };
+        }
+        return {
+            ...node,
+            children,
+        };
+    }
+    if (node.children) {
+        return {
+            ...node,
+            children: updateTreeData(node.children, key, children, childrenLen),
+        };
+    }
+    return node;
+});
+
+/**
+ * 初始化数据
+ * @param data 
+ * @returns 
+ */
+const getInitializationData = (data: Department[]): DataNode[] => {
+    return data.map((item: Department) => {
+        return { title: item.name, key: item.departmentId, checkable: false, children: item?.children?.length ? getInitializationData(item.children) : [] }
+    })
+}
+
+/**
+ * 修改客服组
+ * @returns 
+ */
+const SettingsEnterprise: React.FC<IProps> = ({ adAccountId, data, value, visible, onClose, onChange }) => {
+
+    /*********************************/
+    const [treeData, setTreeData] = useState<DataNode[]>([]);
+    const [loading, setLoading] = useState<boolean>(false);
+    const [userInfoList, setUserInfoList] = useState<{ userId: string, userName: string }[]>(value || [])
+
+    const getCorpDepartmentList = useAjax((params) => getCorpDepartmentListApi(params))
+    /*********************************/
+
+    useEffect(() => {
+        const getData = async () => {
+            try {
+                const res = await getCorpDepartmentList.run({ corpName: data.corpName })
+                let dataTree: DataNode[] = []
+                if (res?.length > 0) {
+                    dataTree = getInitializationData(res)
+                }
+                setLoading(true)
+                const list = await getWechatPagesCsgroupUserApi({
+                    adAccountId,
+                    tencentCorpId: data.tencentCorpId,
+                    corpName: data.corpName
+                })
+                if (list.data?.length) {
+                    dataTree.push(...list.data.map((item: any) => ({ title: item.userName, key: item.userId, isLeaf: true })))
+                }
+                setLoading(false)
+                setTreeData(dataTree)
+            } catch (error) {
+                console.error(error)
+            }
+        }
+        if (adAccountId)
+            getData()
+    }, [adAccountId])
+
+    const handleOk = () => {
+        onChange?.(userInfoList);
+    }
+
+    const onLoadData = ({ key, children }: any) => {
+        return new Promise<void>(resolve => {
+            getWechatPagesCsgroupUserApi({
+                adAccountId,
+                tencentCorpId: data.tencentCorpId,
+                corpName: data.corpName,
+                departmentId: key
+            }).then((res) => {
+                setTreeData(origin =>
+                    updateTreeData(origin, key, res.data.map((item: any) => ({ title: item.userName, key: item.userId, isLeaf: true })), children?.length),
+                );
+                resolve();
+            })
+        });
+    }
+
+
+    return <Modal
+        title={<strong>选择客服</strong>}
+        open={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        className='modalResetCss'
+        width={750}
+        bodyStyle={{ padding: 0 }}
+    >
+        <Spin spinning={getCorpDepartmentList.loading || loading}>
+            <div className='settingsEnterprise'>
+                <div className='settingsEnterprise-left'>
+                    <div>
+                        <NTree
+                            treeData={treeData}
+                            loadData={onLoadData}
+                            onSelect={(selectedKeys) => {
+                                setUserInfoList(selectedKeys.map((item: any) => ({ userId: item.value, userName: item.label, ...item })))
+                            }}
+                            selectedKeys={userInfoList.map(({ userId, userName, ...item }) => ({ value: userId, label: userName, ...item }))}
+                        />
+                    </div>
+                </div>
+                <div className='settingsEnterprise-right'>
+                    <div>
+                        <div className='settingsEnterprise-right-title'>已选 <span>{userInfoList?.length || 0}</span> 个客服</div>
+                        <div className='settingsEnterprise-right-list'>
+                            {userInfoList.map(item => {
+                                return <div className='list-item' key={item.userId}>
+                                    <div>
+                                        <UserOutlined />
+                                        <Typography.Text ellipsis>{item?.userName || item.userId}</Typography.Text>
+                                    </div>
+                                    <a onClick={() => {
+                                        setUserInfoList(userInfoList.filter(item2 => item2.userId !== item.userId))
+                                    }}><CloseOutlined /></a>
+                                </div>
+                            })}
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </Spin>
+    </Modal>
+};
+
+export default React.memo(SettingsEnterprise);

+ 16 - 16
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/tableConfig.tsx → src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/tableConfig.tsx

@@ -1,9 +1,9 @@
-import { Button, Space, TableProps } from "antd"
+import {  Button, Space, TableProps } from "antd"
 import React from "react"
 import UserInfo from "./userInfo"
-import { EditOutlined } from "@ant-design/icons"
+import { ApiOutlined } from "@ant-design/icons"
 
-let columns = (handleEdit: (data: any) => void): TableProps<any>['columns'] => {
+let columns = (handleBand: (value: any) => void): TableProps<any>['columns'] => {
 
     return [
         {
@@ -16,20 +16,20 @@ let columns = (handleEdit: (data: any) => void): TableProps<any>['columns'] => {
             }
         },
         {
-            title: '客服人数',
-            dataIndex: 'groupMemberCnt',
-            key: 'groupMemberCnt',
-            width: 75,
-            align: 'center',
-            render(value) {
-                return <span style={{ fontSize: 12 }}>{value}</span>
+            title: '绑定的本地客服组',
+            dataIndex: 'localCsgroupName',
+            key: 'localCsgroupName',
+            width: 150,
+            render(value, records) {
+                return <span style={{ fontSize: 12 }}>{value ? value + `(${records.localCsgroupId})`: '--'}</span>
             }
         },
         {
-            title: '创建时间',
-            dataIndex: 'createTime',
-            key: 'createTime',
-            width: 150,
+            title: '广告账号',
+            dataIndex: 'accountId',
+            key: 'accountId',
+            width: 75,
+            align: 'center',
             render(value) {
                 return <span style={{ fontSize: 12 }}>{value}</span>
             }
@@ -60,8 +60,8 @@ let columns = (handleEdit: (data: any) => void): TableProps<any>['columns'] => {
             key: 'cz',
             render(_, record) {
                 return <Space>
-                    <UserInfo userInfoList={record.userInfoList} createTime={record.createTime} groupMemberCnt={record.groupMemberCnt}/>
-                    <Button icon={<EditOutlined />} style={{ border: 'none', fontSize: 12 }} size='small' onClick={() => handleEdit(record)}>修改</Button>
+                    <UserInfo data={record} />
+                    <Button icon={<ApiOutlined />} style={{ border: 'none', fontSize: 12 }} size='small' onClick={() => handleBand(record)}>与本地客服组绑定关系</Button>
                 </Space>
             }
         }

+ 53 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/components/tencentGroup/userInfo.tsx

@@ -0,0 +1,53 @@
+import { UnorderedListOutlined } from '@ant-design/icons';
+import { Button, Modal, Spin } from 'antd';
+import React, { useEffect } from 'react';
+import '../../../../../tencentAdPutIn/index.less';
+import { getUserListListApi } from '@/services/adqV3/global';
+import { useAjax } from '@/Hook/useAjax';
+
+/**
+ * UserInfo是一个React函数组件,用于展示用户信息页面
+ * 该组件没有参数和返回值,它通过返回一个包含用户信息页面的JSX元素来工作
+ * 选择使用函数组件而不是类组件是因为用户信息页面通常不涉及复杂的组件状态管理
+ * 此外,通过使用函数组件,可以利用React的Hooks特性,进一步简化组件逻辑
+ */
+const UserInfo: React.FC<{ data: any }> = ({ data }) => {
+    // 返回一个包含用户信息页面布局的JSX元素
+    // 这里使用了简单的HTML元素来构建页面结构,包括一个标题和一段描述
+
+    /************************************/
+    const [visible, setVisible] = React.useState<boolean>(false);
+
+    const getUserListList = useAjax((params) => getUserListListApi(params))
+    /************************************/
+
+    useEffect(() => { 
+        if (data?.groupId && visible) {
+            getUserListList.run({ groupId: data.groupId })
+        }
+    }, [data?.groupId, visible]);
+
+    return <>
+        <Button icon={<UnorderedListOutlined />} style={{ border: 'none', fontSize: 12 }} size='small' onClick={() => setVisible(true)}>详情</Button>
+        {visible && <Modal
+            title={<strong>客服组详情</strong>}
+            open={visible}
+            onCancel={() => setVisible(false)}
+            footer={null}
+            className='modalResetCss'
+        >
+            <Spin spinning={getUserListList.loading} tip='正在加载数据,请稍后...'>
+                <p>客服组名称:{data?.groupName || '--'}</p>
+                <p>客服人数:{getUserListList?.data?.length || 0}</p>
+                <div style={{ display: 'flex' }}>
+                    <p>已选客服:</p>
+                    <div>{getUserListList?.data?.map((item: { tencentCorpUserId: string; corpUserName: string; }) => {
+                        return <div key={item.tencentCorpUserId}>{item.corpUserName + `(${item.tencentCorpUserId})`}</div>
+                    })}</div>
+                </div>
+            </Spin>
+        </Modal>}
+    </>;
+};
+
+export default React.memo(UserInfo);

+ 20 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/const.tsx

@@ -0,0 +1,20 @@
+import { Badge } from "antd"
+import React from "react"
+
+/** 客服号类型 */
+export const POOLTYPE = {
+    // OFFLINE: '默认',
+    ONLINE: '客服组内',
+    WAITING: '等待客服池',
+    WARRING: '告警客服池',
+    BLOCKLIST: '黑名单客服池'
+}
+
+
+export const POOLTYPEEle = {
+    // OFFLINE: '默认',
+    ONLINE: <Badge status="success" size="small" text={<span style={{ fontSize: 12 }}>客服组内</span>} />,
+    WAITING: <Badge status="processing" size="small" text={<span style={{ fontSize: 12 }}>等待客服池</span>} />,
+    WARRING: <Badge status="warning" size="small" text={<span style={{ fontSize: 12 }}>告警客服池</span>} />,
+    BLOCKLIST: <Badge status="error" size="small" text={<span style={{ fontSize: 12 }}>黑名单客服池</span>} />
+}

+ 0 - 0
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/index.less → src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/index.less


+ 7 - 5
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/index.tsx → src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/index.tsx

@@ -1,10 +1,11 @@
 import { Card, Tabs } from 'antd';
 import React from 'react';
-import '../../tencentAdPutIn/index.less'
-import Group from './group';
+import '../../../tencentAdPutIn/index.less'
+import Group from './components/group';
 import SelectAdAccount from '@/components/SelectAdAccount';
-import WechatTieUp from './wechatTieUp';
-import PageTieUp from './pageTieUp';
+import PageTieUp from './components/pageTieUp';
+import LandingPageTieUp from './components/landingPageTieUp';
+import TencentGroup from './components/tencentGroup';
 
 /**
  * 客服管理
@@ -34,8 +35,9 @@ const EnterpriseWechat: React.FC = () => {
         <Tabs
             items={[
                 { label: '客服组', key: '1', children: <Group adAccountId={adAccountId} /> },
-                { label: '企微与投放端关系', key: '2', children: <WechatTieUp adAccountId={adAccountId} /> },
+                { label: '腾讯客服组', key: '2', children: <TencentGroup adAccountId={adAccountId} /> },
                 { label: '官方落地页与客服组关系', key: '3', children: <PageTieUp adAccountId={adAccountId} /> },
+                { label: '原生落地页与客服组关系', key: '4', children: <LandingPageTieUp adAccountId={adAccountId} /> },
             ]}
             size="small"
         />

+ 0 - 0
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/nTree.less → src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/nTree.less


+ 0 - 0
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/nTree.tsx → src/pages/launchSystemV3/tencenTasset/corpWechat/csgroup/nTree.tsx


+ 21 - 38
src/pages/launchSystemV3/tencenTasset/corpWechat/index.tsx → src/pages/launchSystemV3/tencenTasset/corpWechat/manage/index.tsx

@@ -1,9 +1,9 @@
 import { useAjax } from "@/Hook/useAjax";
-import { delCorpWechatApi, getCorpWechatAllApi, getCorpWechatApi, getCorpWechatDetailApi } from "@/services/adqV3/global";
+import { getCorpWechatAllApi, getCorpWechatApi, getCorpWechatDetailApi } from "@/services/adqV3/global";
 import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
-import { Button, Card, Divider, Input, message, Select, Table, Typography } from "antd";
+import { Button, Card, Divider, Input, Select, Table, Typography } from "antd";
 import React, { useEffect, useState } from "react"
-import '../../tencentAdPutIn/index.less'
+import '../../../tencentAdPutIn/index.less'
 import Modify from "./modify";
 import columns from "./tableConfig";
 const { Text, Paragraph } = Typography;
@@ -18,56 +18,36 @@ const CorpWechat: React.FC = () => {
     const [queryForm, setQueryForm] = useState<{ pageNum: number, pageSize: number, wechatName?: string, wechatIdList?: string[] }>({ pageNum: 1, pageSize: 20 })
     const [queryFormNew, setQueryFormNew] = useState<{ pageNum: number, pageSize: number, wechatName?: string, wechatIdList?: string[] }>({ pageNum: 1, pageSize: 20 })
     const [visible, setVisible] = useState<boolean>(false)
+    const [editData, setEditData] = useState<any>()
 
     const getCorpWechat = useAjax((params) => getCorpWechatApi(params))
-    const delCorpWechat = useAjax((params) => delCorpWechatApi(params))
     /**********************************/
 
     useEffect(() => {
         getCorpWechat.run(queryFormNew)
     }, [queryFormNew])
 
-    const del = (id: number) => {
-        delCorpWechat.run(id).then(res => {
-            if (res) {
-                message.success('删除成功')
-                getCorpWechat.refresh()
-            }
-        })
+    const edit = (data: any) => {
+        setEditData({ corp: data.corpName + '&&' + data.localCorpId, tencentCorpId: data.tencentCorpId, agentId: data.agentId })
+        setVisible(true)
     }
 
     return <Card
         className="cardResetCss"
         title={<div className="flexStart" style={{ gap: 8 }}>
             <Input style={{ width: 200 }} placeholder="请输入企业微信名称" value={queryForm?.wechatName} allowClear onChange={(e) => setQueryForm({ ...queryForm, wechatName: e.target.value, pageNum: 1 })} />
-            <Input.TextArea
-                style={{ width: 300 }}
-                placeholder="请输入企业微信ID(多个逗号隔开)"
-                allowClear
-                rows={1}
-                onChange={(e) => {
-                    let value = e.target.value
-                    let arr: string[] = []
-                    if (value) {
-                        value = value.replace(/[,,\s]/g, ',')
-                        arr = value.split(',').filter((a: string) => a)
-                    }
-                    setQueryForm({ ...queryForm, wechatIdList: arr, pageNum: 1 })
-                }}
-            />
             <Button type="primary" icon={<SearchOutlined />} onClick={() => setQueryFormNew({ ...queryForm })}>搜索</Button>
             <Button type="primary" icon={<PlusOutlined />} onClick={() => { setVisible(true) }}>新增企业微信</Button>
         </div>}
     >
-
         <Table
-            columns={columns(del)}
+            columns={columns(edit)}
             dataSource={getCorpWechat.data?.records}
             size="small"
             loading={getCorpWechat?.loading}
             scroll={{ y: 600 }}
             bordered
-            rowKey={'id'}
+            rowKey={'tencentCorpId'}
             pagination={{
                 defaultPageSize: 20,
                 current: getCorpWechat.data?.current || 1,
@@ -84,12 +64,15 @@ const CorpWechat: React.FC = () => {
         {/* 新增修改 */}
         {visible && <Modify
             visible={visible}
+            value={editData}
             onClose={() => {
                 setVisible(false)
+                setEditData(undefined)
             }}
             onChange={() => {
                 setVisible(false)
                 getCorpWechat.refresh()
+                setEditData(undefined)
             }}
         />}
     </Card>
@@ -140,8 +123,8 @@ export const SelectCorpWechat: React.FC<{ value?: number, onChange?: (value?: nu
             value={value}
             onChange={(e) => onChange?.(e)}
         >
-            {getCorpWechatAll?.data?.map((item: { id: number, wechatId: string, wechatName: string }) => {
-                return <Select.Option value={item.id} key={item.id} name={item.wechatName + `(${item.wechatId})`}><Text strong>{item.wechatName}</Text><Text type="secondary">{`(${item.wechatId})`}</Text></Select.Option>
+            {getCorpWechatAll?.data?.map((item: { tencentCorpId: string, corpName: string }) => {
+                return <Select.Option value={item.tencentCorpId} key={item.tencentCorpId} name={item.corpName + `(${item.tencentCorpId})`}><Text strong>{item.corpName}</Text><Text type="secondary">{`(${item.tencentCorpId})`}</Text></Select.Option>
             })}
         </Select>
         {/* 新增修改 */}
@@ -203,8 +186,8 @@ export const SelectCorpWechatCorpId: React.FC<{ value?: string[], onChange?: (va
             value={value}
             onChange={(e) => onChange?.(e)}
         >
-            {getCorpWechatAll?.data?.map((item: { id: number, wechatId: string, wechatName: string }) => {
-                return <Select.Option value={item.wechatId} disabled={value && value?.length > 0 && !value.includes(item.wechatId)} key={item.id} name={item.wechatName + `(${item.wechatId})`}><Text strong>{item.wechatName}</Text><Text type="secondary">{`(${item.wechatId})`}</Text></Select.Option>
+            {getCorpWechatAll?.data?.map((item: { tencentCorpId: string, corpName: string }) => {
+                return <Select.Option value={item.tencentCorpId} disabled={value && value?.length > 0 && !value.includes(item.tencentCorpId)} key={item.tencentCorpId} name={item.corpName + `(${item.tencentCorpId})`}><Text strong>{item.corpName}</Text><Text type="secondary">{`(${item.tencentCorpId})`}</Text></Select.Option>
             })}
         </Select>
         {/* 新增修改 */}
@@ -227,7 +210,7 @@ export const SelectCorpWechatCorpId: React.FC<{ value?: string[], onChange?: (va
  * @param param0 
  * @returns 
  */
-export const ShowCorpWechatDetail: React.FC<{ id: number }> = ({ id }) => {
+export const ShowCorpWechatDetail: React.FC<{ tencentCorpId: string }> = ({ tencentCorpId }) => {
 
     /*******************************/
     const getCorpWechatDetail = useAjax((params) => getCorpWechatDetailApi(params))
@@ -235,15 +218,15 @@ export const ShowCorpWechatDetail: React.FC<{ id: number }> = ({ id }) => {
 
     // 获取列表
     useEffect(() => {
-        if (id) {
-            getCorpWechatDetail.run(id)
+        if (tencentCorpId) {
+            getCorpWechatDetail.run({tencentCorpId})
         }
-    }, [id])
+    }, [tencentCorpId])
 
     return <Paragraph style={{ fontSize: 12, wordBreak: 'break-all', marginBottom: 0 }} ellipsis={{ rows: 2 }}>
         {getCorpWechatDetail.data ? <>
             <Text strong>企业微信:{getCorpWechatDetail.data?.wechatName}</Text><Text type="secondary">{`(${getCorpWechatDetail.data?.wechatId}})`}</Text>
-        </> : id}
+        </> : tencentCorpId}
     </Paragraph>
 }
 

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

@@ -0,0 +1,103 @@
+import { useAjax } from "@/Hook/useAjax"
+import { addCorpWechatApi, getLocalCorpListApi, modifyCorpWechatApi } from "@/services/adqV3/global"
+import { Form, Input, message, Modal, Select } from "antd"
+import React, { useEffect } from "react"
+import '../../../tencentAdPutIn/index.less'
+
+
+interface Props {
+    value?: { agentId: number, corpName: string, localCorpId: string, tencentCorpId: string }
+    visible?: boolean
+    onChange?: () => void
+    onClose?: () => void
+}
+
+/**
+ * 新增企业微信
+ * @returns 
+ */
+const Modify: React.FC<Props> = ({ value, visible, onChange, onClose }) => {
+
+    /********************************/
+    const [form] = Form.useForm()
+    const corp = Form.useWatch('corp', form)
+    const addCorpWechat = useAjax((params) => addCorpWechatApi(params))
+    const modifyCorpWechat = useAjax((params) => modifyCorpWechatApi(params))
+    const getLocalCorpList = useAjax(() => getLocalCorpListApi())
+    /********************************/
+
+    useEffect(() => {
+        getLocalCorpList.run()
+    }, [])
+
+    const handleOk = () => {
+        form.validateFields().then(valid => {
+            console.log(valid)
+            const { corp, ...params } = valid
+            const [corpName, localCorpId] = corp.split('&&')
+
+            if (value && Object.keys(value).length) {
+                modifyCorpWechat.run({ ...params, corpName, localCorpId }).then(res => {
+                    if (res) {
+                        message.success('修改成功')
+                        onChange?.()
+                    }
+                })
+            } else {
+                addCorpWechat.run({ ...params, corpName, localCorpId }).then(res => {
+                    if (res) {
+                        message.success('新增成功')
+                        onChange?.()
+                    }
+                })
+            }
+        })
+    }
+
+    return <Modal
+        title={<strong>{`新增企业微信`}</strong>}
+        open={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        className="modalResetCss"
+        confirmLoading={addCorpWechat.loading}
+    >
+        <Form
+            name="basicMiniProgramWechat"
+            form={form}
+            layout='vertical'
+            autoComplete="off"
+            initialValues={value}
+        >
+            <Form.Item label={<strong>企业微信</strong>} name="corp" rules={[{ required: true, message: '请输入企业微信名称!' }]}>
+                <Select
+                    placeholder='请选择企业微信'
+                    allowClear
+                    options={getLocalCorpList?.data?.map((item: { corpName: string; corpId: string }) => ({ label: item.corpName, value: item.corpName + '&&' + item.corpId }))}
+                />
+            </Form.Item>
+            {corp && <>
+                <Form.Item
+                    label={<strong>企业微信ID(广告创建使用)</strong>}
+                    name="tencentCorpId"
+                    rules={[
+                        { required: true, message: '请输入企业微信ID!' }
+                    ]}
+                >
+                    <Input placeholder="请输入企业微信ID" allowClear disabled={value && Object.keys(value).length > 0} />
+                </Form.Item>
+                <Form.Item
+                    label={<strong>腾讯广告第三方企微应用ID</strong>}
+                    name="agentId"
+                    rules={[
+                        { required: true, message: '请输入腾讯广告第三方企微应用ID!' }
+                    ]}
+                >
+                    <Input placeholder="请输入腾讯广告第三方企微应用ID" allowClear />
+                </Form.Item>
+            </>}
+        </Form>
+    </Modal>
+}
+
+export default React.memo(Modify)

+ 27 - 29
src/pages/launchSystemV3/tencenTasset/corpWechat/tableConfig.tsx → src/pages/launchSystemV3/tencenTasset/corpWechat/manage/tableConfig.tsx

@@ -1,9 +1,9 @@
 import { copy } from "@/utils/utils"
-import { Space, Popconfirm, TableProps } from "antd"
+import { TableProps } from "antd"
 import React from "react"
 
 
-const columns = (del: (id: number) => void): TableProps<any>['columns'] => {
+const columns = (edit: (data: any) => void): TableProps<any>['columns'] => {
 
 
     const data: TableProps<any>['columns'] = [
@@ -14,22 +14,13 @@ const columns = (del: (id: number) => void): TableProps<any>['columns'] => {
             align: 'center',
             width: 70,
             render: (_, b) => {
-                return <Space>
-                    <Popconfirm
-                        title="确定删除?"
-                        onConfirm={() => del(b.id)}
-                        okText="是"
-                        cancelText="否"
-                    >
-                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
-                    </Popconfirm>
-                </Space>
+                return <a style={{ fontSize: 12 }} onClick={() => edit(b)}>修改</a>
             }
         },
         {
             title: '企业微信名称',
-            dataIndex: 'wechatName',
-            key: 'wechatName',
+            dataIndex: 'corpName',
+            key: 'corpName',
             ellipsis: true,
             width: 180,
             render: (a) => {
@@ -37,34 +28,30 @@ const columns = (del: (id: number) => void): TableProps<any>['columns'] => {
             }
         },
         {
-            title: '企业微信ID',
-            dataIndex: 'wechatId',
-            key: 'wechatId',
+            title: '投放端企业微信ID',
+            dataIndex: 'tencentCorpId',
+            key: 'tencentCorpId',
             ellipsis: true,
             render: (a) => {
                 return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a}</a>
             }
         },
         {
-            title: '创建人',
-            dataIndex: 'createByName',
-            key: 'createByName',
-            align: 'center',
-            width: 75,
+            title: '企微端企业微信ID',
+            dataIndex: 'localCorpId',
+            key: 'localCorpId',
             ellipsis: true,
             render: (a) => {
-                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+                return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a}</a>
             }
         },
         {
-            title: '更新人',
-            dataIndex: 'createByName',
-            key: 'createByName',
-            align: 'center',
-            width: 75,
+            title: '腾讯广告第三方企微应用ID',
+            dataIndex: 'agentId',
+            key: 'agentId',
             ellipsis: true,
             render: (a) => {
-                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+                return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a}</a>
             }
         },
         {
@@ -77,6 +64,17 @@ const columns = (del: (id: number) => void): TableProps<any>['columns'] => {
             render: (a) => {
                 return <span style={{ fontSize: "12px" }}>{a}</span>
             }
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            align: 'center',
+            width: 140,
+            ellipsis: true,
+            render: (a) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
         }
     ]
 

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

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

+ 113 - 0
src/pages/launchSystemV3/tencenTasset/corpWechat/userRotatePolicy/addUserPotatePolicy.tsx

@@ -0,0 +1,113 @@
+import React from 'react';
+import { Modal, Form, Input, InputNumber, message } from 'antd';
+import '../../../tencentAdPutIn/index.less'
+import { useAjax } from '@/Hook/useAjax';
+import { addUserRotatePolicyApi, modifyUserRotatePolicyApi } from '@/services/adqV3/global';
+
+interface AddUserRotatePolicyProps {
+    value?: any;
+    visible?: boolean;
+    onClose?: () => void;
+    onChange?: () => void;
+}
+
+const AddUserRotatePolicy: React.FC<AddUserRotatePolicyProps> = ({ value, visible, onClose, onChange }) => {
+
+    /**************************************/
+    const [form] = Form.useForm();
+
+    const addUserRotatePolicy = useAjax((params) => addUserRotatePolicyApi(params))
+    const modifyUserRotatePolicy = useAjax((params) => modifyUserRotatePolicyApi(params))
+    /**************************************/
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+
+            if (value?.id) {
+                modifyUserRotatePolicy.run({ ...values, id: value?.id }).then(res => {
+                    if (res) {
+                        message.success('修改成功')
+                        onChange?.()
+                    }
+                })
+            } else {
+                addUserRotatePolicy.run(values).then((res) => {
+                    if (res) {
+                        message.success('新增成功')
+                        onChange?.()
+                    }
+                })
+            }
+        })
+    }
+
+    return <Modal
+        title={<strong>{(value && Object.keys(value).length > 0) ? '修改' : '新增'}客服号轮换策略</strong>}
+        open={visible}
+        onOk={handleOk}
+        onCancel={onClose}
+        className='modalResetCss'
+        confirmLoading={addUserRotatePolicy.loading}
+        width={600}
+    >
+        <Form
+            form={form}
+            name='basicUserRotatePolicy'
+            autoComplete="off"
+            colon={false}
+            layout='vertical'
+            initialValues={value}
+        >
+            <Form.Item label={<strong>策略名称</strong>} name="policyName" rules={[{ required: true, message: '请输入策略名称!' }]}>
+                <Input placeholder='请输入策略名称' />
+            </Form.Item>
+            <Form.Item
+                label={<strong>{`加粉成本的计算周期(/分钟,必须 >= 20,且必须是 10 的整数倍数)`}</strong>}
+                name="addCostCalculationCycle"
+                rules={[{
+                    pattern: /^(|[1-9]\d{2,}|[2-9]0)$/, // 空值或≥20且为10的倍数
+                    message: '必须≥20且为10的整数倍',
+                }]}
+            >
+                <InputNumber style={{ width: '100%' }} placeholder='请输入加粉成本的计算周期' />
+            </Form.Item>
+            <Form.Item
+                label={<strong>{`加粉成本异常值 (单位:/分)`}</strong>}
+                name="addCostException"
+            >
+                <InputNumber style={{ width: '100%' }} placeholder='请输入加粉成本异常值' />
+            </Form.Item>
+            <Form.Item
+                label={<strong>单号单日最小加粉数量,默认20,加粉数少于该值永远不触发风控</strong>}
+                name="minAddFansCount"
+            >
+                <InputNumber style={{ width: '100%' }} placeholder='请输入单号单日最小加粉数量' />
+            </Form.Item>
+            <Form.Item
+                label={<strong>单号单日最大加粉数量</strong>}
+                name="maxAddFansCount"
+            >
+                <InputNumber style={{ width: '100%' }} placeholder='请输入单号单日最大加粉数量' />
+            </Form.Item>
+            <Form.Item
+                label={<strong>组内客服号在线数量</strong>}
+                name="onlineCount"
+            >
+                <InputNumber style={{ width: '100%' }} placeholder='请输入组内客服号在线数量' />
+            </Form.Item>
+            <Form.Item
+                label={<strong>{`客服组内滚动客服号的周期(/分钟,必须 >= 10)`}</strong>}
+                name="rollingCustomerTime"
+                rules={[{
+                    type: 'number',
+                    min: 10,
+                    message: '输入值必须≥10'
+                }]}
+            >
+                <InputNumber style={{ width: '100%' }} placeholder='请输入加粉成本的计算周期' />
+            </Form.Item>
+        </Form>
+    </Modal>
+};
+
+export default React.memo(AddUserRotatePolicy);

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

@@ -0,0 +1,191 @@
+import { PlusOutlined, QuestionCircleFilled, SearchOutlined } from '@ant-design/icons';
+import { Button, Card, Input, Table, Tooltip } from 'antd';
+import React, { useEffect, useState } from 'react';
+import AddUserRotatePolicy from './addUserPotatePolicy';
+import { useAjax } from '@/Hook/useAjax';
+import { getUserRotatePolicyListApi } from '@/services/adqV3/global';
+import '../../../tencentAdPutIn/index.less'
+
+/**
+ * 客服号轮换策略
+ * @returns 
+ */
+const UserRotatePolicy: React.FC = () => {
+
+    /*********************************************/
+    const [queryParamsNew, setQueryParamsNew] = useState<{ pageNum: number, pageSize: number, policyName?: string }>({ pageNum: 1, pageSize: 20 })
+    const [queryParams, setQueryParams] = useState<{ pageNum: number, pageSize: number, policyName?: string }>({ pageNum: 1, pageSize: 20 })
+    const [visible, setVisible] = useState<boolean>(false);
+    const [editData, setEditData] = useState<any>();
+
+    const getUserRotatePolicyList = useAjax((params) => getUserRotatePolicyListApi(params))
+    /*********************************************/
+
+    useEffect(() => {
+        getUserRotatePolicyList.run(queryParamsNew)
+    }, [queryParamsNew]);
+
+
+    const editUR = (data: any) => {
+        setEditData(data)
+        setVisible(true)
+    }
+
+    return <Card
+        className="cardResetCss"
+    >
+        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
+            <Input placeholder='策略名称' style={{ width: 200 }} allowClear value={queryParams?.policyName} onChange={(e) => setQueryParams({ ...queryParams, policyName: e.target.value })} />
+            <Button type="primary" icon={<SearchOutlined />} loading={false} onClick={() => setQueryParamsNew({ ...queryParams, pageNum: 1 })}>搜索</Button>
+            <Button
+                type="primary"
+                icon={<PlusOutlined />}
+                onClick={() => {
+                    setVisible(true)
+                }}
+            >新增</Button>
+        </div>
+
+        <Table
+            columns={[
+                {
+                    title: '操作',
+                    dataIndex: 'cz',
+                    key: 'cz',
+                    width: 60,
+                    align: 'center',
+                    render(_, record) {
+                        return <a style={{ fontSize: 12 }} onClick={() => editUR(record)}>修改</a>
+                    },
+                },
+                {
+                    title: '策略名称',
+                    dataIndex: 'policyName',
+                    key: 'policyName',
+                    width: 110,
+                    ellipsis: true,
+                    align: 'center'
+                },
+                {
+                    title: <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+                        <span>加粉成本的计算周期</span>
+                        <Tooltip title="/分钟,必须 >= 20,且必须是 10 的整数倍数">
+                            <QuestionCircleFilled />
+                        </Tooltip>
+                    </div>,
+                    dataIndex: 'addCostCalculationCycle',
+                    key: 'addCostCalculationCycle',
+                    width: 90,
+                    align: 'center',
+                    render(value) {
+                        if (value && value === 0) return value
+                        return '--'
+                    },
+                },
+                {
+                    title: <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+                        <span>加粉成本异常值</span>
+                        <Tooltip title="单位:/分">
+                            <QuestionCircleFilled />
+                        </Tooltip>
+                    </div>,
+                    dataIndex: 'addCostException',
+                    key: 'addCostException',
+                    width: 90,
+                    align: 'center',
+                    render(value) {
+                        if (value || value === 0) return value
+                        return '--'
+                    },
+                },
+                {
+                    title: <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+                        <span>单号单日最小加粉数量</span>
+                        <Tooltip title="默认20,加粉数少于该值永远不触发风控">
+                            <QuestionCircleFilled />
+                        </Tooltip>
+                    </div>,
+                    dataIndex: 'minAddFansCount',
+                    key: 'minAddFansCount',
+                    width: 90,
+                    align: 'center',
+                    render(value) {
+                        if (value || value === 0) return value
+                        return '--'
+                    },
+                },
+                {
+                    title: '单号单日最大加粉数量',
+                    dataIndex: 'maxAddFansCount',
+                    key: 'maxAddFansCount',
+                    width: 90,
+                    align: 'center',
+                    render(value) {
+                        if (value || value === 0) return value
+                        return '--'
+                    },
+                },
+                {
+                    title: '组内客服号在线数量',
+                    dataIndex: 'onlineCount',
+                    key: 'onlineCount',
+                    width: 90,
+                    align: 'center',
+                    render(value) {
+                        if (value || value === 0) return value
+                        return '--'
+                    },
+                },
+                {
+                    title: <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+                        <span>客服组内滚动客服号的周期</span>
+                        <Tooltip title="/分钟,必须 >= 10">
+                            <QuestionCircleFilled />
+                        </Tooltip>
+                    </div>,
+                    dataIndex: 'rollingCustomerTime',
+                    key: 'rollingCustomerTime',
+                    width: 100,
+                    align: 'center',
+                    render(value) {
+                        if (value || value === 0) return value
+                        return '--'
+                    },
+                },
+            ]}
+            dataSource={getUserRotatePolicyList.data?.records}
+            size="small"
+            loading={getUserRotatePolicyList?.loading}
+            scroll={{ y: 600, x: 1000 }}
+            rowKey={'id'}
+            pagination={{
+                total: getUserRotatePolicyList.data?.total,
+                defaultPageSize: 20,
+                current: getUserRotatePolicyList.data?.current,
+                pageSize: getUserRotatePolicyList.data?.size
+            }}
+            onChange={(pagination) => {
+                const { current, pageSize } = pagination
+                setQueryParamsNew({ ...queryParamsNew, pageNum: current as number, pageSize: pageSize as number || 20 })
+                setQueryParams({ ...queryParams, pageNum: current as number, pageSize: pageSize as number || 20 })
+            }}
+
+        />
+
+
+        {/* 新增修改客服号轮换策略 */}
+        {visible && <AddUserRotatePolicy
+            visible={visible}
+            value={editData}
+            onChange={() => {
+                setVisible(false)
+                getUserRotatePolicyList.refresh()
+            }}
+            onClose={() => {
+                setVisible(false)
+            }}
+        />}
+    </Card>
+};
+
+export default UserRotatePolicy;

+ 0 - 100
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/addPageTieUp.tsx

@@ -1,100 +0,0 @@
-import { useAjax } from '@/Hook/useAjax';
-import { addPageCustomerGroupApi, getCustomerServiceGroupListApi } from '@/services/adqV3/global';
-import { Form, Modal, Select } from 'antd';
-import React, { useEffect, useState } from 'react';
-import '../../tencentAdPutIn/index.less'
-import SelectPage from './selectPage';
-
-interface Props {
-    adAccountId: number
-    corpList?: { label: string, value: string }[]
-    visible?: boolean
-    onChange?: () => void
-    onClose?: () => void
-}
-
-/**
- * 新增官方落地页与客服组关系
- * @param param0 
- * @returns 
- */
-const AddPageTieUp: React.FC<Props> = ({ adAccountId, corpList, visible, onChange, onClose }) => {
-
-    /*******************************************/
-    const [form] = Form.useForm();
-    const tencentCorpId = Form.useWatch('tencentCorpId', form)
-    const [groupList, setGroupList] = useState<{ label: string, value: number }[]>([])
-
-    const addPageCustomerGroup = useAjax((params) => addPageCustomerGroupApi(params))
-    const getCustomerServiceGroupList = useAjax((params) => getCustomerServiceGroupListApi(params))
-    // const getCorpListAll = useAjax(() => getCorpListAllApi())
-    /*******************************************/
-
-    /** 客服组 */
-    useEffect(() => {
-        if (adAccountId && tencentCorpId) {
-            getCustomerServiceGroupList.run({ adAccountId, pageNum: 1, pageSize: 100, tencentCorpId }).then(res => {
-                setGroupList(res?.records?.map((item: { groupName: string; groupId: number; }) => ({ label: item.groupName, value: item.groupId + '&&' + item.groupName })))
-            }).catch(() => setGroupList([]))
-        }
-    }, [adAccountId, tencentCorpId])
-
-    const handleOk = () => {
-        form.validateFields().then(values => {
-            const { customerGroup, page, ...params } = values
-            const [customerGroupId, customerGroupName] = customerGroup.split('&&')
-            const { pageId, pageName } = page
-            addPageCustomerGroup.run({ adAccountId, customerGroupName, customerGroupId, pageId, pageName, ...params }).then((res) => {
-                if (res)
-                    onChange?.()
-            })
-        })
-    };
-
-    return <Modal
-        title={<strong>新增官方落地页与客服组关系</strong>}
-        open={visible}
-        onOk={handleOk}
-        onCancel={onClose}
-        className='modalResetCss'
-        confirmLoading={addPageCustomerGroup.loading}
-    >
-        <Form
-            form={form}
-            name='basicWechatTieUp'
-            autoComplete="off"
-            colon={false}
-            labelCol={{ span: 5 }}
-            labelAlign="left"
-        >
-            <Form.Item label={<strong>企微端企业</strong>} name="tencentCorpId" rules={[{ required: true, message: '请选择企业!' }]}>
-                <Select
-                    showSearch
-                    placeholder="请选择企业"
-                    filterOption={(input, option) =>
-                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                    }
-                    allowClear
-                    options={corpList}
-                />
-            </Form.Item>
-            {tencentCorpId && <Form.Item label={<strong>客服组</strong>} name="customerGroup" rules={[{ required: true, message: '请选择客服组!' }]}>
-                <Select
-                    showSearch
-                    placeholder="请选择客服组"
-                    filterOption={(input, option) =>
-                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                    }
-                    loading={getCustomerServiceGroupList.loading}
-                    allowClear
-                    options={groupList}
-                />
-            </Form.Item>}
-            <Form.Item label={<strong>官方落地页</strong>} name="page" rules={[{ required: true, message: '请选择官方落地页!' }]}>
-                <SelectPage accountId={adAccountId} />
-            </Form.Item>
-        </Form>
-    </Modal>;
-};
-
-export default React.memo(AddPageTieUp);

+ 0 - 77
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/addWechatTieUp.tsx

@@ -1,77 +0,0 @@
-import React, { useEffect } from 'react';
-import { Modal, Form, Input, Select } from 'antd';
-import '../../tencentAdPutIn/index.less'
-import { useAjax } from '@/Hook/useAjax';
-import { addCorpRelationListApi, getCorpListAllApi } from '@/services/adqV3/global';
-
-interface Props {
-    adAccountId: number
-    visible?: boolean
-    onChange?: () => void
-    onClose?: () => void
-}
-
-/**
- * 新增企微与投放端关系
- * @param param0 
- * @returns 
- */
-const AddWechatTieUp: React.FC<Props> = ({ adAccountId, visible, onChange, onClose }) => {
-
-    /*******************************************/
-    const [form] = Form.useForm();
-
-    const addCorpRelationList = useAjax((params) => addCorpRelationListApi(params))
-    const getCorpListAll = useAjax(() => getCorpListAllApi())
-    /*******************************************/
-
-    useEffect(() => {
-        getCorpListAll.run()
-    }, [])
-
-    const handleOk = () => {
-        form.validateFields().then(values => {
-            const { corp, ...params } = values
-            const [corpName, corpId] = corp.split('&&')
-            addCorpRelationList.run({ adAccountId, corpName, corpId, ...params }).then((res) => {
-                if (res)
-                    onChange?.()
-            })
-        })
-    };
-
-    return <Modal
-        title={<strong>新增企微与投放端关系</strong>}
-        open={visible}
-        onOk={handleOk}
-        onCancel={onClose}
-        className='modalResetCss'
-        confirmLoading={addCorpRelationList.loading}
-    >
-        <Form
-            form={form}
-            name='basicWechatTieUp'
-            autoComplete="off"
-            colon={false}
-            labelCol={{ span: 5 }}
-            labelAlign="left"
-        >
-            <Form.Item label={<strong>企微端企业</strong>} name="corp" rules={[{ required: true, message: '请输入企微端企业!' }]}>
-                <Select
-                    showSearch
-                    placeholder="请选择企微端企业"
-                    filterOption={(input, option) =>
-                        ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                    }
-                    allowClear
-                    options={getCorpListAll?.data?.map((item: { corpName: string; corpId: string; }) => ({ label: item.corpName, value: item.corpName + '&&' + item.corpId }))}
-                />
-            </Form.Item>
-            <Form.Item label={<strong>投放端企业ID</strong>} name="tencentCorpId" rules={[{ required: true, message: '请输入投放端企业ID!' }]}>
-                <Input placeholder='请输入投放端企业ID' />
-            </Form.Item>
-        </Form>
-    </Modal>;
-};
-
-export default React.memo(AddWechatTieUp);

+ 0 - 89
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/group.tsx

@@ -1,89 +0,0 @@
-import { Button, Select, Table } from 'antd';
-import React, { useEffect, useState } from 'react';
-import '../../tencentAdPutIn/index.less'
-import { useAjax } from '@/Hook/useAjax';
-import { getCorpRelationAllApi, getCustomerServiceGroupListApi } from '@/services/adqV3/global';
-import { SearchOutlined } from '@ant-design/icons';
-import columns from './tableConfig';
-import SettingsEnterprise from './settingsEnterprise';
-
-const Group: React.FC<{ adAccountId?: number }> = ({ adAccountId }) => {
-
-    /**********************************/
-    const [queryParamsNew, setQueryParamsNew] = useState<{ adAccountId: number, pageNum: number, pageSize: number, tencentCorpId?: string }>({ pageNum: 1, pageSize: 20, adAccountId: adAccountId || 0 })
-    const [settingsData, setSettingsData] = useState<{ visible: boolean, data: any }>({ visible: false, data: {} })
-
-    const getCustomerServiceGroupList = useAjax((params) => getCustomerServiceGroupListApi(params))
-    const getCorpRelationAll = useAjax((params) => getCorpRelationAllApi(params))
-    /**********************************/
-
-    useEffect(() => {
-        if (adAccountId) {
-            getCorpRelationAll.run({ adAccountId })
-        }
-    }, [adAccountId])
-
-    useEffect(() => {
-        if (adAccountId) {
-            getCustomerServiceGroupList.run({ ...queryParamsNew, adAccountId })
-        }
-    }, [queryParamsNew, adAccountId])
-
-    const handleEdit = (data: any) => {
-        setSettingsData({ visible: true, data })
-        // setQueryParamsNew({ ...queryParamsNew, tencentCorpId: data.tencentCorpId })
-    }
-
-    return <>
-        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
-            <Select
-                showSearch
-                placeholder="请选择企业"
-                filterOption={(input, option) =>
-                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                }
-                style={{ width: 200 }}
-                allowClear
-                value={queryParamsNew?.tencentCorpId}
-                loading={getCorpRelationAll.loading}
-                onChange={(e) => {
-                    setQueryParamsNew({ ...queryParamsNew, tencentCorpId: e, pageNum: 1 })
-                }}
-                options={getCorpRelationAll?.data?.map((item: { corpName: string; tencentCorpId: string; }) => ({ label: item.corpName, value: item.tencentCorpId }))}
-            />
-            <Button type="primary" icon={<SearchOutlined />} loading={getCustomerServiceGroupList.loading} onClick={() => getCustomerServiceGroupList.refresh()}>刷新</Button>
-        </div>
-        <Table
-            columns={columns(handleEdit)}
-            dataSource={getCustomerServiceGroupList.data?.records}
-            size="small"
-            loading={getCustomerServiceGroupList?.loading}
-            scroll={{ y: 600, x: 1000 }}
-            rowKey={'groupId'}
-            pagination={{
-                total: getCustomerServiceGroupList.data?.total,
-                defaultPageSize: 20,
-                current: getCustomerServiceGroupList.data?.current,
-                pageSize: getCustomerServiceGroupList.data?.size
-            }}
-            onChange={(pagination) => {
-                const { current, pageSize } = pagination
-                setQueryParamsNew({ ...queryParamsNew, pageNum: current as number, pageSize: pageSize as number || 20 })
-            }}
-
-        />
-
-        {/* 设置修改 */}
-        {settingsData.visible && <SettingsEnterprise
-            {...settingsData}
-            adAccountId={adAccountId as number}
-            onClose={() => setSettingsData({ visible: false, data: {} })}
-            onChange={() => {
-                getCustomerServiceGroupList.refresh()
-                setSettingsData({ visible: false, data: {} })
-            }}
-        />}
-    </>
-};
-
-export default React.memo(Group);

+ 0 - 257
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/pageTieUp.tsx

@@ -1,257 +0,0 @@
-import { useAjax } from '@/Hook/useAjax';
-import { delPageCustomerGroupApi, getAdqLandingPageOfficialListApi, getCorpRelationAllApi, getCustomerServiceGroupListApi, getPageCustomerGroupListApi, GetPageCustomerGroupListProps } from '@/services/adqV3/global';
-import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
-import { Button, Input, message, Popconfirm, Select, Table } from 'antd';
-import React, { useEffect, useState } from 'react';
-import AddPageTieUp from './addPageTieUp';
-import { useDebounce } from 'ahooks';
-
-/**
- * 落地页与客服组关系
- * @returns 
- */
-const PageTieUp: React.FC<{ adAccountId?: number }> = ({ adAccountId }) => {
-
-    /*******************************/
-    const [queryParamsNew, setQueryParamsNew] = useState<GetPageCustomerGroupListProps>({ pageNum: 1, pageSize: 50, adAccountId: 0 })
-    const [visible, setVisible] = useState<boolean>(false)
-    const [corpList, setCorpList] = useState<{ label: string, value: string }[]>([])
-    const [groupList, setGroupList] = useState<{ label: string, value: number }[]>([])
-    const [queryForm, setQueryForm] = useState<{ accountId?: number, pageName?: string, ownerUid?: number, pageSize: number, pageNum: number, isSqDownPage: boolean }>({ pageNum: 1, pageSize: 20, isSqDownPage: false })
-    const [pageName, setPageName] = useState<string>();
-    const debouncedValue = useDebounce(pageName, { wait: 500 });
-
-    const getPageCustomerGroupList = useAjax((params) => getPageCustomerGroupListApi(params))
-    const delPageCustomerGroup = useAjax((params) => delPageCustomerGroupApi(params))
-    const getCorpRelationAll = useAjax((params) => getCorpRelationAllApi(params))
-    const getCustomerServiceGroupList = useAjax((params) => getCustomerServiceGroupListApi(params))
-    const listAjax = useAjax((params) => getAdqLandingPageOfficialListApi(params))
-    /*******************************/
-
-    // 落地页
-    useEffect(() => {
-        if (adAccountId) {
-            const params: any = {
-                ...queryForm,
-                // pageStatus: 'NORMAL',
-                pageType: 'PAGE_TYPE_OFFICIAL',
-                accountId: adAccountId,
-                pageName: debouncedValue
-            }
-            delete params.isSqDownPage
-            listAjax.run(params)
-        }
-    }, [adAccountId, queryForm, debouncedValue])
-
-    /** 客服组 */
-    useEffect(() => {
-        if (adAccountId) {
-            getCustomerServiceGroupList.run({ adAccountId, pageNum: 1, pageSize: 100, tencentCorpId: queryParamsNew?.tencentCorpId }).then(res => {
-                setGroupList(res?.records?.map((item: { groupName: string; groupId: number; }) => ({ label: item.groupName, value: item.groupId })))
-            }).catch(() => setGroupList([]))
-        }
-    }, [adAccountId, queryParamsNew?.tencentCorpId])
-
-    /** 企业列表 */
-    useEffect(() => {
-        if (adAccountId) {
-            getCorpRelationAll.run({ adAccountId }).then(res => {
-                setCorpList(res?.map((item: { corpName: string; tencentCorpId: string; }) => ({ label: item.corpName, value: item.tencentCorpId })))
-            }).catch(() => setCorpList([]))
-        }
-    }, [adAccountId])
-
-    /** 获取关系列表 */
-    useEffect(() => {
-        if (adAccountId) {
-            getPageCustomerGroupList.run({ ...queryParamsNew, adAccountId })
-        }
-    }, [adAccountId, queryParamsNew])
-
-
-    /**
-     * 删除
-     * @param id 
-     */
-    const handleDel = (id: number) => {
-        delPageCustomerGroup.run(id).then((res) => {
-            if (res) {
-                message.success('删除成功')
-                getPageCustomerGroupList.refresh()
-            }
-        })
-    }
-
-
-    return <>
-        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
-            <Select
-                showSearch
-                placeholder="请选择企业"
-                filterOption={(input, option) =>
-                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                }
-                style={{ width: 200 }}
-                allowClear
-                value={queryParamsNew?.tencentCorpId}
-                loading={getCorpRelationAll.loading}
-                onChange={(e) => {
-                    setQueryParamsNew({ ...queryParamsNew, tencentCorpId: e, pageNum: 1 })
-                }}
-                options={corpList}
-            />
-            <Select
-                showSearch
-                placeholder="请选择客服组"
-                filterOption={(input, option) =>
-                    ((option?.label ?? '') as any).toLowerCase().includes(input.toLowerCase())
-                }
-                style={{ width: 200 }}
-                allowClear
-                value={queryParamsNew?.customerGroupId}
-                loading={getCustomerServiceGroupList.loading}
-                onChange={(e) => {
-                    setQueryParamsNew({ ...queryParamsNew, customerGroupId: e, pageNum: 1 })
-                }}
-                options={groupList}
-            />
-            <Input style={{ width: 140 }} value={queryParamsNew?.customerGroupName} onChange={(e) => setQueryParamsNew({ ...queryParamsNew, pageNum: 1, customerGroupName: e.target.value })} placeholder='客服组名称' />
-            <Select
-                showSearch
-                placeholder="请选择落地页"
-                filterOption={false}
-                style={{ width: 200 }}
-                allowClear
-                value={queryParamsNew?.pageId}
-                loading={listAjax.loading}
-                onChange={(e) => {
-                    setQueryParamsNew({ ...queryParamsNew, pageId: e, pageNum: 1 })
-                }}
-                defaultActiveFirstOption={false}
-                showArrow={false}
-                onSearch={(newValue: string) => {
-                    setPageName(newValue)
-                }}
-                options={listAjax?.data?.records?.map((item: { pageId: number; pageName: string; }) => ({ label: item.pageName, value: item.pageId }))}
-            />
-            <Button type="primary" icon={<SearchOutlined />} loading={getPageCustomerGroupList.loading} onClick={() => getPageCustomerGroupList.refresh()}>刷新</Button>
-            <Button
-                type="primary"
-                icon={<PlusOutlined />}
-                onClick={() => {
-                    setVisible(true)
-                }}
-            >新增</Button>
-        </div>
-
-        <Table
-            columns={[
-                {
-                    title: '广告账号',
-                    dataIndex: 'adAccountId',
-                    key: 'adAccountId',
-                    width: 90,
-                    ellipsis: true,
-                    align: 'center',
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '落地页名称',
-                    dataIndex: 'pageName',
-                    key: 'pageName',
-                    width: 120,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '落地页ID',
-                    dataIndex: 'pageId',
-                    key: 'pageId',
-                    width: 100,
-                    ellipsis: true,
-                    align: 'center',
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '客服组名称',
-                    dataIndex: 'customerGroupName',
-                    key: 'customerGroupName',
-                    width: 120,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '客服组ID',
-                    dataIndex: 'customerGroupId',
-                    key: 'customerGroupId',
-                    align: 'center',
-                    width: 100,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '投放端企业ID',
-                    dataIndex: 'tencentCorpId',
-                    key: 'tencentCorpId',
-                    width: 300,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '操作',
-                    dataIndex: 'cz',
-                    key: 'cz',
-                    width: 300,
-                    render(_, record) {
-                        return <Popconfirm
-                            title="确定删除?"
-                            onConfirm={() => handleDel(record.id)}
-                        >
-                            <a style={{ color: 'red', fontSize: 12 }}>删除</a>
-                        </Popconfirm>
-                    },
-                },
-            ]}
-            dataSource={getPageCustomerGroupList.data?.records || []}
-            loading={getPageCustomerGroupList.loading}
-            scroll={{ x: 1000 }}
-            pagination={{
-                current: queryParamsNew.pageNum,
-                pageSize: queryParamsNew.pageSize,
-                total: getPageCustomerGroupList.data?.total,
-                onChange: (pageNum, pageSize) => setQueryParamsNew({ ...queryParamsNew, pageNum, pageSize }),
-            }}
-            size="small"
-            bordered
-            rowKey="id"
-        />
-
-        {/* 新增 */}
-        {visible && <AddPageTieUp
-            adAccountId={adAccountId as number}
-            corpList={corpList}
-            visible={visible}
-            onChange={() => {
-                getPageCustomerGroupList.refresh()
-                setVisible(false)
-            }}
-            onClose={() => {
-                setVisible(false)
-            }}
-        />}
-    </>;
-};
-
-export default React.memo(PageTieUp);

+ 0 - 120
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/selectPage.tsx

@@ -1,120 +0,0 @@
-import { Badge, Button, Input, Modal, Space, Table, Tag, Typography } from 'antd';
-import React, { useEffect, useState } from 'react';
-import '../../tencentAdPutIn/index.less'
-import { getAdqLandingPageOfficialListApi } from '@/services/adqV3/global';
-import { useAjax } from '@/Hook/useAjax';
-import { SyncOutlined } from '@ant-design/icons';
-import { PageStatusEnum } from '@/services/launchAdq/enum';
-const { Text } = Typography;
-
-/**
- * 选择落地页
- * @returns 
- */
-const SelectPage: React.FC<{ accountId: number, value?: any, onChange?: (e: any) => void }> = ({ accountId, value, onChange }) => {
-
-    /***********************************/
-    const [visible, setVisible] = useState<boolean>(false)
-    const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>([])
-    const [queryForm, setQueryForm] = useState<{ accountId?: number, pageName?: string, ownerUid?: number, pageSize: number, pageNum: number, isSqDownPage: boolean }>({ pageNum: 1, pageSize: 20, isSqDownPage: false })
-    const listAjax = useAjax((params) => getAdqLandingPageOfficialListApi(params))
-    /***********************************/
-
-    useEffect(() => {
-        if (accountId) {
-            const params: any = {
-                ...queryForm,
-                // pageStatus: 'NORMAL',
-                pageType: 'PAGE_TYPE_OFFICIAL',
-                accountId
-            }
-            delete params.isSqDownPage
-            listAjax.run(params)
-        }
-    }, [accountId, queryForm])
-
-    const handleOk = () => {
-        onChange?.(selectedRowKeys?.[0])
-        setVisible(false)
-    }
-
-    return <>
-        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
-            {value && <div style={{ flex: 1, overflow: 'hidden' }}><Text ellipsis>{Array.isArray(value) ? value.map((item: { pageName: any; pageId: any; }) => `${item.pageName}(${item.pageId})`) : `${value?.pageName}(${value?.pageId})`}</Text></div>}
-            <Button type="primary" onClick={() => {
-                setVisible(true)
-                if (value) setSelectedRowKeys(Array.isArray(value) ? value : [value])
-                else setSelectedRowKeys([])
-            }}>选择官方落地页</Button>
-        </div>
-        {visible && <Modal
-            title={<strong>官方落地页</strong>}
-            open={visible}
-            onOk={handleOk}
-            onCancel={() => {
-                setVisible(false)
-            }}
-            className='modalResetCss'
-            width={750}
-        >
-            <Space style={{ marginBottom: 10 }}>
-                <Input value={queryForm?.pageName} style={{ width: 150 }} allowClear placeholder='请输入落地页名称' onChange={(e) => setQueryForm({ ...queryForm, pageNum: 1, pageName: e.target.value })} />
-                <Button style={{ padding: 0, margin: 0 }} icon={<SyncOutlined />} type='link' loading={listAjax?.loading} onClick={() => { listAjax?.refresh() }}><span style={{ fontSize: 12 }}>刷新</span></Button>
-            </Space>
-
-            <Table
-                columns={[
-                    {
-                        title: '落地页ID',
-                        dataIndex: 'pageId',
-                        key: 'pageId',
-                        align: 'center',
-                        width: 85
-                    },
-                    {
-                        title: '落地页名称',
-                        dataIndex: 'pageName',
-                        key: 'pageName',
-                        ellipsis: true,
-                        width: 300
-                    },
-                    {
-                        title: '落地页状态',
-                        dataIndex: 'pageStatus',
-                        key: 'pageStatus',
-                        align: 'center',
-                        width: 90,
-                        render: (a: string | number) => {
-                            return <Badge status={a === 'NORMAL' ? "success" : a === 'DELETED' ? "error" : 'processing'} text={PageStatusEnum[a as keyof typeof PageStatusEnum]} />
-                        }
-                    },
-                ]}
-                dataSource={listAjax?.data?.records}
-                size="small"
-                loading={listAjax?.loading}
-                scroll={{ y: 400 }}
-                bordered
-                rowKey={'pageId'}
-                pagination={{
-                    total: listAjax?.data?.total,
-                    defaultPageSize: 20,
-                    current: listAjax?.data?.current,
-                    pageSize: listAjax?.data?.size
-                }}
-                onChange={(pagination) => {
-                    const { current, pageSize } = pagination
-                    setQueryForm({ ...queryForm, pageNum: current as number, pageSize: pageSize as number || 20 })
-                }}
-                rowSelection={{
-                    selectedRowKeys: selectedRowKeys?.map((item: any) => item?.pageId),
-                    type: 'radio',
-                    onChange(selectedRowKeys, selectedRows, info) {
-                        setSelectedRowKeys(selectedRows)
-                    },
-                }}
-            />
-        </Modal>}
-    </>;
-};
-
-export default SelectPage;

+ 0 - 41
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/userInfo.tsx

@@ -1,41 +0,0 @@
-import { UnorderedListOutlined } from '@ant-design/icons';
-import { Button, Modal } from 'antd';
-import React from 'react';
-import '../../tencentAdPutIn/index.less';
-
-/**
- * UserInfo是一个React函数组件,用于展示用户信息页面
- * 该组件没有参数和返回值,它通过返回一个包含用户信息页面的JSX元素来工作
- * 选择使用函数组件而不是类组件是因为用户信息页面通常不涉及复杂的组件状态管理
- * 此外,通过使用函数组件,可以利用React的Hooks特性,进一步简化组件逻辑
- */
-const UserInfo: React.FC<{ createTime: string, groupMemberCnt: number, userInfoList: { userId: string, userName: string, msg: string, state: 0 | 1 }[] }> = ({ createTime, groupMemberCnt, userInfoList }) => {
-    // 返回一个包含用户信息页面布局的JSX元素
-    // 这里使用了简单的HTML元素来构建页面结构,包括一个标题和一段描述
-
-    /************************************/
-    const [visible, setVisible] = React.useState<boolean>(false);
-    /************************************/
-
-    return <>
-        <Button icon={<UnorderedListOutlined />} style={{ border: 'none', fontSize: 12 }} size='small' onClick={() => setVisible(true)}>详情</Button>
-        {visible && <Modal
-            title={<strong>客服组详情</strong>}
-            open={visible}
-            onCancel={() => setVisible(false)}
-            footer={null}
-            className='modalResetCss'
-        >
-            <p>创建时间:{createTime}</p>
-            <p>客服人数:{groupMemberCnt}</p>
-            <div style={{ display: 'flex' }}>
-                <p>已选客服:</p>
-                <div>{userInfoList.map(item => {
-                    return <div key={item.userId} style={item.state === 1 ? { color: 'red' } : {}}>{item.userName || item.msg + `(${item.userId})`}</div>
-                })}</div>
-            </div>
-        </Modal>}
-    </>;
-};
-
-export default React.memo(UserInfo);

+ 0 - 180
src/pages/launchSystemV3/tencenTasset/enterpriseWechat/wechatTieUp.tsx

@@ -1,180 +0,0 @@
-import { useAjax } from '@/Hook/useAjax';
-import { delCorpRelationApi, getCorpRelationListApi } from '@/services/adqV3/global';
-import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
-import { Button, message, Popconfirm, Table } from 'antd';
-import React, { useEffect, useState } from 'react';
-import AddWechatTieUp from './addWechatTieUp';
-
-
-/**
- * 企微关系链
- * @returns 
- */
-const WechatTieUp: React.FC<{ adAccountId?: number }> = ({ adAccountId }) => {
-
-    /**********************************/
-    const [queryParamsNew, setQueryParamsNew] = useState<{ pageNum: number, pageSize: number }>({ pageNum: 1, pageSize: 10 })
-    const [visible, setVisible] = useState<boolean>(false)
-
-    const getCorpRelationList = useAjax((params) => getCorpRelationListApi(params))
-    const delCorpRelation = useAjax((params) => delCorpRelationApi(params))
-    /**********************************/
-
-    useEffect(() => {
-        if (adAccountId) {
-            getCorpRelationList.run({ adAccountId, ...queryParamsNew })
-        }
-    }, [adAccountId, queryParamsNew]);
-
-    /**
-     * 删除
-     * @param id 
-     */
-    const handleDel = (id: number) => {
-        delCorpRelation.run(id).then((res) => {
-            if (res) {
-                message.success('删除成功')
-                getCorpRelationList.refresh()
-            }
-        })
-    }
-
-    return <>
-        <div className="flexStart" style={{ gap: 8, marginBottom: 16 }}>
-            <Button type="primary" icon={<SearchOutlined />} loading={getCorpRelationList.loading} onClick={() => getCorpRelationList.refresh()}>刷新</Button>
-            <Button
-                type="primary"
-                icon={<PlusOutlined />}
-                onClick={() => {
-                    setVisible(true)
-                }}
-            >新增</Button>
-        </div>
-
-        <Table
-            columns={[
-                {
-                    title: '企业名称',
-                    dataIndex: 'corpName',
-                    key: 'corpName',
-                    width: 150,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '企业ID',
-                    dataIndex: 'corpId',
-                    key: 'corpId',
-                    width: 300,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '投放端企业ID',
-                    dataIndex: 'tencentCorpId',
-                    key: 'tencentCorpId',
-                    width: 300,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '广告账户',
-                    dataIndex: 'adAccountId',
-                    key: 'adAccountId',
-                    align: 'center',
-                    width: 90,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '创建时间',
-                    dataIndex: 'createTime',
-                    key: 'createTime',
-                    width: 140,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '创建人',
-                    dataIndex: 'createByName',
-                    key: 'createByName',
-                    align: 'center',
-                    width: 80,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '更新时间',
-                    dataIndex: 'updateTime',
-                    key: 'updateTime',
-                    width: 140,
-                    ellipsis: true,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '更新人',
-                    dataIndex: 'updateByName',
-                    key: 'updateByName',
-                    align: 'center',
-                    width: 80,
-                    render(value) {
-                        return <span style={{ fontSize: 12 }}>{value}</span>
-                    },
-                },
-                {
-                    title: '操作',
-                    dataIndex: 'cz',
-                    key: 'cz',
-                    width: 300,
-                    render(_, record) {
-                        return <Popconfirm
-                            title="确定删除?"
-                            onConfirm={() => handleDel(record.id)}
-                        >
-                            <a style={{ color: 'red', fontSize: 12 }}>删除</a>
-                        </Popconfirm>
-                    },
-                },
-            ]}
-            dataSource={getCorpRelationList.data?.records || []}
-            loading={getCorpRelationList.loading}
-            scroll={{ x: 1000 }}
-            pagination={{
-                current: queryParamsNew.pageNum,
-                pageSize: queryParamsNew.pageSize,
-                total: getCorpRelationList.data?.total,
-                onChange: (pageNum, pageSize) => setQueryParamsNew({ pageNum, pageSize }),
-            }}
-            size="small"
-            bordered
-            rowKey="id"
-        />
-
-        {/* 新增 */}
-        {visible && <AddWechatTieUp
-            adAccountId={adAccountId as number}
-            visible={visible}
-            onChange={() => {
-                getCorpRelationList.refresh()
-                setVisible(false)
-            }}
-            onClose={() => {
-                setVisible(false)
-            }}
-        />}
-    </>;
-};
-
-export default React.memo(WechatTieUp);

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

@@ -9,7 +9,7 @@ import { getOptimizationGoalPermissionsV3Api } from "@/services/adqV3/global"
 import { adRules } from "../../rules"
 import { QuestionCircleFilled } from "@ant-design/icons"
 import { SelectMiniProgramWechat } from "@/pages/launchSystemV3/tencenTasset/miniProgramWechat"
-import { SelectCorpWechat } from "@/pages/launchSystemV3/tencenTasset/corpWechat"
+import { SelectCorpWechat } from "@/pages/launchSystemV3/tencenTasset/corpWechat/manage"
 import { SelectGameAppId } from "@/pages/launchSystemV3/tencenTasset/game"
 import { useRequest } from "ahooks"
 import { SelectApplication } from "@/pages/launchSystemV3/tencenTasset/application"
@@ -222,7 +222,7 @@ const AdgroupsMarketingContent: React.FC<{ accountIdList: number[], value?: any
 
         {marketingTargetType === 'MARKETING_TARGET_TYPE_MINI_PROGRAM_WECHAT' ? <Form.Item name='sysWechatAppId' rules={[{ required: true, message: '请选择微信小程序' }]}>
             <SelectMiniProgramWechat />
-        </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_WORK' ? <Form.Item name='sysCorpWechatId' rules={[{ required: true, message: '请选择企业微信' }]}>
+        </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_WORK' ? <Form.Item name='tencentCorpId' rules={[{ required: true, message: '请选择企业微信' }]}>
             <SelectCorpWechat />
         </Form.Item> : marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_MINI_GAME' ? <Form.Item name='wxGameAppId' rules={[{ required: true, message: '请选择微信小游戏' }]}>
             <SelectGameAppId gameType="WXGAME" />

+ 1 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/Target/addTarget.tsx

@@ -11,7 +11,7 @@ import moment from "moment"
 import { txtLength } from "@/utils/utils"
 import { addTargetingApi, checkTargetingApi, updateTargetingApi } from "@/services/adqV3"
 import { useModel } from "umi"
-import { SelectCorpWechatCorpId } from "@/pages/launchSystemV3/tencenTasset/corpWechat"
+import { SelectCorpWechatCorpId } from "@/pages/launchSystemV3/tencenTasset/corpWechat/manage"
 const { Title, Paragraph } = Typography;
 
 interface Props {

+ 1 - 1
src/pages/launchSystemV3/tencentAdPutIn/create/tableConfig.tsx

@@ -54,7 +54,7 @@ const columns = (): TableProps<any>['columns'] => {
                             </Space>
                         } else if (['MARKETING_TARGET_TYPE_WECHAT_WORK'].includes(b.adgroupsDto?.marketingAssetOuterSpec?.marketingTargetType)) {
                             return <Space size={0} direction="vertical">
-                                <Text style={{ fontSize: 12 }}>推广产品:企业微信(产品本地ID:{b?.adgroupsDto?.sysCorpWechatId})</Text>
+                                <Text style={{ fontSize: 12 }}>推广产品:企业微信(产品本地ID:{b?.adgroupsDto?.tencentCorpId})</Text>
                                 <Text style={{ fontSize: 12 }}>转化归因:{b?.userActionSetsList ? b?.userActionSetsList.map((item: { name: any; }) => item.name).toString() : b?.conversionList ? b?.conversionList.map((item: { conversionName: any; conversionId: any; }) => `${item?.conversionName}(${item.conversionId})`).toString() : '暂未配置'}</Text>
                             </Space>
                         } else if (['MARKETING_TARGET_TYPE_WECHAT_MINI_GAME'].includes(b.adgroupsDto?.marketingAssetOuterSpec?.marketingTargetType)) {

+ 320 - 42
src/services/adqV3/global.ts

@@ -581,20 +581,42 @@ export async function getWechatAppletDetailApi(id: number) {
  * @param data 
  * @returns 
  */
-export async function addCorpWechatApi(data: { wechatId: string, wechatName: string }) {
-    return request(api + `/adq/v3/corpWechat/add`, {
+export async function addCorpWechatApi(data: { agentId: number, corpName: string, localCorpId: string, tencentCorpId: string }) {
+    return request(api + `/adq/v3/tencentCorp/add`, {
         method: 'POST',
         data
     })
 }
 
+/**
+ * 修改本地企微微信信息
+ * @param data 
+ * @returns 
+ */
+export async function modifyCorpWechatApi(data: { agentId: number, corpName: string, localCorpId: string, tencentCorpId: string }) {
+    return request(api + `/adq/v3/tencentCorp/modify`, {
+        method: 'PUT',
+        data
+    })
+}
+
+/**
+ * 查询企微列表
+ * @returns 
+ */
+export async function getLocalCorpListApi() {
+    return request(api + `/adq/v3/tencentCorp/localCorpList`, {
+        method: 'GET'
+    })
+}
+
 /**
  * 查询本地企业微信列表
  * @param data 
  * @returns 
  */
-export async function getCorpWechatApi(data: { pageNum: number, pageSize: number, wechatName?: string, wechatIdList?: string[] }) {
-    return request(api + `/adq/v3/corpWechat/list`, {
+export async function getCorpWechatApi(data: { pageNum: number, pageSize: number, corpName?: string, wechatIdList?: string[] }) {
+    return request(api + `/adq/v3/tencentCorp/list`, {
         method: 'POST',
         data
     })
@@ -615,25 +637,26 @@ export async function delCorpWechatApi(id: number) {
 
 
 /**
- * 获取所有微信小程序列表
+ * 获取填写所有微列表 投放
  * @param data 
  * @returns 
  */
-export async function getCorpWechatAllApi(data: { wechatName?: string, wechatIdList?: string[] }) {
-    return request(api + `/adq/v3/corpWechat/listAll`, {
+export async function getCorpWechatAllApi(data: { corpName?: string }) {
+    return request(api + `/adq/v3/tencentCorp/listAll`, {
         method: 'POST',
         data
     })
 }
 
 /**
- * 获取详情
+ * 获取企微详情
  * @param id 
  * @returns 
  */
-export async function getCorpWechatDetailApi(id: number) {
-    return request(api + `/adq/v3/corpWechat/getById/${id}`, {
-        method: 'GET'
+export async function getCorpWechatDetailApi(params: { tencentCorpId: string }) {
+    return request(api + `/adq/v3/tencentCorp/getByTencentCorpId`, {
+        method: 'GET',
+        params
     })
 }
 
@@ -1003,18 +1026,115 @@ export async function getApplicationDetailApi(id: number) {
     })
 }
 
+
+export interface GetCustomerServiceGroupListProps {
+    pageNum: number,
+    pageSize: number,
+    csgroupName?: string, // 客服组名称
+    tencentCorpId?: string, // 企业微信ID
+    parentId?: number, // 父级ID
+    type?: 0 | 1, // 类型 0:客服组集,1:客服组,
+    policyId?: number, // 轮换策略ID
+}
 /**
  * 查询企业微信组件客服组
  * @param data 
  * @returns 
  */
-export async function getCustomerServiceGroupListApi(data: { adAccountId: number, pageNum: number, pageSize: number, tencentCorpId?: string }) {
-    return request(api + `/adq/v3/launch/tools/selectCustomerServiceGroup`, {
+export async function getCustomerServiceGroupListApi(data: GetCustomerServiceGroupListProps) {
+    return request(api + `/adq/localCorpCsgroup/listOfPage`, {
         method: 'POST',
         data
     })
 }
 
+/**
+ * 客服组-按类型查询(不分页, 类型非必传, 下拉筛选框使用)
+ * @param params 
+ * @returns 
+ */
+export async function getLocalCorpCsgroupListAllApi(params: { type?: 0 | 1 }) {
+    return request(api + `/adq/localCorpCsgroup/listOfType`, {
+        method: 'GET',
+        params
+    })
+}
+
+export interface GetCustomerServiceGroupUserListProps {
+    localCsgroupId: number, // 企业微信ID
+    pageNum: number, // 页码
+    pageSize: number, // 页大小
+    localCorpUserName?: string, // 客服组成员名称
+    poolType?: 'BLOCKLIST' | 'OFFLINE' | 'ONLINE' | 'WAITING' | 'WARRING', // 客服组成员状态
+}
+
+/**
+ * 客服组成员-分页查询
+ * @param data 
+ * @returns 
+ */
+export async function getLocalCorpCsgroupUserListApi(data: GetCustomerServiceGroupUserListProps) {
+    return request(api + `/adq/localCorpCsgroup/user/listOfPage`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 所有成员
+ * @param data 
+ * @returns 
+ */
+export async function getLocalCorpCsgroupUserAllListApi(data: { poolType?: 'BLOCKLIST' | 'OFFLINE' | 'ONLINE' | 'WAITING' | 'WARRING', localCsgroupId: number, localCorpUserName?: string }) {
+    return request(api + `/adq/localCorpCsgroup/user/all`, {
+        method: 'POST',
+        data
+    })
+}
+
+export interface AddLocalCorpCsgroupUserListProps {
+    localCsgroupId: number,
+    tencentCorpId: string,
+    tencentCorpUserIdList: string[]
+}
+
+/**
+ * 新增客服组成员
+ * @param data 
+ * @returns 
+ */
+export async function addLocalCorpCsgroupUserListApi(data: AddLocalCorpCsgroupUserListProps) {
+    return request(api + `/adq/localCorpCsgroup/user/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 删除客服组成员
+ * @param data 
+ * @returns 
+ */
+export async function delLocalCorpCsgroupUserApi(data: { idList: number[] }) {
+    return request(api + `/adq/localCorpCsgroup/user/deleteBatch`, {
+        method: 'DELETE',
+        data
+    })
+}
+
+
+/**
+ * 修改客服号
+ * @param data 
+ * @returns 
+ */
+export async function modifyLocalCorpCsgroupUserApi(data: { idList: number[], limitAddFansOfDay: number, poolType: 'BLOCKLIST' | 'OFFLINE' | 'ONLINE' | 'WAITING' | 'WARRING' }) {
+    return request(api + `/adq/localCorpCsgroup/user/modifyBatch`, {
+        method: 'PATCH',
+        data
+    })
+}
+
 export interface updateCustomerServiceGroupProps {
     adAccountId: number,
     tencentCorpId: string,
@@ -1022,18 +1142,38 @@ export interface updateCustomerServiceGroupProps {
     groupName: string,
     userIdList: string[]
 }
+
+export interface AddLocalCorpCsgroupProps {
+    csgroupName: string, // 客服组名称
+    tencentCorpId: string,
+    type: 0 | 1,  // 类型 0:客服组集,1:客服组,
+    parentId?: number, // 父级ID
+    policyId?: number, // 轮换策略ID
+}
 /**
- * 修改企业微信组件客服组
+ * 新增客服组
  * @param data 
  * @returns 
  */
-export async function updateCustomerServiceGroupApi(data: updateCustomerServiceGroupProps) {
-    return request(api + `/adq/v3/launch/tools/updateCustomerServiceGroup`, {
+export async function addLocalCorpCsgroupApi(data: AddLocalCorpCsgroupProps) {
+    return request(api + `/adq/localCorpCsgroup/add`, {
         method: 'POST',
         data
     })
 }
 
+/**
+ * 修改客服组
+ * @param data 
+ * @returns 
+ */
+export async function modifyLocalCorpCsgroupApi(data: AddLocalCorpCsgroupProps) {
+    return request(api + `/adq/localCorpCsgroup/modifyById`, {
+        method: 'PATCH',
+        data
+    })
+}
+
 export interface getWechatPagesCsgroupUserProps {
     adAccountId: number,
     tencentCorpId: string,
@@ -1064,25 +1204,74 @@ export async function getCorpDepartmentListApi(params: { corpName: string }) {
     })
 }
 
+export interface GetTencentCorpCsgroupListProps {
+    groupName?: string, // 客服组名称
+    pageNum: number, // 页码
+    pageSize: number, // 页大小
+}
+
 /**
- * 查询企微关系列表
+ * 腾讯客服组列表查询
+ * @param data 
+ * @returns 
+ */
+export async function getTencentCorpCsgroupListApi(data: GetTencentCorpCsgroupListProps) {
+    return request(api + `/adq/tencentCorpCsgroup/listOfPage`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 腾讯客服组-查询组内成员
  * @param params 
  * @returns 
  */
-export async function getCorpRelationListApi(data: { adAccountId: number }) {
-    return request(api + `/adq/corpRelation/listOfPage`, {
+export async function getUserListListApi(params: { groupId: number }) {
+    return request(api + `/adq/tencentCorpCsgroup/userList`, {
+        method: 'GET',
+        params
+    })
+}
+
+export interface AddTencentCorpCsgroupProps {
+    accountId: number, // 账户ID
+    tencentCorpId: string, // 企业微信ID
+    groupName: string, // 客服组名称
+    tencentCorpUserIds: string[], // 企业微信成员ID
+}
+
+/**
+ * 腾讯客服组-新增
+ * @param data 
+ * @returns 
+ */
+export async function addTencentCorpCsgroupApi(data: GetTencentCorpCsgroupListProps) {
+    return request(api + `/adq/tencentCorpCsgroup/add`, {
         method: 'POST',
         data
     })
 }
 
 /**
- * 查询所有企微关系
+ * 绑定本地客服组
  * @param data 
  * @returns 
  */
-export async function getCorpRelationAllApi(data: { adAccountId: number }) {
-    return request(api + `/adq/corpRelation/listAll`, {
+export async function bandTencentCorpCsgroupApi(data: { groupId: number, localCsgroupId?: number }) {
+    return request(api + `/adq/tencentCorpCsgroup/modifyById`, {
+        method: 'PATCH',
+        data
+    })
+}
+
+/**
+ * 查询企微关系列表
+ * @param params 
+ * @returns 
+ */
+export async function getCorpRelationListApi(data: { adAccountId: number }) {
+    return request(api + `/adq/corpRelation/listOfPage`, {
         method: 'POST',
         data
     })
@@ -1111,6 +1300,65 @@ export async function addCorpRelationListApi(data: { adAccountId: number, corpId
     })
 }
 
+
+export interface AddUserRotatePolicyProps {
+    policyName: string              // 策略名称
+    addCostCalculationCycle?: number // 加粉成本的计算周期(/分钟,必须 >= 20,且必须是 10 的整数倍数)
+    addCostException?: number       // 加粉成本异常值 (单位:/分)
+    maxAddFansCount?: number        // 单号单日最大加粉数量
+    minAddFansCount?: number        // 单号单日最大加粉数量,默认 20,加粉数少于该值永远不触发风控
+    onlineCount?: number            // 组内客服号在线数量
+    rollingCustomerTime?: number    // 客服组内滚动客服号的周期(/分钟,必须 >= 10)
+    id?: number
+}
+/**
+ * 客服组成员轮换策略-新增
+ * @param params 
+ * @returns 
+ */
+export async function addUserRotatePolicyApi(data: AddUserRotatePolicyProps) {
+    return request(api + `/adq/localCorpCsgroup/userRotatePolicy/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 修改策略
+ * @param data 
+ * @returns 
+ */
+export async function modifyUserRotatePolicyApi(data: AddUserRotatePolicyProps) {
+    return request(api + `/adq/localCorpCsgroup/userRotatePolicy/modifyById`, {
+        method: 'PATCH',
+        data
+    })
+}
+
+/**
+ * 客服组成员轮换策略-分页查询
+ * @param data 
+ * @returns 
+ */
+export async function getUserRotatePolicyListApi(data: { pageNum: number, pageSize: number, policyName?: string }) {
+    return request(api + `/adq/localCorpCsgroup/userRotatePolicy/listOfPage`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 所有客服组成员轮换策略
+ * @param data 
+ * @returns 
+ */
+export async function getUserRotatePolicyAllListApi(data: { policyName?: string }) {
+    return request(api + `/adq/localCorpCsgroup/userRotatePolicy/all`, {
+        method: 'POST',
+        data
+    })
+}
+
 /**
  * 获取所有企微列表
  * @returns 
@@ -1122,12 +1370,8 @@ export async function getCorpListAllApi() {
 }
 
 export interface AddPageCustomerGroupProps {
-    adAccountId: number,
-    customerGroupId: number,
-    customerGroupName: string,
-    pageId: number,
-    pageName: string,
-    tencentCorpId: string
+    landingPageList: { [x: string]: any }[], // 落地页
+    localCsgroupId: string
 }
 /**
  * 新增落地页客服关系
@@ -1135,32 +1379,54 @@ export interface AddPageCustomerGroupProps {
  * @returns 
  */
 export async function addPageCustomerGroupApi(data: AddPageCustomerGroupProps) {
-    return request(api + `/adq/pageCustomerGroup/relation/add`, {
+    return request(api + `/adq/localCorpCsgroup/landingPage/add`, {
         method: 'POST',
         data
     })
 }
 
 /**
- * 删除落地页客服关系
- * @param id 
+ * 修改落地页客服关系
+ * @param data 
  * @returns 
  */
-export async function delPageCustomerGroupApi(id: number) {
-    return request(api + `/adq/pageCustomerGroup/relation/delById/${id}`, {
-        method: 'DELETE'
+export async function modifyPageCustomerGroupApi(data: { localCsgroupId: number, pageId: number, pageType: string }) {
+    return request(api + `/adq/localCorpCsgroup/landingPage/modifyById`, {
+        method: 'PATCH',
+        data
+    })
+}
+
+
+/**
+ * 新增原生落地页客服关系
+ * @param data 
+ * @returns 
+ */
+export async function addLandingPageCustomerGroupApi(data: AddPageCustomerGroupProps) {
+    return request(api + `/adq/localCorpCsgroup/wechat/landingPage/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 修改落地页客服关系
+ * @param data 
+ * @returns 
+ */
+export async function modifyLandingPageCustomerGroupApi(data: { localCsgroupId: number, pageId: number }) {
+    return request(api + `/adq/localCorpCsgroup/wechat/landingPage/modifyById`, {
+        method: 'PATCH',
+        data
     })
 }
 
 export interface GetPageCustomerGroupListProps {
-    adAccountId: number,
     pageNum: number,
     pageSize: number,
-    pageId?: number,
-    customerGroupId?: number,
-    customerGroupName?: string,
-    pageName?: string,
-    tencentCorpId?: string
+    pageId?: string,
+    pageName?: string
 }
 /**
  * 查询落地页客服关系
@@ -1168,7 +1434,19 @@ export interface GetPageCustomerGroupListProps {
  * @returns 
  */
 export async function getPageCustomerGroupListApi(data: GetPageCustomerGroupListProps) {
-    return request(api + `/adq/pageCustomerGroup/relation/listOfPage`, {
+    return request(api + `/adq/localCorpCsgroup/landingPage/listOfPage`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 原生落地页客服关系
+ * @param data 
+ * @returns 
+ */
+export async function getLandingPageCustomerGroupListApi(data: GetPageCustomerGroupListProps) {
+    return request(api + `/adq/localCorpCsgroup/wechat/landingPage/listOfPage`, {
         method: 'POST',
         data
     })
@@ -1311,7 +1589,7 @@ export async function updateDefaultSharingApi(params: { adAccountId: number, typ
  * @param params 
  * @returns 
  */
-export async function delComponentApi({componentId, ...params}: { adAccountId: number, componentId: number[] }) {
+export async function delComponentApi({ componentId, ...params }: { adAccountId: number, componentId: number[] }) {
     return request(api + `/adq/creative/component/deleteBatch`, {
         method: 'DELETE',
         params,

+ 83 - 0
src/services/adqV3/monitorEWList.ts

@@ -0,0 +1,83 @@
+import { request } from 'umi';
+import { api } from '../api';
+
+
+export interface GetCorpUserDayListProps {
+    pageNum: number,
+    pageSize: number,
+    day?: string,
+    corpId?: string
+}
+
+/**
+ * 客服号天数据 
+ * @param data 
+ * @returns 
+ */
+export async function getCorpUserDayListApi(data: GetCorpUserDayListProps) {
+    return request(api + `/tencentMonitor/corpUser/day/listOfPage`, {
+        method: 'POST',
+        data
+    })
+}
+
+export interface GetCorpUserDayHourListProps {
+    day: string,
+    corpId: string
+    corpUserId: string
+}
+
+/**
+ * 客服号小时数据
+ * @param data 
+ * @returns 
+ */
+export async function getCorpUserDayHourListApi(data: GetCorpUserDayHourListProps) {
+    return request(api + `/tencentMonitor/corpUser/day/hour/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+export interface GetLocalCsgroupDayListProps {
+    pageNum: number,
+    pageSize: number,
+    day?: string,
+    localCsgroupId?: number
+}
+
+/**
+ * 本地客服组天数据
+ * @param data 
+ * @returns 
+ */
+export async function getLocalCsgroupDayListApi(data: GetLocalCsgroupDayListProps) {
+    return request(api + `/tencentMonitor/localCsgroup/day/listOfPage`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 本地客服组单条天数据对应的小时数据
+ * @param data 
+ * @returns 
+ */
+export async function getLocalCsgroupDayHourListApi(data: { day: string, localCsgroupId: number }) {
+    return request(api + `/tencentMonitor/localCsgroup/day/hour/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 本地客服组单条天数据对应的索引数据
+ * @param data 
+ * @returns 
+ */
+export async function getLocalCsgroupDayIndexListApi(data: { pageNum: number, pageSize: number, day: string, localCsgroupId: number }) {
+    return request(api + `/tencentMonitor/localCsgroup/index/listOfPage`, {
+        method: 'POST',
+        data
+    })
+}