wjx 1 hónapja
szülő
commit
52f20550fa
36 módosított fájl, 1935 hozzáadás és 466 törlés
  1. 2 2
      src/pages/weComTask/API/businessPlan/create.ts
  2. 95 1
      src/pages/weComTask/API/global.ts
  3. 1 1
      src/pages/weComTask/page/bookLink/selectBookLink.tsx
  4. 14 6
      src/pages/weComTask/page/businessPlan/taskList/index.tsx
  5. 11 91
      src/pages/weComTask/page/businessPlan/taskList/tableConfig.tsx
  6. 4 1
      src/pages/weComTask/page/corpUserManage/selectCorpUser.tsx
  7. 167 0
      src/pages/weComTask/page/groupChatSend/official/create/components/SelectMpCorp/index.tsx
  8. 1 4
      src/pages/weComTask/page/groupChatSend/official/create/components/Strategy/index.tsx
  9. 2 36
      src/pages/weComTask/page/groupChatSend/official/create/components/Strategy/previewStrategy.tsx
  10. 4 43
      src/pages/weComTask/page/groupChatSend/official/create/components/Strategy/settingsStrategy.tsx
  11. 3 12
      src/pages/weComTask/page/groupChatSend/official/create/components/content/index.tsx
  12. 119 21
      src/pages/weComTask/page/groupChatSend/official/create/index.tsx
  13. 26 20
      src/pages/weComTask/page/groupChatSend/official/create/tableConfig.tsx
  14. 22 0
      src/pages/weComTask/page/groupChatSend/official/taskList/components/groupTask/index.tsx
  15. 14 6
      src/pages/weComTask/page/groupChatSend/official/taskList/index.tsx
  16. 11 6
      src/pages/weComTask/page/groupChatSend/official/taskList/tableConfig.tsx
  17. 7 0
      src/pages/weComTask/page/groupChatSend/official/typings.d.ts
  18. 2 3
      src/pages/weComTask/page/groupChatSend/robot/create/components/Strategy/index.tsx
  19. 1 56
      src/pages/weComTask/page/groupChatSend/robot/create/components/Strategy/previewStrategy.tsx
  20. 3 67
      src/pages/weComTask/page/groupChatSend/robot/create/components/Strategy/settingsStrategy.tsx
  21. 3 11
      src/pages/weComTask/page/groupChatSend/robot/create/components/content/index.tsx
  22. 43 0
      src/pages/weComTask/page/groupChatSend/robot/create/const.ts
  23. 141 27
      src/pages/weComTask/page/groupChatSend/robot/create/index.tsx
  24. 104 40
      src/pages/weComTask/page/groupChatSend/robot/create/tableConfig.tsx
  25. 33 0
      src/pages/weComTask/page/groupChatSend/robot/taskList/components/groupTask/index.tsx
  26. 14 6
      src/pages/weComTask/page/groupChatSend/robot/taskList/index.tsx
  27. 11 6
      src/pages/weComTask/page/groupChatSend/robot/taskList/tableConfig.tsx
  28. 1 0
      src/pages/weComTask/page/groupChatSend/robot/typings.d.ts
  29. 89 0
      src/pages/weComTask/page/weAssociation/list/chatLog.tsx
  30. 65 0
      src/pages/weComTask/page/weAssociation/list/disbandChatLog.tsx
  31. 380 0
      src/pages/weComTask/page/weAssociation/list/index.tsx
  32. 53 0
      src/pages/weComTask/page/weAssociation/list/remarkSet.tsx
  33. 194 0
      src/pages/weComTask/page/weAssociation/list/tableConfig.tsx
  34. 145 0
      src/pages/weComTask/page/weAssociation/list/userContent.tsx
  35. 75 0
      src/pages/weComTask/page/weAssociation/list/userTableConfig.tsx
  36. 75 0
      src/pages/weComTask/page/weAssociation/list/zpMp.tsx

+ 2 - 2
src/pages/weComTask/API/businessPlan/create.ts

@@ -83,9 +83,9 @@ export async function delProjectApi(data: { projectIds: number[] }) {
  * @param data 
  * @returns 
  */
-export async function cancelProjectApi(data: { projectIds: number[] }) {
+export async function cancelProjectApi(data: { projectIds: number[],  pause: boolean }) {
     return request({
-        url: api + `/corpOperation/corp/create/project/cancel`,
+        url: api + `/corpOperation/corp/create/project/cancel/${data.pause}`,
         method: 'POST',
         data: data.projectIds
     });

+ 95 - 1
src/pages/weComTask/API/global.ts

@@ -76,7 +76,7 @@ export async function getTagGroupListApi(data: any) {
 export interface ApiParamsChatListProps {
     pageNum: number,
     pageSize: number,
-    corpId: string,//企业ID
+    corpId?: string,//企业ID
     corpUserId?: string[],//企微客服id
     chatName?: string,//群名称
     createTimeEnd?: string,//群创建结束时间
@@ -102,6 +102,100 @@ export async function getGroupChatListApi(data: ApiParamsChatListProps) {
     })
 }
 
+/**
+ * 总计
+ * @param data 
+ * @returns 
+ */
+export async function getGroupChatCountApi(data: ApiParamsChatListProps) {
+    return request({
+        url: '/corp/group/chat/list/count',
+        method: 'POST',
+        data
+    })
+}
+
+/**企业微信群成员列表*/
+export async function getGroupChatUserListApi(data: ApiParamsChatListProps) {
+    return request({
+        url: '/corp/group/chat/user/list',
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 设置备注
+ * @param data 
+ * @returns 
+ */
+export async function setRemarkApi(data: ApiParamsChatListProps) {
+    return request({
+        url: '/corp/group/chat/remark',
+        method: 'POST',
+        data,
+    })
+}
+
+/**
+ * 指派公众号
+ * @param data 
+ * @returns 
+ */
+export async function setQLMpApi(data: { chatIds: string[], corpId: string, mpAccountId: number }) {
+    return request({
+        url: '/corp/group/chat/configCorpGroupChats',
+        method: 'PUT',
+        data,
+    })
+}
+
+/**
+ * 获取群聊变更记录
+ * @param param0 
+ * @returns 
+ */
+export async function getMpChangeRecordListApi({ corpId, chatId }: { corpId: string, chatId: string }) {
+    return request({
+        url: `/corp/group/chat/getMpChangeRecord/${corpId}/${chatId}`,
+        method: 'GET'
+    })
+}
+
+/**
+ * 解散群聊
+ * @param data 
+ * @returns 
+ */
+export async function disbandChatApi(data: { corpId: string, chatIdList: string[] }) {
+    return request({
+        url: '/corp/group/chat/disband',
+        method: 'POST',
+        data
+    })
+}
+
+
+export interface getDisbandChatLogListProps {
+    pageNum: number,
+    pageSize: number,
+    corpId: string,
+    chatName?: string
+    sysUserId?: number
+}
+/**
+ * 解散群聊日志列表
+ * @param data 
+ * @returns 
+ */
+export async function getDisbandChatLogListApi(data: getDisbandChatLogListProps) {
+    return request({
+        url: '/corp/group/chat/disband/list',
+        method: 'POST',
+        data
+    })
+}
+
 /**获取背景图ID列表*/
 export async function api_get_img_typeList() {
     return request({

+ 1 - 1
src/pages/weComTask/page/bookLink/selectBookLink.tsx

@@ -131,7 +131,7 @@ const SelectBookLink: React.FC<Props> = ({ bookPlatForm, linkData, mpAccountId,
                             ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
                         }
                         allowClear
-                        options={Object.keys(LINKTYPE).filter(key => ['1', '2'].includes(key)).map(key => ({ label: LINKTYPE[key], value: key }))}
+                        options={Object.keys(LINKTYPE).map(key => ({ label: LINKTYPE[key], value: key }))}
                     />
                     <DatePicker.RangePicker
                         placeholder={['创建时间开始', '创建时间结束']}

+ 14 - 6
src/pages/weComTask/page/businessPlan/taskList/index.tsx

@@ -53,7 +53,7 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
     }
 
     // 删除
-    const handleDel = (data: { projectIds: number[] }, type: 'del' | 'cancel') => {
+    const handleDel = (data: { projectIds: number[] }, type: 'del' | 'cancel' | 'open') => {
         const hide = message.loading(type === 'del' ? '正在删除...' : '正在取消...', 0)
         switch (type) {
             case 'del':
@@ -69,14 +69,15 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
                 }).catch(() => hide())
                 break
             case 'cancel':
-                cancelProject.run(data).then(res => {
+            case 'open':
+                cancelProject.run({ ...data, pause: type === 'cancel' ? false : true }).then(res => {
                     hide()
                     setselectedRows([])
                     if (res?.data) {
-                        message.success('取消成功')
+                        message.success(type === 'cancel' ? '取消成功' : '启用成功')
                         getProjectList.refresh()
                     } else {
-                        message.error('取消失败')
+                        message.error(type === 'cancel' ? '取消失败' : '启用失败')
                     }
                 }).catch(() => hide())
                 break
@@ -123,11 +124,18 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
                 <Button type='primary' danger icon={<DeleteOutlined />} loading={delProject.loading} disabled={selectedRows.length === 0}>删除</Button>
             </Popconfirm>
             <Popconfirm
-                title="确定取消?"
+                title="确定暂停?"
                 onConfirm={() => { handleDel({ projectIds: selectedRows.map(i => i.id) }, 'cancel') }}
                 disabled={selectedRows.length === 0}
             >
-                <Button type='primary' style={{ backgroundColor: 'orange', borderColor: 'orange' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>取消任务</Button>
+                <Button type='primary' style={{ backgroundColor: 'orange', borderColor: 'orange' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>暂停任务</Button>
+            </Popconfirm>
+            <Popconfirm
+                title="确定启用?"
+                onConfirm={() => { handleDel({ projectIds: selectedRows.map(i => i.id) }, 'open') }}
+                disabled={selectedRows.length === 0}
+            >
+                <Button type='primary' style={{ backgroundColor: '#87d068', borderColor: '#87d068' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>启用任务</Button>
             </Popconfirm>
         </div>
         <Table

+ 11 - 91
src/pages/weComTask/page/businessPlan/taskList/tableConfig.tsx

@@ -3,13 +3,11 @@ import { AnyObject } from "antd/es/_util/type";
 import { ColumnsType } from "antd/es/table";
 import style from './index.less'
 import { QuestionCircleFilled } from '@ant-design/icons';
-import { restoreGroupData, restoreMomentData, restoreUserInheritData } from "../create/const";
+import { restoreGroupData, restoreUserInheritData } from "../create/const";
 import PreviewUserInherit from "../create/components/userInherit/previewUserlnherit";
 import PreviewMassSendingStrategy from "../create/components/massSending/previewMassSendingStrategy";
 import ShowContent from "../create/components/massSending/showContent";
 import PreviewWelcome from "../create/components/welcome/previewWelcome";
-import PreviewFriendsStrategy from "../create/components/friends/previewFriendsStrategy";
-import ShowFriendsContent from "../create/components/friends/showFriendsContent";
 
 const { Text, Paragraph } = Typography;
 const taskListColumns = (
@@ -17,7 +15,7 @@ const taskListColumns = (
     bookList: any[],
     handleLog: (data: any) => void,
     handleCopy: (data: any, isCopy: boolean) => void,
-    handleDel: (data: any, type: 'del' | 'cancel') => void,
+    handleDel: (data: any, type: 'del' | 'cancel' | 'open') => void,
 ): ColumnsType<AnyObject> => {
 
     return [
@@ -28,12 +26,17 @@ const taskListColumns = (
             width: 160,
             render(_, record) {
                 return <Space>
-                    <Popconfirm
-                        title="确定取消?"
+                    {record?.status === 1 ? <Popconfirm
+                        title="确定暂停?"
                         onConfirm={() => { handleDel({ projectIds: [record.id] }, 'cancel') }}
                     >
-                        <a style={{ color: 'orange' }}>取消任务</a>
-                    </Popconfirm>
+                        <a style={{ color: 'orange' }}>暂停任务</a>
+                    </Popconfirm> : record?.status === 3 ? <Popconfirm
+                        title="确定启用?"
+                        onConfirm={() => { handleDel({ projectIds: [record.id] }, 'open') }}
+                    >
+                        <a style={{ color: '#87d068' }}>启用任务</a>
+                    </Popconfirm> : undefined}
                     <a onClick={() => handleCopy(record, true)}>复制</a>
                     <a onClick={() => handleCopy(record, false)}>编辑</a>
                     <a onClick={() => handleLog(record)}>详情</a>
@@ -91,45 +94,6 @@ const taskListColumns = (
                 </div> : <Text type="danger">当前没有欢迎语配置</Text>
             }
         },
-        // {
-        //     title: '朋友圈配置',
-        //     dataIndex: 'momentCreateDTO',
-        //     key: 'momentCreateDTO',
-        //     width: 150,
-        //     ellipsis: true,
-        //     render: (value) => {
-        //         if (value && Object.keys(value)?.length > 0) {
-        //             const data = restoreMomentData(value)
-        //             return <div className={style.nameBox}>
-        //                 <div>
-        //                     <Text ellipsis>{value?.momentSendName || '<空>'}</Text>
-        //                 </div>
-        //                 <Popover
-        //                     placement="left"
-        //                     content={<div>
-        //                         <PreviewFriendsStrategy friendsStrategy={data?.friendsStrategy} />
-        //                     </div>}
-        //                     styles={{ body: { width: 360, overflow: 'hidden', overflowY: 'auto', maxHeight: 400 } }}
-        //                 >
-        //                     <a><QuestionCircleFilled /></a>
-        //                 </Popover>
-        //                 <Popover
-        //                     placement="left"
-        //                     content={<div>
-        //                         <ShowFriendsContent
-        //                             strategySettings={data?.friendsStrategy?.strategySettings}
-        //                             friendsContent={data?.friendsContent}
-        //                         />
-        //                     </div>}
-        //                     styles={{ body: { width: 360, overflow: 'hidden', overflowY: 'auto', maxHeight: 400 } }}
-        //                 >
-        //                     <a><QuestionCircleFilled /></a>
-        //                 </Popover>
-        //             </div>
-        //         }
-        //         return <Text type="danger">当前没有朋友圈配置</Text>
-        //     }
-        // },
         {
             title: '群发配置',
             dataIndex: 'groupSendTaskAddDTO',
@@ -171,50 +135,6 @@ const taskListColumns = (
                 return <Text type="danger">当前没有群发配置</Text>
             }
         },
-        // {
-        //     title: '高级群发配置',
-        //     dataIndex: 'messageSendTaskAddDTO',
-        //     key: 'messageSendTaskAddDTO',
-        //     width: 170,
-        //     ellipsis: true,
-        //     render: (value, record) => {
-        //         if (value && Object.keys(value)?.length > 0) {
-        //             const data = restoreGroupData(value, 'GROUP_GROUP')
-        //             console.log('data', record.projectName, data)
-        //             return <div className={style.nameBox}>
-        //                 <div>
-        //                     <Text ellipsis>{value?.groupSendName || '<空>'}</Text>
-        //                 </div>
-        //                 <Popover
-        //                     placement="left"
-        //                     content={<div>
-        //                         <PreviewMassSendingStrategy
-        //                             massSendingStrategy={data.massSendingStrategy}
-        //                             configType='GROUP_GROUP'
-        //                         />
-        //                     </div>}
-        //                     styles={{ body: { width: 360, overflow: 'hidden', overflowY: 'auto', maxHeight: 400 } }}
-        //                 >
-        //                     <a><QuestionCircleFilled /></a>
-        //                 </Popover>
-        //                 <Popover
-        //                     placement="left"
-        //                     content={<div>
-        //                         <ShowContent
-        //                             strategySettings={data?.massSendingStrategy?.strategySettings}
-        //                             massSendingContent={data?.massSendingContent}
-        //                             type="highQf"
-        //                         />
-        //                     </div>}
-        //                     styles={{ body: { width: 360, overflow: 'hidden', overflowY: 'auto', maxHeight: 400 } }}
-        //                 >
-        //                     <a><QuestionCircleFilled /></a>
-        //                 </Popover>
-        //             </div>
-        //         }
-        //         return <Text type="danger">当前没有群发配置</Text>
-        //     }
-        // },
         {
             title: '客户继承配置',
             dataIndex: 'externalUserTransferTasksDTO',

+ 4 - 1
src/pages/weComTask/page/corpUserManage/selectCorpUser.tsx

@@ -148,9 +148,12 @@ const SelectCorpUserModal: React.FC<SelectCorpUserModalProps> = React.memo(({ op
         } else {
             delete params?.corpUserIds
         }
+        if (corpId && !(queryForm?.corpIds?.length > 0)) {
+            return
+        }
         getCorpUser.run(params)
 
-    }, [queryForm])
+    }, [queryForm, corpId])
 
     const handleOk = () => {
         if (editSelectedRow.length) {

+ 167 - 0
src/pages/weComTask/page/groupChatSend/official/create/components/SelectMpCorp/index.tsx

@@ -0,0 +1,167 @@
+import { Avatar, Button, Input, message, Modal, Select, Space, Table, Tag, Typography } from "antd";
+import React, { useEffect, useState } from "react";
+import style from '../../../../../../components/selectExternalAccount/index.less'
+import { CheckOutlined, UserOutlined, CloseOutlined } from '@ant-design/icons'
+import { useAjax } from "@/Hook/useAjax";
+import { copy } from "@/utils/utils";
+import { DefaultOptionType } from "antd/es/select";
+const { Text, Title } = Typography;
+
+interface Props {
+    corpList: DefaultOptionType[]
+    mpAccount?: OFFICIAL_CHAT_CREATE.MpAccountProps[];
+    visible?: boolean;
+    onClose?: () => void;
+    onChange?: (value: OFFICIAL_CHAT_CREATE.MpAccountProps[]) => void;
+}
+/**
+ * 高级群发外部客户选择
+ * @param param0 
+ * @returns 
+ */
+const SelectMpCorp: React.FC<Props> = ({ corpList, mpAccount, visible, onClose, onChange }) => {
+
+    /***************************************/
+    const [data, setData] = useState<OFFICIAL_CHAT_CREATE.MpAccountProps[]>(mpAccount || []);
+    const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
+    const [queryForm, setQueryForm] = useState<any>({ })
+    /***************************************/
+
+    const handleOk = () => {
+        if (data?.every(item => item?.corp?.length)) {
+            onChange?.(data)
+        } else {
+            message.error('请选择关联主体')
+        }
+    }
+
+    const handleSelectAdz = (value: number) => {
+        if (value === selectAdz) {
+            return
+        }
+        setSelectAdz(value)
+    }
+
+    return <Modal
+        title={<strong>选择关联主体</strong>}
+        open={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        width={1000}
+        className={`${style.SelectPackage}`}
+        styles={{
+            body: {
+                padding: '0 10px 0 10px'
+            }
+        }}
+    >
+        <div className={style.content}>
+            <div className={style.left}>
+                <h4 className={style.title}>公众号</h4>
+                <div className={style.accountIdList}>
+                    {mpAccount?.map((item, index) => {
+                        const corp = data[index]?.corp || []
+                        return <div key={index} onClick={() => { handleSelectAdz(index + 1) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
+                            <div><Text ellipsis={{ tooltip: true }}>{item?.label}</Text></div>
+                            {corp?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
+                        </div>
+                    })}
+                </div>
+            </div>
+            <div className={style.right}>
+                <Space style={{ marginBottom: 10 }} align="end" size={5}>
+                    <Space.Compact>
+                        <Button>企业</Button>
+                        <Select
+                            showSearch
+                            style={{ width: 200 }}
+                            placeholder="请选择企业"
+                            filterOption={(input, option) =>
+                                ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
+                            }
+                            value={queryForm?.corpId}
+                            onChange={(e) => {
+                                setQueryForm({ ...queryForm, corpId: e, pageNum: 1 })
+                            }}
+                            allowClear
+                            options={corpList}
+                        />
+                    </Space.Compact>
+                </Space>
+
+                <Table
+                    tableLayout='fixed'
+                    dataSource={queryForm?.corpId ? corpList.filter(item => item.value === queryForm.corpId) : corpList}
+                    columns={[
+                        {
+                            title: '主体名称',
+                            dataIndex: 'label',
+                            key: 'label'
+                        }
+                    ]}
+                    scroll={{ y: 400 }}
+                    rowKey={'value'}
+                    size='small'
+                    rowSelection={{
+                        selectedRowKeys: data[selectAdz - 1]?.corp?.map((item: any) => item?.value),
+                        getCheckboxProps: (record: any) => ({
+                            name: record.name,
+                        }),
+                        onSelect: (record: { value: string }, selected: boolean) => {
+                            let newData = JSON.parse(JSON.stringify(data))
+                            let dataIten = newData[selectAdz - 1]?.corp || []
+                            if (selected) {
+                                dataIten.push({ ...record })
+                            } else {
+                                dataIten = dataIten.filter((item: { value: string }) => item.value !== record.value)
+                            }
+                            newData[selectAdz - 1].corp = dataIten
+                            setData(newData)
+                        },
+                        onSelectAll: (selected: boolean, selectedRowss: { value: string }[], changeRows: { value: string }[]) => {
+                            let newData = JSON.parse(JSON.stringify(data))
+                            let dataIten = newData[selectAdz - 1]?.corp || []
+                            if (selected) {
+                                let newSelectAccData = [...dataIten]
+                                changeRows.forEach((item: { value: string }) => {
+                                    let index = newSelectAccData.findIndex((ite: { value: string }) => ite.value === item.value)
+                                    if (index === -1) {
+                                        let data: any = { ...item }
+                                        newSelectAccData.push(data)
+                                    }
+                                })
+                                newData[selectAdz - 1].corp = newSelectAccData
+                            } else {
+                                let newSelectAccData = dataIten.filter((item: { value: string }) => {
+                                    let index = changeRows.findIndex((ite: { value: string }) => ite.value === item.value)
+                                    if (index !== -1) {
+                                        return false
+                                    } else {
+                                        return true
+                                    }
+                                })
+                                newData[selectAdz - 1].corp = newSelectAccData
+                            }
+                            setData(newData)
+                        }
+                    }}
+                />
+            </div>
+            <div className={style.center}>
+                <Title level={5}>已选:{data[selectAdz - 1]?.corp?.length || 0}</Title>
+                <div className={style.select_content}>
+                    {data[selectAdz - 1]?.corp?.map(item => <div key={item.value}>
+                        <Text ellipsis={{ tooltip: true }} className={style.marketingAssetName}>{item.label}</Text>
+                        <CloseOutlined className={style.close} onClick={() => {
+                            let newData = JSON.parse(JSON.stringify(data))
+                            newData[selectAdz - 1].corp = newData[selectAdz - 1]?.corp?.filter((i: any) => i?.value !== item.value)
+                            setData(newData)
+                        }} />
+                    </div>)}
+                </div>
+            </div>
+        </div>
+    </Modal>
+}
+
+export default React.memo(SelectMpCorp);

+ 1 - 4
src/pages/weComTask/page/groupChatSend/official/create/components/Strategy/index.tsx

@@ -9,7 +9,6 @@ import PreviewStrategy from "./previewStrategy";
 const Strategy: React.FC = () => {
 
     /*********************************/
-    const { message } = App.useApp()
     const { token } = useNewToken()
     const { setSettings, settings, onPreviewReset, mpList, corpList } = useContext(DispatchOfficialChatCreate)!;
     const [newVisible, setNewVisible] = useState<boolean>(false);
@@ -34,7 +33,7 @@ const Strategy: React.FC = () => {
                     <div className={style.detail_title}>群发策略配置</div>
                     <div className={style.detail_body}>
                         {settings?.strategyDTO && Object.keys(settings?.strategyDTO).length > 0 ? <>
-                            <PreviewStrategy strategyDTO={settings?.strategyDTO} mpList={mpList} corpList={corpList} />
+                            <PreviewStrategy strategyDTO={settings?.strategyDTO} />
                         </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                     </div>
                 </div>
@@ -48,8 +47,6 @@ const Strategy: React.FC = () => {
         {newVisible && <SettingsStrategy
             visible={newVisible}
             value={settings?.strategyDTO}
-            mpList={mpList}
-            corpList={corpList}
             onClose={() => {
                 setNewVisible(false);
             }}

+ 2 - 36
src/pages/weComTask/page/groupChatSend/official/create/components/Strategy/previewStrategy.tsx

@@ -1,18 +1,15 @@
 import React, { useEffect } from 'react';
 import dayjs from 'dayjs';
-import { Card, Form, Input, Radio, Select } from 'antd';
+import { Card, Form, Input, Radio } from 'antd';
 import SendTimeSet from '@/pages/weComTask/components/sendTimeSet';
 import '../../../../../businessPlan/create/global.less'
 import FilterUser from '@/pages/weComTask/components/filterUser';
-import { DefaultOptionType } from 'antd/es/select';
 
 interface Props {
     strategyDTO: { [x: string]: any }
-    mpList: DefaultOptionType[]
-    corpList: DefaultOptionType[]
 }
 
-const PreviewStrategy: React.FC<Props> = ({ strategyDTO, mpList, corpList }) => {
+const PreviewStrategy: React.FC<Props> = ({ strategyDTO }) => {
 
     /**************************************/
     const [form] = Form.useForm();
@@ -94,37 +91,6 @@ const PreviewStrategy: React.FC<Props> = ({ strategyDTO, mpList, corpList }) =>
                                                     <Form.Item {...restField} label={<strong>发送群对象名称</strong>} name={[name, 'sendGroupName']}>
                                                         <Input placeholder="请输入发送群对象名称" />
                                                     </Form.Item>
-                                                    <Form.Item
-                                                        {...restField}
-                                                        label={<strong>群聊关联公众号</strong>}
-                                                        name={[name, 'mpAccountId']}
-                                                    >
-                                                        <Select
-                                                            showSearch
-                                                            allowClear
-                                                            placeholder="选择公众号"
-                                                            filterOption={(input, option) =>
-                                                                (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                            }
-                                                            options={mpList}
-                                                        />
-                                                    </Form.Item>
-                                                    <Form.Item
-                                                        {...restField}
-                                                        label={<strong>群聊主体</strong>}
-                                                        name={[name, 'corpIds']}
-                                                    >
-                                                        <Select
-                                                            showSearch
-                                                            allowClear
-                                                            placeholder="选择群聊主体"
-                                                            filterOption={(input, option) =>
-                                                                (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                            }
-                                                            mode='multiple'
-                                                            options={corpList}
-                                                        />
-                                                    </Form.Item>
                                                     <Form.Item label={<strong>发送群对象配置</strong>}>
                                                         <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
                                                             <Form.Item

+ 4 - 43
src/pages/weComTask/page/groupChatSend/official/create/components/Strategy/settingsStrategy.tsx

@@ -1,23 +1,18 @@
-import { App, Button, Card, Form, Input, Modal, Radio, Select } from "antd";
+import { App, Button, Card, Form, Input, Modal, Radio } from "antd";
 import React, { useEffect, useRef, useState } from "react";
 import '../../../../../businessPlan/create/global.less'
 import SendTimeSet from "@/pages/weComTask/components/sendTimeSet";
 import { MinusCircleOutlined, PlusOutlined, DeleteOutlined } from "@ant-design/icons";
 import FilterUser from "@/pages/weComTask/components/filterUser";
-import { DefaultOptionType } from "antd/es/select";
 import NewSteps from "@/pages/weComTask/components/newSteps";
 import dayjs from 'dayjs';
 
-interface Props extends GROUP_CHAT_CREATE.FoundationProps<any> {
-    mpList: DefaultOptionType[]
-    corpList: DefaultOptionType[]
-}
 /**
  * 官方 群聊群发策略配置
  * @param param0 
  * @returns 
  */
-const SettingsStrategy: React.FC<Props> = ({ visible, onClose, value, onChange, mpList, corpList }) => {
+const SettingsStrategy: React.FC<GROUP_CHAT_CREATE.FoundationProps<any>> = ({ visible, onClose, value, onChange }) => {
 
     /************************************/
     const { message } = App.useApp()
@@ -119,11 +114,11 @@ const SettingsStrategy: React.FC<Props> = ({ visible, onClose, value, onChange,
                 ((timeRepeatType === "TIME_TYPE_REPEAT_WEEK" || timeRepeatType === "TIME_TYPE_REPEAT_MONTH") && startTime && sendTime && repeatArray)
 
             const children = sendData?.map((sd, sdIndex) => {
-                const { sendGroupName, mpAccountId, corpIds, externalUserType, externalUserFilter } = sd
+                const { sendGroupName, externalUserType, externalUserFilter } = sd
                 return {
                     title: `发送群对象${sdIndex + 1}`, id: `strategy_${index + 1}_sendData_${sdIndex + 1}`,
                     description: `名称、公众号、主体...`,
-                    checked: sendGroupName && mpAccountId && corpIds?.length && (externalUserType === 'all' ? true : externalUserFilter && Object.keys(externalUserFilter)?.length > 0)
+                    checked: sendGroupName && (externalUserType === 'all' ? true : externalUserFilter && Object.keys(externalUserFilter)?.length > 0)
                 }
             })
             return {
@@ -238,40 +233,6 @@ const SettingsStrategy: React.FC<Props> = ({ visible, onClose, value, onChange,
                                                         <Form.Item {...restField} label={<strong>发送群对象名称</strong>} name={[name, 'sendGroupName']} rules={[{ required: true, message: '请输入发送群对象名称!' }]}>
                                                             <Input placeholder="请输入发送群对象名称" style={{ width: 358 }} allowClear />
                                                         </Form.Item>
-                                                        <Form.Item
-                                                            {...restField}
-                                                            label={<strong>群聊关联公众号</strong>}
-                                                            name={[name, 'mpAccountId']}
-                                                        >
-                                                            <Select
-                                                                showSearch
-                                                                allowClear
-                                                                placeholder="选择公众号"
-                                                                filterOption={(input, option) =>
-                                                                    (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                                }
-                                                                style={{ width: 358 }}
-                                                                options={mpList}
-                                                            />
-                                                        </Form.Item>
-                                                        <Form.Item
-                                                            {...restField}
-                                                            label={<strong>群聊主体</strong>}
-                                                            name={[name, 'corpIds']}
-                                                            rules={[{ required: true, message: '请选择群聊主体!' }]}
-                                                        >
-                                                            <Select
-                                                                showSearch
-                                                                allowClear
-                                                                placeholder="选择群聊主体"
-                                                                filterOption={(input, option) =>
-                                                                    (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                                }
-                                                                mode="multiple"
-                                                                style={{ width: 358 }}
-                                                                options={corpList}
-                                                            />
-                                                        </Form.Item>
                                                         <Form.Item
                                                             label={<strong>发送群对象配置</strong>}
                                                             required

+ 3 - 12
src/pages/weComTask/page/groupChatSend/official/create/components/content/index.tsx

@@ -2,7 +2,7 @@ import React, { useContext, useState } from 'react';
 import style from '../../../../../businessPlan/create/index.less'
 import useNewToken from '@/Hook/useNewToken';
 import { DispatchOfficialChatCreate } from '../..';
-import { Button, Empty, message, Popconfirm } from 'antd';
+import { Button, Empty, message } from 'antd';
 import SettingsContent from './settingsContent';
 import PreviewContent from './previewContent';
 
@@ -10,7 +10,7 @@ const Content: React.FC = () => {
 
     /*******************************************/
     const { token } = useNewToken()
-    const { setSettings, settings, onPreviewReset, mpList, corpList } = useContext(DispatchOfficialChatCreate)!;
+    const { setSettings, settings, onPreviewReset } = useContext(DispatchOfficialChatCreate)!;
     const [newVisible, setNewVisible] = useState<boolean>(false);
     /*******************************************/
 
@@ -19,15 +19,6 @@ const Content: React.FC = () => {
             <div className={`${style.settingsBody_content_col}`} style={{ width: '100%' }}>
                 <div className={style.title}>
                     <span>内容</span>
-                    {/* {settings?.strategyDTO && Object.keys(settings?.strategyDTO).length > 0 && <Popconfirm
-                        title="确定清空?"
-                        onConfirm={() => {
-                            setSettings(undefined)
-                            onPreviewReset();
-                        }}
-                    >
-                        <a style={{ color: 'red' }}>清空</a>
-                    </Popconfirm>} */}
                 </div>
                 <div className={style.detail}>
                     <div className={style.detail_title}>群发内容配置</div>
@@ -42,7 +33,6 @@ const Content: React.FC = () => {
                         ? <Button type="link" style={{ padding: 0, fontSize: 12, color: token.colorPrimary }} size="small" onClick={() => setNewVisible(true)}>编辑</Button>
                         : <Button type="link" style={{ padding: 0, fontSize: 12, color: token.colorPrimary }} size="small" onClick={() => message.error('请先设置策略')}>编辑</Button>
                     }
-
                 </div>
             </div>
         </div>
@@ -61,6 +51,7 @@ const Content: React.FC = () => {
                     }
                 })
                 setNewVisible(false)
+                onPreviewReset();
             }}
         />}
     </>;

+ 119 - 21
src/pages/weComTask/page/groupChatSend/official/create/index.tsx

@@ -9,14 +9,15 @@ import Strategy from './components/Strategy';
 import { getBindMpListApi } from '@/pages/weComTask/API/corpUserAssign';
 import { DefaultOptionType } from 'antd/es/select';
 import { getCorpAllListApi } from '@/API/global';
-import { SaveOutlined, RedoOutlined, SearchOutlined, PlusOutlined } from '@ant-design/icons';
+import { SaveOutlined, RedoOutlined, SearchOutlined, PlusOutlined, CheckOutlined } from '@ant-design/icons';
 import Content from './components/content';
-import { removeEmptyValues } from '@/utils/utils';
+import { groupBy, removeEmptyValues } from '@/utils/utils';
 import { PreviewColumns } from './tableConfig';
 import SubmitModal from '../../../businessPlan/create/submitModal';
 import { addTaskApi, getCreateDetailsApi, updateTaskApi } from '@/pages/weComTask/API/businessPlan/create';
 import { useNavigate } from 'react-router-dom';
 import { getGroupChatSendData } from './const';
+import SelectMpCorp from './components/SelectMpCorp';
 
 
 export const DispatchOfficialChatCreate = React.createContext<OFFICIAL_CHAT_CREATE.DispatchOfficialChatCreate | null>(null);
@@ -39,6 +40,7 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
     const [corpList, setCorpList] = useState<DefaultOptionType[]>([])
     const [previewContent, setPreviewContent] = useState<{ [x: string]: { [x: string]: string } }>({})
     const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
+    const [corpVisible, setCorpVisible] = useState<boolean>(false)
 
     const welcomeMsgJobType = useAjax(() => welcomeMsgJobTypeApi())//获取业务类型
     const getBindMpList = useAjax(() => getBindMpListApi())
@@ -59,12 +61,20 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
             getCreateDetails.run(id).then(res => {
                 sessionStorage.removeItem('PGSD_OFFICIALTASKID')
                 if (res?.data) {
-                    const { bizType, platform, templateProductId, channel, groupChatSendTaskAddDTO } = res.data
+                    const { bizType, platform, templateProductId, channel, groupChatSendTaskAddDTO, mpAccounts } = res.data
                     let newSettings: OFFICIAL_CHAT_CREATE.SettingsProps = {
                         bizType,
                         platform: Number(platform) as any,
                         templateProductId,
-                        channel
+                        channel,
+                        mpAccount: mpAccounts.map(m => {
+                            return {
+                                label: m?.mpAccountVO?.name || '',
+                                value: m?.mpAccountVO?.id || '',
+                                appId: m?.mpAccountVO?.appId || '',
+                                corp: m?.corpVOList?.map(c => ({ label: c.corpName, value: c.corpId })) || []
+                            }
+                        })
                     }
                     if (groupChatSendTaskAddDTO && Object.keys(groupChatSendTaskAddDTO).length > 0) {
                         const data = getGroupChatSendData(groupChatSendTaskAddDTO)
@@ -116,11 +126,19 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
 
     // 预览
     const preview = () => {
-        const { bizType, platform, templateProductId, channel } = settings
+        const { bizType, platform, templateProductId, channel, mpAccount } = settings
         if (!(settings?.strategyDTO && Object.keys(settings?.strategyDTO).length > 0)) {
             message.error('请先配置内容')
             return
         }
+        if (!mpAccount?.length) {
+            message.error('请选择公众号')
+            return
+        }
+        if (mpAccount.some(item => !item?.corp?.length)) {
+            message.error('请选择关联主体')
+            return
+        }
         if (!bizType) {
             message.error('请选择业务类型')
             return
@@ -130,11 +148,10 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
             return
         }
         const list: any[] = []
-        let id = 1
         if (!settings?.strategyDTO?.strategyList?.every((str, s_index) => {
             const { sendData, ...sdto } = str
             return sendData?.every((sd, f_index) => {
-                const { contentDTO, mpAccountId, corpIds, ...cdto } = sd
+                const { contentDTO, ...cdto } = sd
                 if (contentDTO?.length) {
                     return contentDTO?.every((cd, c_index) => {
                         if (cd?.attachmentList?.length || cd?.text?.content) {
@@ -167,20 +184,13 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                                         return `<span style="color: red">请联系管理员</span>`
                                 }
                             })
-                            const weChat = mpList.find(item => item.value === mpAccountId)
                             list.push({
-                                id,
                                 bizType,
                                 platform,
                                 templateProductId,
                                 channel,
                                 taskName: settings?.strategyDTO?.taskName,
                                 strategyData: sdto,
-                                mpAccountId,
-                                weChatName: weChat?.label,
-                                mpAccountAppid: weChat?.appId,
-                                corpIds,
-                                corpNames: corpIds.map(corpId => corpList.find(item => item.value === corpId)?.label),
                                 sendData: cdto,
                                 contentReactNode,
                                 content: cd,
@@ -190,7 +200,6 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                                 linkData,
                                 miniProgramData
                             })
-                            id++;
                             return true
                         } else {
                             message.error(`策略:${str?.strategyName},发送对象:${sd?.sendGroupName}请填写发送内容`)
@@ -206,9 +215,25 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
         })) {
             return
         }
-        console.log('=========================>', list)
-        setPreviewData(list)
-        setPreviewDataOld(list)
+        let id = 1
+        const newPreviewData = mpAccount.reduce((pre, cur, index) => {
+            return pre.concat(...list.map((item, i) => {
+                return {
+                    ...item,
+                    id: id++,
+                    mpAccountId: cur.value,
+                    mpAccountName: cur.label,
+                    mpAccountAppid: (cur as any).appId,
+                    corpIds: cur.corp?.map(item => item.value),
+                    corpNames: cur.corp?.map(item => item.label),
+                    mpIndex: index,
+                    mpRowSpan: i === 0 ? list.length : 0, // 用于表格合并
+                }
+            }))
+        }, [])
+        console.log('=========================>', list, newPreviewData)
+        setPreviewData(newPreviewData)
+        setPreviewDataOld(newPreviewData)
         setPreviewContent({})
     }
 
@@ -240,13 +265,29 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
     }
 
     const onSubmit = (values: any) => {
-        const { bizType, platform, templateProductId, channel, strategyDTO } = settings
+        const groupData = groupBy(previewDataOld, (item) => item['mpAccountId'], true);
+        const { bizType, platform, templateProductId, channel, strategyDTO, mpAccount } = settings
         const params = {
             ...values,
             bizType,
             platform,
             templateProductId,
             channel,
+            mpAccounts: mpAccount.map(item => {
+                const data = groupData[item.value] || []
+                const groupChatMsgContent: any[] = []
+                data.forEach(d => {
+                    const { strategyIndex, sendIndex, contentIndex, id } = d
+                    if (!groupChatMsgContent[strategyIndex]) groupChatMsgContent[strategyIndex] = [];
+                    if (!groupChatMsgContent[strategyIndex][sendIndex]) groupChatMsgContent[strategyIndex][sendIndex] = [];    
+                    groupChatMsgContent[strategyIndex][sendIndex][contentIndex] = previewContent?.[id] || {};    
+                })
+                return {
+                    mpAccountId: item.value,
+                    corpIds: item.corp?.map(c => c.value) || [],
+                    groupChatMsgContent
+                }
+            }),
             groupChatSendTaskAddDTO: {
                 ...strategyDTO,
                 strategyList: strategyDTO?.strategyList?.map(item => {
@@ -287,7 +328,6 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
             })
         } else {
             addTask.run(params).then(res => {
-                console.log(res)
                 if (res?.data) {
                     modal.success({
                         content: '任务提交成功',
@@ -306,11 +346,51 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
             })
         }
     }
-    
+
     return <div className={style.create}>
         <Spin spinning={false}>
             <Card title={<strong>{projectId ? getCreateDetails?.data?.data?.projectName + '任务编辑' : ''}配置区</strong>} className={`${style.card} ${style.config}`}>
                 <Space wrap>
+                    <Space.Compact>
+                        <Button>群聊关联公众号</Button>
+                        <Select
+                            showSearch
+                            allowClear
+                            placeholder="选择公众号"
+                            filterOption={(input, option) =>
+                                (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                            style={{ minWidth: 200 }}
+                            maxTagCount={1}
+                            mode='multiple'
+                            options={mpList}
+                            value={settings?.mpAccount?.map(item => item.value)}
+                            onChange={(_, options) => {
+                                onPreviewReset()
+                                if (options.length > 0) {
+                                    const oldMpAccountIds = settings?.mpAccount?.reduce((acc, item) => {
+                                        acc[item.value] = item;
+                                        return acc;
+                                    }, {}) || []
+                                    setSettings({
+                                        ...settings,
+                                        mpAccount: options.map(item => {
+                                            if (oldMpAccountIds?.[item.value]) {
+                                                return oldMpAccountIds[item.value]
+                                            }
+                                            return item
+                                        })
+                                    })
+                                } else {
+                                    setSettings({ ...settings, mpAccount: [] })
+                                }
+                            }}
+                        />
+                    </Space.Compact>
+                    {settings?.mpAccount?.length > 0 && <Button onClick={() => setCorpVisible(true)}>
+                        选择关联主体
+                        {settings?.mpAccount?.some(item => item?.corp?.length) && <CheckOutlined style={{ color: settings?.mpAccount?.every(item => item?.corp?.length) ? '#1890ff' : '#52C41A' }} />}
+                    </Button>}
                     <Space.Compact>
                         <Button>业务类型</Button>
                         <Select
@@ -495,6 +575,24 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                 setSubVisible(false)
             }}
         />}
+
+        {/* 选择关联主体 */}
+        {corpVisible && <SelectMpCorp
+            corpList={corpList}
+            visible={corpVisible}
+            mpAccount={settings?.mpAccount}
+            onChange={(mpAccount) => {
+                setSettings({
+                    ...settings,
+                    mpAccount
+                })
+                setCorpVisible(false)
+                onPreviewReset()
+            }}
+            onClose={() => {
+                setCorpVisible(false)
+            }}
+        />}
     </div>;
 };
 

+ 26 - 20
src/pages/weComTask/page/groupChatSend/official/create/tableConfig.tsx

@@ -11,6 +11,32 @@ const { Paragraph, Title } = Typography;
 export const PreviewColumns = (bookPlatForm: TASK_CREATE.BookPlatFormProps[], bookList: TASK_CREATE.BookListProps[], platform: string, setPreviewContent: React.Dispatch<React.SetStateAction<{ [x: string]: { [x: string]: string; }; }>>, previewContent: { [x: string]: { [x: string]: string } }): ColumnsType<AnyObject> => {
 
     return [
+        {
+            title: '群聊关注公众号',
+            dataIndex: 'mpAccountName',
+            key: 'mpAccountName',
+            width: 120,
+            ellipsis: true,
+            render(value) {
+                return value || '--'
+            },
+            onCell: (record) => {
+                return { rowSpan: record.mpRowSpan }
+            }
+        },
+        {
+            title: '企微主体',
+            dataIndex: 'corpNames',
+            key: 'corpNames',
+            width: 160,
+            ellipsis: true,
+            render(value) {
+                return value ? value.join('、') : '--'
+            },
+            onCell: (record) => {
+                return { rowSpan: record.mpRowSpan }
+            }
+        },
         {
             title: '基础信息',
             dataIndex: 'taskName',
@@ -42,26 +68,6 @@ export const PreviewColumns = (bookPlatForm: TASK_CREATE.BookPlatFormProps[], bo
                 </>
             }
         },
-        {
-            title: '群聊关注公众号',
-            dataIndex: 'weChatName',
-            key: 'weChatName',
-            width: 120,
-            ellipsis: true,
-            render(value) {
-                return value || '--'
-            },
-        },
-        {
-            title: '企微主体',
-            dataIndex: 'corpNames',
-            key: 'corpNames',
-            width: 160,
-            ellipsis: true,
-            render(value) {
-                return value ? value.join('、') : '--'
-            },
-        },
         {
             title: '发送对象',
             dataIndex: 'sendData',

+ 22 - 0
src/pages/weComTask/page/groupChatSend/official/taskList/components/groupTask/index.tsx

@@ -38,6 +38,28 @@ const GroupTask: React.FC<Props> = ({ groupSendTaskVOList }) => {
                 ellipsis: true,
                 align: 'center'
             },
+            {
+                title: '关联公众号',
+                dataIndex: 'mpAccountInfo',
+                key: 'mpAccountInfo',
+                width: 100,
+                ellipsis: true,
+                align: 'center',
+                render(value) {
+                    return value?.name || '--'
+                },
+            },
+            {
+                title: '关联主体',
+                dataIndex: 'corpInfoList',
+                key: 'corpInfoList',
+                width: 100,
+                ellipsis: true,
+                align: 'center',
+                render(value) {
+                    return value?.map(item => item.corpName)?.join('、') || '--'
+                },
+            },
             {
                 title: '状态',
                 dataIndex: 'status',

+ 14 - 6
src/pages/weComTask/page/groupChatSend/official/taskList/index.tsx

@@ -71,7 +71,7 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
     }
 
     // 删除
-    const handleDel = (data: { projectIds: number[] }, type: 'del' | 'cancel') => {
+    const handleDel = (data: { projectIds: number[] }, type: 'del' | 'cancel' | 'open') => {
         const hide = message.loading(type === 'del' ? '正在删除...' : '正在取消...', 0)
         switch (type) {
             case 'del':
@@ -87,14 +87,15 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
                 }).catch(() => hide())
                 break
             case 'cancel':
-                cancelProject.run(data).then(res => {
+            case 'open':
+                cancelProject.run({ ...data, pause: type === 'cancel' ? false : true }).then(res => {
                     hide()
                     setselectedRows([])
                     if (res?.data) {
-                        message.success('取消成功')
+                        message.success(type === 'cancel' ? '取消成功' : '启用成功')
                         getProjectList.refresh()
                     } else {
-                        message.error('取消失败')
+                        message.error(type === 'cancel' ? '取消失败' : '启用失败')
                     }
                 }).catch(() => hide())
                 break
@@ -141,11 +142,18 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
                 <Button type='primary' danger icon={<DeleteOutlined />} loading={delProject.loading} disabled={selectedRows.length === 0}>删除</Button>
             </Popconfirm>
             <Popconfirm
-                title="确定取消?"
+                title="确定暂停?"
                 onConfirm={() => { handleDel({ projectIds: selectedRows.map(i => i.id) }, 'cancel') }}
                 disabled={selectedRows.length === 0}
             >
-                <Button type='primary' style={{ backgroundColor: 'orange', borderColor: 'orange' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>取消任务</Button>
+                <Button type='primary' style={{ backgroundColor: 'orange', borderColor: 'orange' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>暂停任务</Button>
+            </Popconfirm>
+            <Popconfirm
+                title="确定启用?"
+                onConfirm={() => { handleDel({ projectIds: selectedRows.map(i => i.id) }, 'open') }}
+                disabled={selectedRows.length === 0}
+            >
+                <Button type='primary' style={{ backgroundColor: '#87d068', borderColor: '#87d068' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>启用任务</Button>
             </Popconfirm>
         </div>
         <Table

+ 11 - 6
src/pages/weComTask/page/groupChatSend/official/taskList/tableConfig.tsx

@@ -14,7 +14,7 @@ const taskListColumns = (
     corpList: DefaultOptionType[],
     handleLog: (data: any) => void,
     handleCopy: (data: any, isCopy: boolean) => void,
-    handleDel: (data: any, type: 'del' | 'cancel') => void,
+    handleDel: (data: any, type: 'del' | 'cancel' | 'open') => void,
 ): ColumnsType<AnyObject> => {
 
     return [
@@ -25,12 +25,17 @@ const taskListColumns = (
             width: 160,
             render(_, record) {
                 return <Space>
-                    <Popconfirm
-                        title="确定取消?"
+                   {record?.status === 1 ? <Popconfirm
+                        title="确定暂停?"
                         onConfirm={() => { handleDel({ projectIds: [record.id] }, 'cancel') }}
                     >
-                        <a style={{ color: 'orange' }}>取消任务</a>
-                    </Popconfirm>
+                        <a style={{ color: 'orange' }}>暂停任务</a>
+                    </Popconfirm> : record?.status === 3 ? <Popconfirm
+                        title="确定启用?"
+                        onConfirm={() => { handleDel({ projectIds: [record.id] }, 'open') }}
+                    >
+                        <a style={{ color: '#87d068' }}>启用任务</a>
+                    </Popconfirm> : undefined}
                     <a onClick={() => handleCopy(record, true)}>复制</a>
                     <a onClick={() => handleCopy(record, false)}>编辑</a>
                     <a onClick={() => handleLog(record)}>详情</a>
@@ -77,7 +82,7 @@ const taskListColumns = (
                         <Popover
                             placement="left"
                             content={<div>
-                                <PreviewStrategy strategyDTO={data} mpList={mpList} corpList={corpList} />
+                                <PreviewStrategy strategyDTO={data} />
                             </div>}
                             styles={{ body: { width: 380, overflow: 'hidden', overflowY: 'auto', maxHeight: 400 } }}
                         >

+ 7 - 0
src/pages/weComTask/page/groupChatSend/official/typings.d.ts

@@ -8,8 +8,15 @@ declare namespace OFFICIAL_CHAT_CREATE {
         mpList: DefaultOptionType[]
         corpList: DefaultOptionType[]
     }
+    interface MpAccountProps {
+        label: string;
+        value: number;
+        appid: string; // 公众号appid
+        corp?: any[];
+    }
     // 官方群发创建参数配置
     interface SettingsProps {
+        mpAccount?: MpAccountProps[];
         bizType?: string; // 业务类型
         platform?: string; // 书城
         channel?: string;   // 渠道

+ 2 - 3
src/pages/weComTask/page/groupChatSend/robot/create/components/Strategy/index.tsx

@@ -11,7 +11,7 @@ const Strategy: React.FC = () => {
     /*********************************/
     const { message } = App.useApp()
     const { token } = useNewToken()
-    const { setSettings, settings, onPreviewReset, mpList, corpList } = useContext(DispatchRobotChatCreate)!;
+    const { setSettings, settings, onPreviewReset, corpList } = useContext(DispatchRobotChatCreate)!;
     const [newVisible, setNewVisible] = useState<boolean>(false);
     /*********************************/
 
@@ -34,7 +34,7 @@ const Strategy: React.FC = () => {
                     <div className={style.detail_title}>群发策略配置</div>
                     <div className={style.detail_body}>
                         {settings?.strategyDTO && Object.keys(settings?.strategyDTO).length > 0 ? <>
-                            <PreviewStrategy strategyDTO={settings?.strategyDTO} mpList={mpList} corpList={corpList} />
+                            <PreviewStrategy strategyDTO={settings?.strategyDTO} corpList={corpList} />
                         </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                     </div>
                 </div>
@@ -48,7 +48,6 @@ const Strategy: React.FC = () => {
         {newVisible && <SettingsStrategy
             visible={newVisible}
             value={settings?.strategyDTO}
-            mpList={mpList}
             corpList={corpList}
             onClose={() => {
                 setNewVisible(false);

+ 1 - 56
src/pages/weComTask/page/groupChatSend/robot/create/components/Strategy/previewStrategy.tsx

@@ -5,16 +5,14 @@ import SendTimeSet from '@/pages/weComTask/components/sendTimeSet';
 import '../../../../../businessPlan/create/global.less'
 import FilterUser from '@/pages/weComTask/components/filterUser';
 import { DefaultOptionType } from 'antd/es/select';
-import SelectGroupChat from '@/pages/weComTask/API/SelectGroupChat';
 import SelectCorpUser from '@/pages/weComTask/page/corpUserManage/selectCorpUser';
 
 interface Props {
     strategyDTO: { [x: string]: any }
-    mpList: DefaultOptionType[]
     corpList: DefaultOptionType[]
 }
 
-const PreviewStrategy: React.FC<Props> = ({ strategyDTO, mpList, corpList }) => {
+const PreviewStrategy: React.FC<Props> = ({ strategyDTO, corpList }) => {
 
     /**************************************/
     const [form] = Form.useForm();
@@ -98,36 +96,6 @@ const PreviewStrategy: React.FC<Props> = ({ strategyDTO, mpList, corpList }) =>
                                                     <Form.Item {...restField} label={<strong>发送群对象名称</strong>} name={[name, 'sendGroupName']}>
                                                         <Input placeholder="请输入发送群对象名称" />
                                                     </Form.Item>
-                                                    <Form.Item
-                                                        {...restField}
-                                                        label={<strong>群聊关联公众号</strong>}
-                                                        name={[name, 'mpAccountId']}
-                                                    >
-                                                        <Select
-                                                            showSearch
-                                                            allowClear
-                                                            placeholder="选择公众号"
-                                                            filterOption={(input, option) =>
-                                                                (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                            }
-                                                            options={mpList}
-                                                        />
-                                                    </Form.Item>
-                                                    <Form.Item
-                                                        {...restField}
-                                                        label={<strong>群聊主体</strong>}
-                                                        name={[name, 'corpIds']}
-                                                    >
-                                                        <Select
-                                                            showSearch
-                                                            placeholder="选择群聊主体"
-                                                            filterOption={(input, option) =>
-                                                                (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                            }
-                                                            mode='multiple'
-                                                            options={corpList}
-                                                        />
-                                                    </Form.Item>
                                                     <Form.Item
                                                         {...restField}
                                                         label={<strong>机器人主体</strong>}
@@ -151,29 +119,6 @@ const PreviewStrategy: React.FC<Props> = ({ strategyDTO, mpList, corpList }) =>
                                                     >
                                                         <SelectCorpUser placeholder="请选择机器人号" corpId={robotCorpId} type="radio" />
                                                     </Form.Item>}
-                                                    <Form.Item
-                                                        {...restField}
-                                                        label={<strong>素材群主体</strong>}
-                                                        name={[name, 'chatCorpId']}
-                                                        rules={[{ required: true, message: '请选择素材群主体!' }]}
-                                                    >
-                                                        <Select
-                                                            showSearch
-                                                            placeholder="选择素材群主体"
-                                                            filterOption={(input, option) =>
-                                                                (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                            }
-                                                            options={corpList}
-                                                        />
-                                                    </Form.Item>
-                                                    {chatCorpId && <Form.Item
-                                                        {...restField}
-                                                        label={<strong>素材群</strong>}
-                                                        name={[name, 'corpGroupChat']}
-                                                        rules={[{ required: true, message: '请选择素材群!' }]}
-                                                    >
-                                                        <SelectGroupChat corpId={chatCorpId} />
-                                                    </Form.Item>}
                                                     <Form.Item label={<strong>发送群对象配置</strong>}>
                                                         <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
                                                             <Form.Item

+ 3 - 67
src/pages/weComTask/page/groupChatSend/robot/create/components/Strategy/settingsStrategy.tsx

@@ -7,11 +7,9 @@ import FilterUser from "@/pages/weComTask/components/filterUser";
 import { DefaultOptionType } from "antd/es/select";
 import NewSteps from "@/pages/weComTask/components/newSteps";
 import dayjs from 'dayjs';
-import SelectGroupChat from "@/pages/weComTask/API/SelectGroupChat";
 import SelectCorpUser from "@/pages/weComTask/page/corpUserManage/selectCorpUser";
 
 interface Props extends GROUP_CHAT_CREATE.FoundationProps<any> {
-    mpList: DefaultOptionType[]
     corpList: DefaultOptionType[]
 }
 /**
@@ -19,7 +17,7 @@ interface Props extends GROUP_CHAT_CREATE.FoundationProps<any> {
  * @param param0 
  * @returns 
  */
-const SettingsStrategy: React.FC<Props> = ({ visible, onClose, value, onChange, mpList, corpList }) => {
+const SettingsStrategy: React.FC<Props> = ({ visible, onClose, value, onChange, corpList }) => {
 
     /************************************/
     const { message } = App.useApp()
@@ -121,17 +119,14 @@ const SettingsStrategy: React.FC<Props> = ({ visible, onClose, value, onChange,
                 ((timeRepeatType === "TIME_TYPE_REPEAT_WEEK" || timeRepeatType === "TIME_TYPE_REPEAT_MONTH") && startTime && sendTime && repeatArray)
 
             const children = sendData?.map((sd, sdIndex) => {
-                const { sendGroupName, mpAccountId, corpIds, robotCorpId, robotCorpUser, chatCorpId, corpGroupChat, externalUserType, externalUserFilter } = sd
+                const { sendGroupName, robotCorpId, robotCorpUser, externalUserType, externalUserFilter } = sd
                 return {
                     title: `发送群对象${sdIndex + 1}`, id: `strategy_${index + 1}_sendData_${sdIndex + 1}`,
                     description: `名称、公众号、主体...`,
                     checked:
                         sendGroupName &&
-                        mpAccountId &&
-                        corpIds?.length &&
                         (externalUserType === 'all' ? true : externalUserFilter && Object.keys(externalUserFilter)?.length > 0) &&
-                        (robotCorpId ? robotCorpUser?.length > 0 : true) &&
-                        chatCorpId && Object.keys(corpGroupChat || {}).length > 0
+                        (robotCorpId ? robotCorpUser?.length > 0 : true)
                 }
             })
             return {
@@ -248,40 +243,6 @@ const SettingsStrategy: React.FC<Props> = ({ visible, onClose, value, onChange,
                                                         <Form.Item {...restField} label={<strong>发送群对象名称</strong>} name={[name, 'sendGroupName']} rules={[{ required: true, message: '请输入发送群对象名称!' }]}>
                                                             <Input placeholder="请输入发送群对象名称" style={{ width: 358 }} allowClear />
                                                         </Form.Item>
-                                                        <Form.Item
-                                                            {...restField}
-                                                            label={<strong>群聊关联公众号</strong>}
-                                                            name={[name, 'mpAccountId']}
-                                                        >
-                                                            <Select
-                                                                showSearch
-                                                                allowClear
-                                                                placeholder="选择公众号"
-                                                                filterOption={(input, option) =>
-                                                                    (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                                }
-                                                                style={{ width: 358 }}
-                                                                options={mpList}
-                                                            />
-                                                        </Form.Item>
-                                                        <Form.Item
-                                                            {...restField}
-                                                            label={<strong>群聊主体</strong>}
-                                                            name={[name, 'corpIds']}
-                                                            rules={[{ required: true, message: '请选择群聊主体!' }]}
-                                                        >
-                                                            <Select
-                                                                showSearch
-                                                                allowClear
-                                                                placeholder="选择群聊主体"
-                                                                filterOption={(input, option) =>
-                                                                    (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                                }
-                                                                mode="multiple"
-                                                                style={{ width: 358 }}
-                                                                options={corpList}
-                                                            />
-                                                        </Form.Item>
                                                         <Form.Item
                                                             {...restField}
                                                             label={<strong>机器人主体</strong>}
@@ -306,31 +267,6 @@ const SettingsStrategy: React.FC<Props> = ({ visible, onClose, value, onChange,
                                                         >
                                                             <SelectCorpUser placeholder="请选择机器人号" width={358} corpId={robotCorpId} type="radio" />
                                                         </Form.Item>}
-                                                        <Form.Item
-                                                            {...restField}
-                                                            label={<strong>素材群主体</strong>}
-                                                            name={[name, 'chatCorpId']}
-                                                            rules={[{ required: true, message: '请选择素材群主体!' }]}
-                                                        >
-                                                            <Select
-                                                                showSearch
-                                                                allowClear
-                                                                placeholder="选择素材群主体"
-                                                                filterOption={(input, option) =>
-                                                                    (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
-                                                                }
-                                                                style={{ width: 358 }}
-                                                                options={corpList}
-                                                            />
-                                                        </Form.Item>
-                                                        {chatCorpId && <Form.Item
-                                                            {...restField}
-                                                            label={<strong>素材群</strong>}
-                                                            name={[name, 'corpGroupChat']}
-                                                            rules={[{ required: true, message: '请选择素材群!' }]}
-                                                        >
-                                                            <SelectGroupChat corpId={chatCorpId} />
-                                                        </Form.Item>}
                                                         <Form.Item
                                                             label={<strong>发送群对象配置</strong>}
                                                             required

+ 3 - 11
src/pages/weComTask/page/groupChatSend/robot/create/components/content/index.tsx

@@ -2,7 +2,7 @@ import React, { useContext, useState } from 'react';
 import style from '../../../../../businessPlan/create/index.less'
 import useNewToken from '@/Hook/useNewToken';
 import { DispatchRobotChatCreate } from '../..';
-import { Button, Empty, message, Popconfirm } from 'antd';
+import { Button, Empty, message } from 'antd';
 import SettingsContent from './settingsContent';
 import PreviewContent from './previewContent';
 
@@ -10,7 +10,7 @@ const Content: React.FC = () => {
 
     /*******************************************/
     const { token } = useNewToken()
-    const { setSettings, settings, onPreviewReset, mpList, corpList } = useContext(DispatchRobotChatCreate)!;
+    const { setSettings, settings, onPreviewReset } = useContext(DispatchRobotChatCreate)!;
     const [newVisible, setNewVisible] = useState<boolean>(false);
     /*******************************************/
 
@@ -19,15 +19,6 @@ const Content: React.FC = () => {
             <div className={`${style.settingsBody_content_col}`} style={{ width: '100%' }}>
                 <div className={style.title}>
                     <span>内容</span>
-                    {/* {settings?.strategyDTO && Object.keys(settings?.strategyDTO).length > 0 && <Popconfirm
-                        title="确定清空?"
-                        onConfirm={() => {
-                            setSettings(undefined)
-                            onPreviewReset();
-                        }}
-                    >
-                        <a style={{ color: 'red' }}>清空</a>
-                    </Popconfirm>} */}
                 </div>
                 <div className={style.detail}>
                     <div className={style.detail_title}>群发内容配置</div>
@@ -60,6 +51,7 @@ const Content: React.FC = () => {
                     }
                 })
                 setNewVisible(false)
+                onPreviewReset();
             }}
         />}
     </>;

+ 43 - 0
src/pages/weComTask/page/groupChatSend/robot/create/const.ts

@@ -36,4 +36,47 @@ export const getRobotSendData = (data: { [x: string]: any }) => {
             }
         })
     }
+}
+
+
+interface CorpGroupChat {
+  chatId: string;
+  chatName: string;
+  corpId: string;
+  corpName: string;
+}
+
+interface InputItem {
+  chatCorpId: string;
+  corpGroupChat: CorpGroupChat;
+}
+
+interface OutputStructure {
+  [key: string]: CorpGroupChat[][];
+}
+
+export function transformData(input: Record<string, InputItem>): OutputStructure {
+  const output: OutputStructure = {};
+  const groupMap = new Map<string, Map<string, CorpGroupChat[]>>();
+
+  // 遍历输入数据并分类
+  Object.entries(input).forEach(([key, value]) => {
+    const [prefix, group, _] = key.split('_');
+    if (!groupMap.has(prefix)) {
+      groupMap.set(prefix, new Map<string, CorpGroupChat[]>());
+    }
+    const groupData = groupMap.get(prefix)!;
+    
+    if (!groupData.has(group)) {
+      groupData.set(group, []);
+    }
+    groupData.get(group)!.push(value.corpGroupChat);
+  });
+
+  // 转换为输出格式
+  groupMap.forEach((groups, prefix) => {
+    output[prefix] = Array.from(groups.values());
+  });
+
+  return output;
 }

+ 141 - 27
src/pages/weComTask/page/groupChatSend/robot/create/index.tsx

@@ -9,14 +9,15 @@ import Strategy from './components/Strategy';
 import { getBindMpListApi } from '@/pages/weComTask/API/corpUserAssign';
 import { DefaultOptionType } from 'antd/es/select';
 import { getCorpAllListApi } from '@/API/global';
-import { SaveOutlined, RedoOutlined, SearchOutlined, PlusOutlined } from '@ant-design/icons';
+import { SaveOutlined, RedoOutlined, SearchOutlined, PlusOutlined, CheckOutlined } from '@ant-design/icons';
 import Content from './components/content';
-import { removeEmptyValues } from '@/utils/utils';
+import { groupBy, removeEmptyValues } from '@/utils/utils';
 import { PreviewColumns } from './tableConfig';
 import SubmitModal from '../../../businessPlan/create/submitModal';
 import { addTaskApi, getCreateDetailsApi, updateTaskApi } from '@/pages/weComTask/API/businessPlan/create';
 import { useNavigate } from 'react-router-dom';
-import { getRobotSendData } from './const';
+import { getRobotSendData, transformData } from './const';
+import SelectMpCorp from '../../official/create/components/SelectMpCorp';
 
 
 export const DispatchRobotChatCreate = React.createContext<ROBOT_CHAT_CREATE.DispatchRobotChatCreate | null>(null);
@@ -39,6 +40,8 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
     const [corpList, setCorpList] = useState<DefaultOptionType[]>([])
     const [previewContent, setPreviewContent] = useState<{ [x: string]: { [x: string]: string } }>({})
     const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
+    const [corpVisible, setCorpVisible] = useState<boolean>(false)
+    const [corpGroupChat, setCorpGroupChat] = useState<{ [x: string]: any }>({})
 
     const welcomeMsgJobType = useAjax(() => welcomeMsgJobTypeApi())//获取业务类型
     const getBindMpList = useAjax(() => getBindMpListApi())
@@ -59,12 +62,20 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
             getCreateDetails.run(id).then(res => {
                 sessionStorage.removeItem('ROBOT_OFFICIALTASKID')
                 if (res?.data) {
-                    const { bizType, platform, templateProductId, channel, robotGroupChatSendTaskAddDTO } = res.data
+                    const { bizType, platform, templateProductId, channel, robotGroupChatSendTaskAddDTO, mpAccounts } = res.data
                     let newSettings: ROBOT_CHAT_CREATE.SettingsProps = {
                         bizType,
                         platform: Number(platform) as any,
                         templateProductId,
-                        channel
+                        channel,
+                        mpAccount: mpAccounts.map(m => {
+                            return {
+                                label: m?.mpAccountVO?.name || '',
+                                value: m?.mpAccountVO?.id || '',
+                                appId: m?.mpAccountVO?.appId || '',
+                                corp: m?.corpVOList?.map(c => ({ label: c.corpName, value: c.corpId })) || []
+                            }
+                        })
                     }
                     if (robotGroupChatSendTaskAddDTO && Object.keys(robotGroupChatSendTaskAddDTO).length > 0) {
                         const data = getRobotSendData(robotGroupChatSendTaskAddDTO)
@@ -116,11 +127,19 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
 
     // 预览
     const preview = () => {
-        const { bizType, platform, templateProductId, channel } = settings
+        const { bizType, platform, templateProductId, channel, mpAccount } = settings
         if (!(settings?.strategyDTO && Object.keys(settings?.strategyDTO).length > 0)) {
             message.error('请先配置内容')
             return
         }
+        if (!mpAccount?.length) {
+            message.error('请选择公众号')
+            return
+        }
+        if (mpAccount.some(item => !item?.corp?.length)) {
+            message.error('请选择关联主体')
+            return
+        }
         if (!bizType) {
             message.error('请选择业务类型')
             return
@@ -130,11 +149,12 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
             return
         }
         const list: any[] = []
-        let id = 1
         if (!settings?.strategyDTO?.strategyList?.every((str, s_index) => {
             const { sendData, ...sdto } = str
+            const count = sendData.reduce((pre, cur) => pre + (cur?.contentDTO?.length || 0), 0)
+            let saveIndex = 0
             return sendData?.every((sd, f_index) => {
-                const { contentDTO, mpAccountId, corpIds, robotCorpId, robotCorpUser, chatCorpId, corpGroupChat, ...cdto } = sd
+                const { contentDTO, robotCorpId, robotCorpUser, chatCorpId, corpGroupChat, ...cdto } = sd
                 if (contentDTO?.length) {
                     return contentDTO?.every((cd, c_index) => {
                         if (cd?.attachmentList?.length || cd?.text?.content) {
@@ -167,20 +187,13 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                                         return `<span style="color: red">请联系管理员</span>`
                                 }
                             })
-                            const weChat = mpList.find(item => item.value === mpAccountId)
                             list.push({
-                                id,
                                 bizType,
                                 platform,
                                 templateProductId,
                                 channel,
-                                taskName: settings?.strategyDTO?.taskName,
+                                groupSendName: settings?.strategyDTO?.groupSendName,
                                 strategyData: sdto,
-                                mpAccountId,
-                                weChatName: weChat?.label,
-                                mpAccountAppid: weChat?.appId,
-                                corpIds,
-                                corpNames: corpIds.map(corpId => corpList.find(item => item.value === corpId)?.label),
                                 robotCorpId,
                                 robotCorpName: corpList.find(item => item.value === robotCorpId)?.label,
                                 robotCorpUser,
@@ -194,9 +207,11 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                                 sendIndex: f_index,
                                 contentIndex: c_index,
                                 linkData,
-                                miniProgramData
+                                miniProgramData,
+                                contentRowSpan: c_index === 0 ? contentDTO.length : 0, // 用于表格合并
+                                strategyRowSpan: saveIndex === 0 ? count : 0
                             })
-                            id++;
+                            saveIndex += 1
                             return true
                         } else {
                             message.error(`策略:${str?.strategyName},发送对象:${sd?.sendGroupName}请填写发送内容`)
@@ -212,10 +227,27 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
         })) {
             return
         }
-        console.log('=========================>', list)
-        setPreviewData(list)
-        setPreviewDataOld(list)
+        let id = 1
+        const newPreviewData = mpAccount.reduce((pre, cur, index) => {
+            return pre.concat(...list.map((item, i) => {
+                return {
+                    ...item,
+                    id: id++,
+                    mpAccountId: cur.value,
+                    mpAccountName: cur.label,
+                    mpAccountAppid: (cur as any).appId,
+                    corpIds: cur.corp?.map(item => item.value),
+                    corpNames: cur.corp?.map(item => item.label),
+                    mpIndex: index,
+                    mpRowSpan: i === 0 ? list.length : 0, // 用于表格合并
+                }
+            }))
+        }, [])
+        console.log('=========================>', newPreviewData)
+        setPreviewData(newPreviewData)
+        setPreviewDataOld(newPreviewData)
         setPreviewContent({})
+        setCorpGroupChat({})
     }
 
     const tableSearch = useCallback((values) => {
@@ -233,6 +265,13 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
     }, [previewDataOld, previewData])
 
     const setTaskName = () => {
+        if (!settings.mpAccount?.every((mp) => settings?.strategyDTO?.strategyList?.every((str, s_index) => str.sendData?.every((sd, f_index) => {
+            const chat = corpGroupChat?.[mp.value + '_' + s_index + '_' + f_index]
+            return chat?.chatCorpId && chat?.corpGroupChat
+        })))) {
+            message.error('请补充群聊主体等信息')
+            return
+        }
         if (previewDataOld.every((item, index) => {
             if ((item?.linkData?.length > 0 ? previewContent?.[index + 1]?.linkUrl : true) && (item?.miniProgramData?.length > 0 ? (previewContent?.[index + 1]?.miniprogramAppid && previewContent?.[index + 1]?.miniprogramPage) : true)) {
                 return true
@@ -246,13 +285,32 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
     }
 
     const onSubmit = (values: any) => {
-        const { bizType, platform, templateProductId, channel, strategyDTO } = settings
+        console.log('---------->', corpGroupChat, transformData(corpGroupChat))
+        const corpGroupChatData = transformData(corpGroupChat)
+        const groupData = groupBy(previewDataOld, (item) => item['mpAccountId'], true);
+        const { bizType, platform, templateProductId, channel, strategyDTO, mpAccount } = settings
         const params = {
             ...values,
             bizType,
             platform,
             templateProductId,
             channel,
+            mpAccounts: mpAccount.map(item => {
+                const data = groupData[item.value] || []
+                const groupChatMsgContent: any[] = []
+                data.forEach(d => {
+                    const { strategyIndex, sendIndex, contentIndex, id } = d
+                    if (!groupChatMsgContent[strategyIndex]) groupChatMsgContent[strategyIndex] = [];
+                    if (!groupChatMsgContent[strategyIndex][sendIndex]) groupChatMsgContent[strategyIndex][sendIndex] = [];
+                    groupChatMsgContent[strategyIndex][sendIndex][contentIndex] = previewContent?.[id] || {};
+                })
+                return {
+                    mpAccountId: item.value,
+                    corpIds: item.corp?.map(c => c.value) || [],
+                    groupChatMsgContent,
+                    corpGroupChat: corpGroupChatData[item.value]
+                }
+            }),
             robotChatSendTaskAddDTO: {
                 ...strategyDTO,
                 strategyList: strategyDTO?.strategyList?.map(item => {
@@ -260,11 +318,10 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                     return {
                         ...its,
                         taskDetail: sendData.map(sd => {
-                            const { externalUserType, externalUserFilter, contentDTO, sendGroupName, chatCorpId, corpGroupChat, robotCorpId, robotCorpUser, ...itsd } = sd
+                            const { externalUserType, externalUserFilter, contentDTO, sendGroupName, robotCorpId, robotCorpUser, ...itsd } = sd
 
                             const detail: { [x: string]: any } = {
                                 ...itsd,
-                                corpGroupChat,
                                 contentDTO,
                                 externalUserFilterName: sendGroupName,
                             }
@@ -283,7 +340,7 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                 })
             }
         }
-        // console.log('--->', params)
+        console.log('--->', params)
         if (projectId) {
             params.projectId = projectId
             updateTask.run(params).then(res => {
@@ -297,7 +354,6 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
             })
         } else {
             addTask.run(params).then(res => {
-                console.log(res)
                 if (res?.data) {
                     modal.success({
                         content: '任务提交成功',
@@ -321,6 +377,46 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
         <Spin spinning={false}>
             <Card title={<strong>{projectId ? getCreateDetails?.data?.data?.projectName + '任务编辑' : ''}配置区</strong>} className={`${style.card} ${style.config}`}>
                 <Space wrap>
+                    <Space.Compact>
+                        <Button>群聊关联公众号</Button>
+                        <Select
+                            showSearch
+                            allowClear
+                            placeholder="选择公众号"
+                            filterOption={(input, option) =>
+                                (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                            style={{ minWidth: 200 }}
+                            maxTagCount={1}
+                            mode='multiple'
+                            options={mpList}
+                            value={settings?.mpAccount?.map(item => item.value)}
+                            onChange={(_, options) => {
+                                onPreviewReset()
+                                if (options.length > 0) {
+                                    const oldMpAccountIds = settings?.mpAccount?.reduce((acc, item) => {
+                                        acc[item.value] = item;
+                                        return acc;
+                                    }, {}) || []
+                                    setSettings({
+                                        ...settings,
+                                        mpAccount: options.map(item => {
+                                            if (oldMpAccountIds?.[item.value]) {
+                                                return oldMpAccountIds[item.value]
+                                            }
+                                            return item
+                                        })
+                                    })
+                                } else {
+                                    setSettings({ ...settings, mpAccount: [] })
+                                }
+                            }}
+                        />
+                    </Space.Compact>
+                    {settings?.mpAccount?.length > 0 && <Button onClick={() => setCorpVisible(true)}>
+                        选择关联主体
+                        {settings?.mpAccount?.some(item => item?.corp?.length) && <CheckOutlined style={{ color: settings?.mpAccount?.every(item => item?.corp?.length) ? '#1890ff' : '#52C41A' }} />}
+                    </Button>}
                     <Space.Compact>
                         <Button>业务类型</Button>
                         <Select
@@ -478,7 +574,7 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                 </Form>
                 <Table
                     dataSource={previewData}
-                    columns={PreviewColumns(bookPlatForm, bookList, bookPlatForm.find(item => item.id === Number(settings?.platform)).platformKey, setPreviewContent, previewContent)}
+                    columns={PreviewColumns(corpList, bookPlatForm, bookList, bookPlatForm.find(item => item.id === Number(settings?.platform)).platformKey, setPreviewContent, previewContent, corpGroupChat, setCorpGroupChat)}
                     rowKey={'id'}
                     bordered={true}
                     scroll={{ y: 550 }}
@@ -505,6 +601,24 @@ const OfficialCreate: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE
                 setSubVisible(false)
             }}
         />}
+
+        {/* 选择关联主体 */}
+        {corpVisible && <SelectMpCorp
+            corpList={corpList}
+            visible={corpVisible}
+            mpAccount={settings?.mpAccount}
+            onChange={(mpAccount) => {
+                setSettings({
+                    ...settings,
+                    mpAccount
+                })
+                setCorpVisible(false)
+                onPreviewReset()
+            }}
+            onClose={() => {
+                setCorpVisible(false)
+            }}
+        />}
     </div>;
 };
 

+ 104 - 40
src/pages/weComTask/page/groupChatSend/robot/create/tableConfig.tsx

@@ -1,20 +1,57 @@
-import { Input, Typography } from "antd";
+import { Input, Select, Typography } from "antd";
 import { AnyObject } from "antd/es/_util/type"
 import { ColumnsType } from "antd/es/table"
 import { TIME_TYPE, welcomeContentData } from "../../../businessPlan/create/const";
 import FilterUser from "@/pages/weComTask/components/filterUser";
 import ShowContentTable from "../../../businessPlan/create/components/massSending/showContentTable";
 import SelectBookLinkButton from "../../../bookLink/SelectBookLinkButton";
+import { DefaultOptionType } from "antd/es/select";
+import SelectGroupChat from "@/pages/weComTask/API/SelectGroupChat";
 const { Paragraph, Title } = Typography;
 
 
-export const PreviewColumns = (bookPlatForm: TASK_CREATE.BookPlatFormProps[], bookList: TASK_CREATE.BookListProps[], platform: string, setPreviewContent: React.Dispatch<React.SetStateAction<{ [x: string]: { [x: string]: string; }; }>>, previewContent: { [x: string]: { [x: string]: string } }): ColumnsType<AnyObject> => {
+export const PreviewColumns = (
+    corpList: DefaultOptionType[],
+    bookPlatForm: TASK_CREATE.BookPlatFormProps[],
+    bookList: TASK_CREATE.BookListProps[],
+    platform: string,
+    setPreviewContent: React.Dispatch<React.SetStateAction<{ [x: string]: { [x: string]: string; }; }>>,
+    previewContent: { [x: string]: { [x: string]: string } },
+    corpGroupChat: { [x: string]: any },
+    setCorpGroupChat: React.Dispatch<React.SetStateAction<{ [x: string]: any; }>>
+): ColumnsType<AnyObject> => {
 
     return [
+        {
+            title: '群聊关注公众号',
+            dataIndex: 'mpAccountName',
+            key: 'mpAccountName',
+            width: 120,
+            ellipsis: true,
+            render(value) {
+                return value || '--'
+            },
+            onCell: (record) => {
+                return { rowSpan: record.mpRowSpan }
+            }
+        },
+        {
+            title: '企微主体',
+            dataIndex: 'corpNames',
+            key: 'corpNames',
+            width: 160,
+            ellipsis: true,
+            render(value) {
+                return value ? value.join('、') : '--'
+            },
+            onCell: (record) => {
+                return { rowSpan: record.mpRowSpan }
+            }
+        },
         {
             title: '基础信息',
-            dataIndex: 'taskName',
-            key: 'taskName',
+            dataIndex: 'groupSendName',
+            key: 'groupSendName',
             width: 130,
             render(value, record) {
                 return <>
@@ -23,6 +60,9 @@ export const PreviewColumns = (bookPlatForm: TASK_CREATE.BookPlatFormProps[], bo
                     {record?.platform && <Paragraph style={{ margin: 0 }}>书城:{record?.platform ? bookPlatForm?.find(item => item.id === record?.platform)?.platformName : '<空>'}</Paragraph>}
                     {record?.templateProductId && <Paragraph style={{ margin: 0 }}>适用产品:{record?.templateProductId ? bookList?.find(item => item.id === record?.templateProductId)?.bookName : '<空>'}</Paragraph>}
                 </>
+            },
+            onCell: (record) => {
+                return { rowSpan: record.mpRowSpan }
             }
         },
         {
@@ -40,27 +80,10 @@ export const PreviewColumns = (bookPlatForm: TASK_CREATE.BookPlatFormProps[], bo
                     {value?.sendTime && <Paragraph style={{ margin: 0 }}>执行时间:{value?.sendTime}</Paragraph>}
                     {value?.repeatArray && <Paragraph style={{ margin: 0 }}>执行天数:{value?.repeatArray.join('、')}</Paragraph>}
                 </>
-            }
-        },
-        {
-            title: '群聊关注公众号',
-            dataIndex: 'weChatName',
-            key: 'weChatName',
-            width: 120,
-            ellipsis: true,
-            render(value) {
-                return value || '--'
-            },
-        },
-        {
-            title: '群聊企微主体',
-            dataIndex: 'corpNames',
-            key: 'corpNames',
-            width: 160,
-            ellipsis: true,
-            render(value) {
-                return value ? value.join('、') : '--'
             },
+            onCell: (record) => {
+                return { rowSpan: record.strategyRowSpan }
+            }
         },
         {
             title: '机器人',
@@ -70,23 +93,13 @@ export const PreviewColumns = (bookPlatForm: TASK_CREATE.BookPlatFormProps[], bo
             ellipsis: true,
             render(value, record) {
                 return value ? <div style={{ width: '100%' }}>
-                    <Title level={5} style={{ margin: 0 }}>主体:{value}</Title>
-                    <Paragraph style={{ margin: 0 }}>机器人号:{record?.robotCorpUser?.map(item => item.name)?.join('、') || '<空>'}</Paragraph>
-                </div> : '--'
-            },
-        },
-        {
-            title: '素材群',
-            dataIndex: 'chatCorpName',
-            key: 'chatCorpName',
-            width: 160,
-            ellipsis: true,
-            render(value, record) {
-                return value ? <div style={{ width: '100%' }}>
-                    <Title ellipsis level={5} style={{ margin: 0 }}>主体:{value}</Title>
-                    <Paragraph style={{ margin: 0 }} ellipsis>素材群:{record?.corpGroupChat?.chatName || record?.corpGroupChat?.chatId}</Paragraph>
+                    <Title level={5} ellipsis style={{ margin: 0 }}>主体:{value}</Title>
+                    <Paragraph style={{ margin: 0 }} ellipsis>机器人号:{record?.robotCorpUser?.map(item => item.name)?.join('、') || '<空>'}</Paragraph>
                 </div> : '--'
             },
+            onCell: (record) => {
+                return { rowSpan: record.contentRowSpan }
+            }
         },
         {
             title: '发送对象',
@@ -103,7 +116,58 @@ export const PreviewColumns = (bookPlatForm: TASK_CREATE.BookPlatFormProps[], bo
                 </>
             },
             onCell: (record) => {
-                return { rowSpan: record.sendDataRowSpan }
+                return { rowSpan: record.contentRowSpan }
+            }
+        },
+        {
+            title: '素材群主体',
+            dataIndex: 'chatCorpId',
+            key: 'chatCorpId',
+            width: 250,
+            render(_, record) {
+                const key = record.mpAccountId + '_' + record.strategyIndex + '_' + record.sendIndex
+                return <Select
+                    showSearch
+                    allowClear
+                    placeholder="选择素材群主体"
+                    filterOption={(input, option) =>
+                        (option?.label as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                    style={{ width: '100%' }}
+                    options={corpList}
+                    value={corpGroupChat?.[key]?.chatCorpId}
+                    onChange={(e) => {
+                        const newCorpGroupChat = JSON.parse(JSON.stringify(corpGroupChat))
+                        const c = corpGroupChat?.[key] || {}
+                        newCorpGroupChat[key] = { ...c, chatCorpId: e }
+                        setCorpGroupChat(newCorpGroupChat)
+                    }}
+                />
+            },
+            onCell: (record) => {
+                return { rowSpan: record.contentRowSpan }
+            }
+        },
+        {
+            title: '素材群',
+            dataIndex: 'corpGroupChat',
+            key: 'corpGroupChat',
+            width: 250,
+            render(_, record) {
+                const key = record.mpAccountId + '_' + record.strategyIndex + '_' + record.sendIndex
+                return corpGroupChat?.[key]?.chatCorpId ? <SelectGroupChat
+                    corpId={corpGroupChat?.[key]?.chatCorpId}
+                    value={corpGroupChat?.[key]?.corpGroupChat as any}
+                    onChange={(e) => {
+                        const newCorpGroupChat = JSON.parse(JSON.stringify(corpGroupChat))
+                        const c = corpGroupChat?.[key] || {}
+                        newCorpGroupChat[key] = { ...c, corpGroupChat: e }
+                        setCorpGroupChat(newCorpGroupChat)
+                    }}
+                /> : <span>请先选择素材群主体</span>
+            },
+            onCell: (record) => {
+                return { rowSpan: record.contentRowSpan }
             }
         },
         {

+ 33 - 0
src/pages/weComTask/page/groupChatSend/robot/taskList/components/groupTask/index.tsx

@@ -39,6 +39,28 @@ const GroupTask: React.FC<Props> = ({ robotGroupChatSendTaskVOList }) => {
                 ellipsis: true,
                 align: 'center'
             },
+            {
+                title: '关联公众号',
+                dataIndex: 'mpAccountInfo',
+                key: 'mpAccountInfo',
+                width: 100,
+                ellipsis: true,
+                align: 'center',
+                render(value) {
+                    return value?.name || '--'
+                },
+            },
+            {
+                title: '关联主体',
+                dataIndex: 'corpInfoList',
+                key: 'corpInfoList',
+                width: 100,
+                ellipsis: true,
+                align: 'center',
+                render(value) {
+                    return value?.map(item => item.corpName)?.join('、') || '--'
+                },
+            },
             {
                 title: '状态',
                 dataIndex: 'status',
@@ -119,6 +141,17 @@ const GroupTask: React.FC<Props> = ({ robotGroupChatSendTaskVOList }) => {
                     return value?.chatName || '--'
                 },
             },
+            {
+                title: '机器人',
+                dataIndex: 'corpUserMap',
+                key: 'corpUserMap',
+                width: 100,
+                ellipsis: true,
+                align: 'center',
+                render: (value) => {
+                    return value ? Object.keys(value).map(key => value[key]).join(',') : '--'
+                },
+            },
             {
                 title: '投手',
                 dataIndex: 'putUserName',

+ 14 - 6
src/pages/weComTask/page/groupChatSend/robot/taskList/index.tsx

@@ -71,7 +71,7 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
     }
 
     // 删除
-    const handleDel = (data: { projectIds: number[] }, type: 'del' | 'cancel') => {
+    const handleDel = (data: { projectIds: number[] }, type: 'del' | 'cancel' | 'open') => {
         const hide = message.loading(type === 'del' ? '正在删除...' : '正在取消...', 0)
         switch (type) {
             case 'del':
@@ -87,14 +87,15 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
                 }).catch(() => hide())
                 break
             case 'cancel':
-                cancelProject.run(data).then(res => {
+            case 'open':
+                cancelProject.run({ ...data, pause: type === 'cancel' ? false : true }).then(res => {
                     hide()
                     setselectedRows([])
                     if (res?.data) {
-                        message.success('取消成功')
+                        message.success(type === 'cancel' ? '取消成功' : '启用成功')
                         getProjectList.refresh()
                     } else {
-                        message.error('取消失败')
+                        message.error(type === 'cancel' ? '取消失败' : '启用失败')
                     }
                 }).catch(() => hide())
                 break
@@ -141,11 +142,18 @@ const TaskList: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookL
                 <Button type='primary' danger icon={<DeleteOutlined />} loading={delProject.loading} disabled={selectedRows.length === 0}>删除</Button>
             </Popconfirm>
             <Popconfirm
-                title="确定取消?"
+                title="确定暂停?"
                 onConfirm={() => { handleDel({ projectIds: selectedRows.map(i => i.id) }, 'cancel') }}
                 disabled={selectedRows.length === 0}
             >
-                <Button type='primary' style={{ backgroundColor: 'orange', borderColor: 'orange' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>取消任务</Button>
+                <Button type='primary' style={{ backgroundColor: 'orange', borderColor: 'orange' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>暂停任务</Button>
+            </Popconfirm>
+            <Popconfirm
+                title="确定启用?"
+                onConfirm={() => { handleDel({ projectIds: selectedRows.map(i => i.id) }, 'open') }}
+                disabled={selectedRows.length === 0}
+            >
+                <Button type='primary' style={{ backgroundColor: '#87d068', borderColor: '#87d068' }} loading={cancelProject.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0}>启用任务</Button>
             </Popconfirm>
         </div>
         <Table

+ 11 - 6
src/pages/weComTask/page/groupChatSend/robot/taskList/tableConfig.tsx

@@ -14,7 +14,7 @@ const taskListColumns = (
     corpList: DefaultOptionType[],
     handleLog: (data: any) => void,
     handleCopy: (data: any, isCopy: boolean) => void,
-    handleDel: (data: any, type: 'del' | 'cancel') => void,
+    handleDel: (data: any, type: 'del' | 'cancel' | 'open') => void,
 ): ColumnsType<AnyObject> => {
 
     return [
@@ -25,12 +25,17 @@ const taskListColumns = (
             width: 160,
             render(_, record) {
                 return <Space>
-                    <Popconfirm
-                        title="确定取消?"
+                    {record?.status === 1 ? <Popconfirm
+                        title="确定暂停?"
                         onConfirm={() => { handleDel({ projectIds: [record.id] }, 'cancel') }}
                     >
-                        <a style={{ color: 'orange' }}>取消任务</a>
-                    </Popconfirm>
+                        <a style={{ color: 'orange' }}>暂停任务</a>
+                    </Popconfirm> : record?.status === 3 ? <Popconfirm
+                        title="确定启用?"
+                        onConfirm={() => { handleDel({ projectIds: [record.id] }, 'open') }}
+                    >
+                        <a style={{ color: '#87d068' }}>启用任务</a>
+                    </Popconfirm> : undefined}
                     <a onClick={() => handleCopy(record, true)}>复制</a>
                     <a onClick={() => handleCopy(record, false)}>编辑</a>
                     <a onClick={() => handleLog(record)}>详情</a>
@@ -77,7 +82,7 @@ const taskListColumns = (
                         <Popover
                             placement="left"
                             content={<div>
-                                <PreviewStrategy strategyDTO={data} mpList={mpList} corpList={corpList} />
+                                <PreviewStrategy strategyDTO={data} corpList={corpList} />
                             </div>}
                             styles={{ body: { width: 500, overflow: 'hidden', overflowY: 'auto', maxHeight: 400 } }}
                         >

+ 1 - 0
src/pages/weComTask/page/groupChatSend/robot/typings.d.ts

@@ -10,6 +10,7 @@ declare namespace ROBOT_CHAT_CREATE {
     }
     // 官方群发创建参数配置
     interface SettingsProps {
+        mpAccount?: MpAccountProps[];
         bizType?: string; // 业务类型
         platform?: string; // 书城
         channel?: string;   // 渠道

+ 89 - 0
src/pages/weComTask/page/weAssociation/list/chatLog.tsx

@@ -0,0 +1,89 @@
+import { useAjax } from '@/Hook/useAjax';
+import { getMpChangeRecordListApi } from '@/pages/weComTask/API/global';
+import { Modal, Table } from 'antd';
+import React, { useEffect } from 'react';
+
+
+interface ChatLogProps {
+    corpId: string;
+    chatData: any;
+    visible?: boolean;
+    onClose?: () => void;
+}
+
+/**
+ * 群聊变更记录
+ * @returns 
+ */
+const ChatLog: React.FC<ChatLogProps> = ({ corpId, chatData, visible, onClose }) => {
+
+    /*********************************************/
+    const getMpChangeRecordList = useAjax((params) => getMpChangeRecordListApi(params))
+    /*********************************************/
+
+    useEffect(() => {
+        if (corpId && chatData?.chatId) {
+            getMpChangeRecordList.run({ corpId, chatId: chatData.chatId })
+        }
+    }, [corpId, chatData])
+
+    return <Modal
+        title={<strong>{chatData?.chatName} 群聊变更记录</strong>}
+        open={visible}
+        onCancel={onClose}
+        footer={null}
+        width={700}
+        maskClosable={false}
+    >
+        <Table
+            dataSource={getMpChangeRecordList?.data?.data}
+            loading={getMpChangeRecordList?.loading}
+            columns={[
+                {
+                    title: '公众号',
+                    dataIndex: 'mpAccount',
+                    key: 'mpAccount',
+                    ellipsis: true,
+                    align: 'center',
+                    width: 100,
+                    render: (text: any) => { 
+                        return text?.name || '--';
+                    },
+                },
+                {
+                    title: '开始时间',
+                    dataIndex: 'beginTime',
+                    key: 'beginTime',
+                    ellipsis: true,
+                    align: 'center',
+                    width: 130
+                },
+                {
+                    title: '结束时间',
+                    dataIndex: 'endTime',
+                    key: 'endTime',
+                    ellipsis: true,
+                    align: 'center',
+                    width: 130,
+                    render: (text: any) => {
+                        return text || '--';
+                    }
+                },
+                {
+                    title: '创建时间',
+                    dataIndex: 'createTime',
+                    key: 'createTime',
+                    ellipsis: true,
+                    align: 'center',
+                    width: 130
+                },
+            ]}
+            scroll={{ y: 450 }}
+            rowKey={'id'}
+            bordered
+            size='small'
+        />
+    </Modal>;
+};
+
+export default React.memo(ChatLog);

+ 65 - 0
src/pages/weComTask/page/weAssociation/list/disbandChatLog.tsx

@@ -0,0 +1,65 @@
+import { useAjax } from "@/Hook/useAjax"
+import { Button, Modal, Table, Tag } from "antd"
+import React, { useEffect, useState } from "react"
+import { TableConfigLog } from "./tableConfig"
+import { getDisbandChatLogListApi, getDisbandChatLogListProps } from "@/pages/weComTask/API/global"
+
+
+
+const DisbandChatLog: React.FC<{ corpId: string }> = ({ corpId }) => {
+
+    /***************************************/
+    const [visible, setVisible] = useState<boolean>(false)
+    const [queryParams, setQueryParmas] = useState<getDisbandChatLogListProps>({ pageNum: 1, pageSize: 20, corpId })
+
+    const getDisbandChatLogList = useAjax((params) => getDisbandChatLogListApi(params))
+    /***************************************/
+
+    useEffect(() => {
+        if (visible) {
+            getDisbandChatLogList.run({ ...queryParams, corpId })
+        }
+    }, [queryParams, visible, corpId])
+
+    return <>
+        <Button onClick={() => {
+            setQueryParmas({ pageNum: 1, pageSize: 20, corpId })
+            setVisible(true)
+        }}>解散群聊日志</Button>
+
+        {visible && <Modal
+            title={<strong>解散群聊日志</strong>}
+            open={visible}
+            onCancel={() => setVisible(false)}
+            footer={null}
+            width={700}
+        >
+            <Table
+                dataSource={getDisbandChatLogList?.data?.data?.records}
+                loading={getDisbandChatLogList?.loading}
+                columns={TableConfigLog()}
+                scroll={{ y: 450 }}
+                rowKey={(s) => {
+                    return s.id
+                }}
+                bordered
+                size='small'
+                pagination={{
+                    total: getDisbandChatLogList?.data?.data?.total,
+                    showTotal: (total) => <Tag color="cyan">总共{total}数据</Tag>,
+                    showSizeChanger: true,
+                    showLessItems: true,
+                    defaultCurrent: 1,
+                    defaultPageSize: 20,//默认初始的每页条数
+                    current: getDisbandChatLogList?.data?.data?.current || 1,
+                    pageSize: getDisbandChatLogList?.data?.data?.size || 20,
+                    onChange: (page, pageSize) => {
+                        setQueryParmas({ ...queryParams, pageNum: page, pageSize })
+                    }
+                }}
+            />
+        </Modal>}
+    </>
+}
+
+export default React.memo(DisbandChatLog)

+ 380 - 0
src/pages/weComTask/page/weAssociation/list/index.tsx

@@ -0,0 +1,380 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import style from '../../corpUserManage/index.less'
+import { App, Button, Card, DatePicker, Drawer, Input, Pagination, Popconfirm, Select, Space, Table, Tabs, Tag } from 'antd';
+import { MenuUnfoldOutlined, MenuFoldOutlined, SearchOutlined } from '@ant-design/icons';
+import { useAjax } from '@/Hook/useAjax';
+import { api_corpUser_allOfUser, getAdAccountAllOfMember } from '@/API/global';
+import TeamMembers from '@/components/Team/teamMembers';
+import SearchBox from '@/pages/weComTask/components/searchBox';
+import { ApiParamsChatListProps, disbandChatApi, getGroupChatCountApi, getGroupChatListApi } from '@/pages/weComTask/API/global';
+import { useSize } from 'ahooks';
+import { TableConfig } from './tableConfig';
+import UserContent from './userContent';
+import RemarkSet from './remarkSet';
+import DisbandChatLog from './disbandChatLog';
+import ZpMp from './zpMp';
+import ChatLog from './chatLog';
+
+const WeAssociationList: React.FC = () => {
+
+    /************************************/
+    const { message } = App.useApp()
+    const ref = useRef<HTMLDivElement>(null)
+    const size = useSize(ref)
+    const [activeKey, setActiveKey] = useState<string>('1')
+    const [showLeft, setShowLeft] = useState<boolean>(false)
+    const userIdStr = sessionStorage.getItem('userId');
+    const [userId, setUserId] = useState<number>(userIdStr ? Number(userIdStr) : undefined);
+    const [listData, setListData] = useState<ApiParamsChatListProps>({
+        pageNum: 1,
+        pageSize: 20
+    })
+    const [userQuerys, setUserQuerys] = useState<{
+        numArr: Array<number | undefined>,
+        value: number | null,
+        options: { value: number, label: string }[]
+    }>({
+        numArr: [0, 100],
+        value: null,
+        options: []
+    })
+    const [open, setOpen] = useState(false)
+    const [activeCorp, setActiveCorp] = useState<any>()
+    const [editSelectedRow, setEditSelectedRow] = useState<any[]>([])
+    const [userData, setUserData] = useState<any>()
+    const [remarkVisible, setRemarkVisible] = useState<boolean>(false)
+    const [zpVisible, setZpVisible] = useState<boolean>(false)
+    const [chatLogData, setChatLogData] = useState<{ visible: boolean, chatData: any }>();
+
+    const corpUser_allOfUser = useAjax((params) => api_corpUser_allOfUser(params))
+    const getGroupChatList = useAjax((params) => getGroupChatListApi(params))
+    const getGroupChatCount = useAjax((params) => getGroupChatCountApi(params))
+    const allOfMember = useAjax(() => getAdAccountAllOfMember())
+    const disbandChat = useAjax((params) => disbandChatApi(params))
+    /************************************/
+
+    useEffect(() => {
+        allOfMember.run()
+    }, [])
+
+    useEffect(() => {
+        if (userId) {
+            corpUser_allOfUser.run(userId).then(res => {
+                setActiveCorp(res?.data?.[0])
+                setListData({ ...listData, corpId: res?.data?.[0]?.t1?.corpId, pageNum: 1 })
+            })
+        }
+    }, [userId])
+
+    // 初始请求
+    useEffect(() => {
+        if (listData.corpId) {
+            getList()
+        } else if (getGroupChatList?.data) {
+            getGroupChatList.mutate({ data: { records: [], size: 20, total: 0, pages: 0, current: 1 } })
+            getGroupChatCount.mutate({ data: {} })
+        }
+    }, [listData])
+
+    //搜索事件
+    const getList = () => {
+        getGroupChatList.run({ ...listData })
+        getGroupChatCount.run({ ...listData })
+    }
+
+
+    // 人数输入确定
+    const setNum = useCallback(() => {
+        let options = [
+            { value: 1, label: `最小${userQuerys.numArr[0]}人` + '~' + `最大${userQuerys.numArr[1]}人` }
+        ]
+        setUserQuerys({
+            ...userQuerys,
+            value: 1,
+            options
+        })
+        setListData({ ...listData, userCountMin: userQuerys.numArr[0], userCountMax: userQuerys.numArr[1], })
+        setOpen(false)
+    }, [userQuerys, listData])
+
+
+    //查看客户
+    const lockUsers = (data: any) => {
+        setUserData(data)
+    }
+
+    // 解散群聊
+    const disbandChatHandle = () => {
+        disbandChat.run({ chatIdList: editSelectedRow.map(item => item.chatId), corpId: listData.corpId }).then(res => {
+            if (res?.data) {
+                message.success('任务提交成功')
+                getGroupChatList.refresh()
+            }
+        })
+    }
+
+    const handleChatLog = (d: any) => {
+        setChatLogData({ visible: true, chatData: d });
+    }
+
+    return <div className={style.corpUserManage}>
+        <Tabs
+            tabBarStyle={{ marginBottom: 1 }}
+            activeKey={activeKey}
+            type="card"
+            onChange={(activeKey) => {
+                if (activeKey !== 'contract') {
+                    if (activeKey === '1') {
+                        setUserId(Number(userIdStr));
+                    }
+                    setActiveKey(activeKey)
+                } else {
+                    setShowLeft(!showLeft)
+                }
+            }}
+            items={[{ label: '我的', key: '1' }, { label: '组员', key: '2' }, { label: showLeft ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />, key: 'contract' }]}
+        />
+        <div className={style.corpUserManage_bottom}>
+            {!showLeft && activeKey === '2' && <TeamMembers
+                allOfMember={allOfMember}
+                onChange={(putUserId) => {
+                    setUserId(putUserId);
+                }}
+                value={userId}
+            />}
+
+            <Card className={style.corpUserList} styles={{ body: { padding: 0, display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' } }}>
+                <SearchBox
+                    bodyPadding={`16px 16px 12px`}
+                    buttons={<>
+                        <Button type="primary" onClick={() => getList()} icon={<SearchOutlined />}>搜索</Button>
+                        <Button onClick={() => setRemarkVisible(true)} disabled={editSelectedRow.length === 0} type="primary">添加备注</Button>
+                        <Popconfirm
+                            title="确定解散群聊?"
+                            onConfirm={disbandChatHandle}
+                            disabled={editSelectedRow.length === 0}
+                        >
+                            <Button disabled={editSelectedRow.length === 0} loading={disbandChat.loading} danger type="primary">解散群聊</Button>
+                        </Popconfirm>
+                        <DisbandChatLog corpId={listData.corpId} />
+                        <Button onClick={() => setZpVisible(true)} disabled={editSelectedRow.length === 0} type="primary">指派公众号</Button>
+                    </>}
+                >
+                    <>
+                        <Select
+                            style={{ width: 180 }}
+                            placeholder="选择企业"
+                            value={listData?.corpId}
+                            showSearch
+                            maxTagCount={1}
+                            onChange={(e, option: any) => {
+                                setListData({ ...listData, corpId: e, pageNum: 1 })
+                                setActiveCorp(option?.data)
+                            }}
+                            filterOption={(input: string, option?: { label: string; value: string }) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
+                            options={corpUser_allOfUser?.data?.data?.map((item: { t1: any }) => ({ label: item.t1?.corpName, value: item.t1?.corpId, data: item }))}
+                        />
+                        <Input placeholder="群名称"
+                            allowClear
+                            style={{ maxWidth: 150 }}
+                            onChange={(e) => {
+                                let v = e.target.value
+                                setListData({ ...listData, chatName: v })
+                            }}
+                        />
+                        <Select
+                            style={{ minWidth: 150 }}
+                            placeholder="选择群主"
+                            value={listData?.corpUserId}
+                            showSearch
+                            allowClear
+                            mode="multiple"
+                            maxTagCount={1}
+                            onChange={(e) => {
+                                setListData({ ...listData, corpUserId: e, pageNum: 1 })
+                            }}
+                            filterOption={(input: string, option?: { label: string; value: string }) =>
+                                (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
+                            options={activeCorp?.t2?.map((item: { name: any; corpUserId: any }) => ({ label: item.name, value: item.corpUserId }))}
+                        />
+                        <DatePicker.RangePicker placeholder={['建群开始日期', '建群结束日期']} allowClear onChange={(dates, dateStrings) => {
+                            console.log(dateStrings)
+                            setListData({ ...listData, createTimeStart: dateStrings[0], createTimeEnd: dateStrings[1] })
+                        }} />
+                        <Select
+                            style={{ width: 200 }}
+                            placeholder='群人数'
+                            value={userQuerys.value}
+                            options={userQuerys.options}
+                            allowClear
+                            onChange={(value) => {
+                                if (!value) {
+                                    setUserQuerys({
+                                        numArr: [0, 100],
+                                        value: null,
+                                        options: []
+                                    })
+                                    let { userCountMin, userCountMax, ...newObj } = listData
+                                    setListData(newObj)
+                                }
+                                console.log(value)
+                            }}
+                            open={open}
+                            onDropdownVisibleChange={(open) => {
+                                setOpen(open)
+                            }}
+                            dropdownRender={() => {
+                                return <div >
+                                    <div style={{ display: 'flex', flexFlow: 'row', alignItems: 'center' }}>
+                                        <Input placeholder="最小人数" allowClear value={userQuerys.numArr[0]} onChange={(e) => {
+                                            let value = e.target.value
+                                            if (!isNaN(Number(value)) && !value.includes('.')) {
+                                                console.log(11111)
+                                                let newArr = userQuerys.numArr
+                                                newArr[0] = Number(value)
+                                                setUserQuerys({ ...userQuerys, numArr: newArr })
+                                            }
+                                        }} />~<Input placeholder="最大人数" allowClear value={userQuerys.numArr[1]} onChange={(e) => {
+                                            let value = e.target.value
+                                            if (!isNaN(Number(value)) && !value.includes('.')) {
+                                                let newArr = userQuerys.numArr
+                                                newArr[1] = Number(value)
+                                                setUserQuerys({ ...userQuerys, numArr: newArr })
+                                            }
+                                        }} />
+                                    </div>
+                                    <Button style={{ width: '100%', marginTop: 5 }} type='primary' onClick={setNum}>确定</Button>
+                                </div>
+                            }}
+                        />
+                        <Input placeholder="群备注"
+                            allowClear
+                            style={{ maxWidth: 150 }}
+                            onChange={(e) => {
+                                let v = e.target.value
+                                setListData({ ...listData, remark: v })
+                            }}
+                        />
+                    </>
+                </SearchBox>
+                <div className={style.corpUserList_table} ref={ref}>
+                    <Table
+                        style={{ marginBottom: 1 }}
+                        dataSource={getGroupChatList?.data?.data?.records}
+                        loading={getGroupChatList?.loading}
+                        columns={TableConfig(lockUsers, handleChatLog)}
+                        scroll={{ y: size?.height && ref.current ? size?.height - ref.current.querySelector('.ant-table-thead').clientHeight - 38 : 300 }}
+                        rowKey={'id'}
+                        bordered
+                        pagination={false}
+                        size='small'
+                        summary={() => (
+                            <Table.Summary fixed>
+                                <Table.Summary.Row>
+                                    <Table.Summary.Cell index={0} colSpan={2} align="center">
+                                        <Space size={0}>
+                                            <strong style={{ fontSize: 16 }}>群总数:</strong>
+                                            <strong style={{ fontSize: 16 }}>{getGroupChatCount.data?.data?.groupChatCount}</strong>
+                                        </Space>
+                                    </Table.Summary.Cell>
+                                    <Table.Summary.Cell index={1} colSpan={2} align="center">
+                                        <Space>
+                                            <strong style={{ fontSize: 16 }}>群成员总数:</strong>
+                                            <strong style={{ fontSize: 16 }}>{getGroupChatCount.data?.data?.groupChatUserCount}</strong>
+                                        </Space>
+                                    </Table.Summary.Cell>
+                                </Table.Summary.Row>
+                            </Table.Summary>
+                        )}
+                        rowSelection={{
+                            type: 'checkbox',
+                            selectedRowKeys: editSelectedRow?.map(item => item.id),
+                            getCheckboxProps: (record: any) => ({
+                                name: record.name,
+                            }),
+                            onSelect: (record, selected) => {
+                                if (selected) {
+                                    setEditSelectedRow([...editSelectedRow, record])
+                                } else {
+                                    setEditSelectedRow(editSelectedRow?.filter(item => item.id !== record.id))
+                                }
+                            },
+                            onSelectAll: (selected, rows, changeRows) => {
+                                if (selected) {
+                                    setEditSelectedRow([...editSelectedRow, ...changeRows])
+                                } else {
+                                    let newArr = editSelectedRow?.filter(item => changeRows.every(i => i?.id !== item?.id))
+                                    setEditSelectedRow(newArr)
+                                }
+                            }
+                        }}
+                    />
+                </div>
+                <div className={style.corpUserList_footer}>
+                    <Pagination
+                        size="small"
+                        total={getGroupChatList?.data?.data?.total || 0}
+                        showSizeChanger
+                        showQuickJumper
+                        pageSize={getGroupChatList?.data?.data?.size || 20}
+                        current={getGroupChatList?.data?.data?.current || 1}
+                        onChange={(page: number, pageSize: number) => {
+                            ref.current?.scrollTo({ top: 0 })
+                            setListData({ ...listData, pageNum: page, pageSize })
+                        }}
+                        showTotal={(total: number) => <span style={{ fontSize: 12 }}>共 {total} 条</span>}
+                    />
+                </div>
+            </Card>
+        </div>
+
+        {/* 设置备注弹窗 */}
+        {remarkVisible && <RemarkSet
+            corpId={listData.corpId}
+            listData={listData}
+            editSelectedRow={editSelectedRow}
+            visible={remarkVisible}
+            onClose={() => {
+                setRemarkVisible(false)
+            }}
+            onChange={() => {
+                setEditSelectedRow([])
+                setRemarkVisible(false)
+                getGroupChatList.refresh()
+            }}
+        />}
+
+        {/* 客户列表弹窗 */}
+        <Drawer
+            title={userData?.chatName + '--成员列表'}
+            placement="right"
+            width='80%'
+            onClose={() => { setUserData(null) }}
+            open={userData}
+        >
+            <UserContent userData={userData} />
+        </Drawer>
+
+        {/* 指派公众号 */}
+        {zpVisible && <ZpMp
+            corpId={listData.corpId}
+            visible={zpVisible}
+            onClose={() => setZpVisible(false)}
+            editSelectedRow={editSelectedRow}
+            onChange={() => {
+                setEditSelectedRow([])
+                setZpVisible(false)
+                getGroupChatList.refresh()
+            }}
+        />}
+
+        {/* 变更记录 */}
+        {chatLogData?.visible && <ChatLog
+            {...chatLogData}
+            corpId={listData.corpId}
+            onClose={() => setChatLogData(undefined)}
+        />}
+    </div>;
+};
+
+export default WeAssociationList;

+ 53 - 0
src/pages/weComTask/page/weAssociation/list/remarkSet.tsx

@@ -0,0 +1,53 @@
+import { useAjax } from "@/Hook/useAjax"
+import { ApiParamsChatListProps, setRemarkApi } from "@/pages/weComTask/API/global"
+import { App, Input, Modal } from "antd"
+import React, { useEffect, useState } from "react"
+
+interface Props {
+    listData: ApiParamsChatListProps,
+    editSelectedRow: any[],
+    corpId: string,
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+/**
+ * 设置群备注
+ * @returns 
+ */
+const RemarkSet: React.FC<Props> = ({ visible, onClose, onChange, listData, editSelectedRow, corpId }) => {
+
+    /*******************************/
+    const [remark, setRemark] = useState<string>()
+    const { message } = App.useApp()
+
+    const setRemarkA = useAjax((params: ApiParamsChatListProps) => setRemarkApi(params))
+    /*******************************/
+
+    useEffect(() => {
+        if (editSelectedRow.length === 1) {
+            setRemark(editSelectedRow[0]?.remark)
+        }
+    }, [editSelectedRow])
+
+    const handleOk = () => {
+        let { remark: a, ...params } = listData
+        setRemarkA.run({ ...params, remark, chatIdList: editSelectedRow.map(item => item.chatId), corpId }).then(res => {
+            if (res?.data) {
+                message.success('设置成功')
+                onChange?.()
+            }
+        })
+    }
+
+    return <Modal
+        title="设置备注"
+        open={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+    >
+        <Input placeholder="请输入备注" value={remark} onChange={(e) => setRemark(e.target.value)} />
+    </Modal>
+}
+
+export default React.memo(RemarkSet)

+ 194 - 0
src/pages/weComTask/page/weAssociation/list/tableConfig.tsx

@@ -0,0 +1,194 @@
+import { Space, Tag, Tooltip, Typography } from "antd";
+import { ColumnsType } from "antd/es/table";
+const { Text } = Typography;
+
+export function TableConfig(lockUsers: (v: any) => void, chatLog: (v: any) => void): ColumnsType<any> {
+
+    const arr: ColumnsType<any> = [
+        {
+            title: '客户群',
+            dataIndex: 'chatName',
+            key: 'chatName',
+            width: 300,
+            fixed: 'left',
+            render(value) {
+                return value ? <Text copyable ellipsis>{value}</Text> : '--'
+            },
+        },
+        {
+            title: '群主',
+            dataIndex: 'ownerName',
+            key: 'ownerName',
+            align: 'center',
+            width: 80,
+            fixed: 'left',
+        },
+        {
+            title: '备注',
+            dataIndex: 'remark',
+            key: 'remark',
+            width: 140,
+            ellipsis: true,
+            render(value) {
+                return value || '--'
+            },
+        },
+        {
+            title: '群人数',
+            dataIndex: 'userCount',
+            key: 'userCount',
+            align: 'center',
+            width: 65,
+        },
+        {
+            title: '企业名称',
+            dataIndex: 'corpName',
+            key: 'corpName',
+            align: 'center',
+            width: 180,
+            ellipsis: true
+        },
+        {
+            title: '绑定公众号',
+            dataIndex: 'mpAccount',
+            key: 'mpAccount',
+            align: 'center',
+            width: 100,
+            ellipsis: true,
+            render(value) {
+                return value?.name || '--'
+            },
+        },
+        {
+            title: '群公告',
+            dataIndex: 'notice',
+            key: 'notice',
+            width: 400,
+            ellipsis: true,
+            render: (a: any) => {
+                return a || '--'
+            }
+        },
+        {
+            title: '群标签',
+            dataIndex: 'chatTagNames',
+            key: 'chatTagNames',
+            width: 300,
+            ellipsis: {
+                showTitle: false
+            },
+            render: (a: string) => {
+                return a ? <Tooltip placement="topLeft" title={a}>
+                    {a.split(',').sort((a, b) => {
+                        if (a.startsWith('QC_') && !b.startsWith('QC_')) {
+                            return -1;
+                        } else if (!a.startsWith('QC_') && b.startsWith('QC_')) {
+                            return 1;
+                        } else {
+                            return 0;
+                        }
+                    }).map((item: string, index) => <span key={index}><span style={item.includes('QC') ? { color: 'red' } : {}}>{item}</span>,</span>)}
+                </Tooltip> : '--'
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 150
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 100,
+            fixed: 'right',
+            render: (a, b) => {
+                return <Space>
+                    <a onClick={() => lockUsers(b)}>客户</a>
+                    <a onClick={() => chatLog(b)}>变更记录</a>
+                </Space>
+            }
+        }
+    ]
+    return arr
+}
+
+export function TableConfigLog(): ColumnsType<any> {
+
+    const arr: ColumnsType<any> = [
+        {
+            title: '群名',
+            dataIndex: 'chatName',
+            key: 'chatName',
+            ellipsis: true,
+            width: 150
+        },
+        {
+            title: '群主',
+            dataIndex: 'ownerName',
+            key: 'ownerName',
+            ellipsis: true,
+            align: 'center',
+            width: 80
+        },
+        {
+            title: '执行状态',
+            dataIndex: 'executionStatus',
+            key: 'executionStatus',
+            width: 90,
+            align: 'center',
+            render(value) {
+                return value === 1 ? '执行中' : value === 100 ? '执行成功' : value === -1 ? '执行失败' : '--'
+            },
+        },
+        {
+            title: '任务创建时间',
+            dataIndex: 'taskTime',
+            key: 'taskTime',
+            ellipsis: true,
+            align: 'center',
+            width: 130
+        },
+        {
+            title: '创建者名称',
+            dataIndex: 'createName',
+            key: 'createName',
+            ellipsis: true,
+            align: 'center',
+            width: 80
+        },
+        {
+            title: '任务结果',
+            dataIndex: 'disbandStatus',
+            key: 'disbandStatus',
+            width: 90,
+            align: 'center',
+            render(value) {
+                return value === 1 ? <Tag bordered={false} color="success">成功</Tag> : value === 0 ? <Tag bordered={false} color="error">失败</Tag> : '--'
+            },
+        },
+        {
+            title: '完成时间',
+            dataIndex: 'finishTime',
+            key: 'finishTime',
+            ellipsis: true,
+            align: 'center',
+            width: 130
+        },
+        {
+            title: '失败信息',
+            dataIndex: 'errorMsg',
+            key: 'errorMsg',
+            ellipsis: true,
+            width: 250,
+            render(value) {
+                return value || '--'
+            },
+        },
+    ]
+
+    return arr
+}

+ 145 - 0
src/pages/weComTask/page/weAssociation/list/userContent.tsx

@@ -0,0 +1,145 @@
+import { useAjax } from "@/Hook/useAjax";
+import { Card, DatePicker, Table, Tag, Input, Button, Select } from "antd"
+import { useCallback, useEffect, useState } from "react";
+import { SearchOutlined } from '@ant-design/icons';
+import SearchBox from "@/pages/weComTask/components/searchBox";
+import { UserTableConfig } from "./userTableConfig";
+import React from "react";
+import { getGroupChatUserListApi } from "@/pages/weComTask/API/global";
+const { RangePicker } = DatePicker;
+
+const UserContent: React.FC<{ userData: any }> = ({ userData }) => {
+
+    const [listData, setListData] = useState<any>({
+        pageNum: 1,
+        pageSize: 20,
+    })
+    const getGroupChatUserList = useAjax((params: any) => getGroupChatUserListApi(params))
+    //搜索事件
+    const getList = useCallback((props?: any) => {
+        getGroupChatUserList.run({ ...listData, ...props, corpId: userData?.corpId, chatId: userData?.chatId })
+    }, [listData, userData])
+
+    useEffect(() => {
+        if (userData?.corpId && userData?.chatId) {
+            getList()
+        }
+        return () => {
+            setListData({
+                pageNum: 1,
+                pageSize: 20,
+            })
+        }
+    }, [userData])
+
+    return <>
+        {/* 搜索 */}
+        <SearchBox
+            buttons={<>
+                <Button type="primary" onClick={() => getList()}><SearchOutlined />搜索</Button>
+            </>}
+        >
+            <>
+                <Input placeholder="成员ID"
+                    allowClear
+                    value={listData?.userId}
+                    onChange={(e) => {
+                        let v = e.target.value
+                        setListData({ ...listData, userId: v })
+                    }}
+                />
+
+                <Input placeholder="昵称/群昵称"
+                    allowClear
+                    value={listData?.userName}
+                    onChange={(e) => {
+                        let v = e.target.value
+                        setListData({ ...listData, userName: v })
+                    }}
+                />
+
+                <Select
+                    showSearch
+                    style={{ minWidth: 200 }}
+                    value={listData.userType}
+                    allowClear
+                    onChange={(valuse) => {
+                        setListData({ ...listData, userType: valuse })
+                    }}
+                    placeholder="成员类型"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
+                    }
+                    options={[
+                        {
+                            "value": "1",
+                            "label": "企业成员"
+                        },
+                        {
+                            "value": "2",
+                            "label": "外部联系人"
+                        }
+                    ]}
+                />
+
+                <Select
+                    showSearch
+                    style={{ minWidth: 200 }}
+                    value={listData.joinScene}
+                    allowClear
+                    onChange={(valuse) => {
+                        setListData({ ...listData, joinScene: valuse })
+                    }}
+                    placeholder="入群方式"
+                    filterOption={(input, option) =>
+                        ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
+                    }
+                    options={[
+                        {
+                            "value": "1",
+                            "label": "直接邀请入群"
+                        },
+                        {
+                            "value": "2",
+                            "label": "通过邀请链接入群"
+                        },
+                        {
+                            "value": "3",
+                            "label": "扫描群二维码入群"
+                        }
+                    ]}
+                />
+
+                <RangePicker placeholder={['入群开始日期', '入群结束日期']} allowClear onChange={(dates, dateStrings) => {
+                    setListData({ ...listData, joinTimeStart: dateStrings[0], joinTimeEnd: dateStrings[1] })
+                }} />
+            </>
+        </SearchBox>
+        {/* 表 */}
+        <Table
+            style={{ marginBottom: 1 }}
+            dataSource={getGroupChatUserList?.data?.data?.records}
+            loading={getGroupChatUserList?.loading}
+            columns={UserTableConfig()}
+            scroll={{ x: 400, y: 500 }}
+            rowKey={(s) => {
+                return s.id
+            }}
+            size='small'
+            pagination={{
+                total: getGroupChatUserList?.data?.data?.total,
+                showTotal: (total) => <Tag color="cyan">总共{total}数据</Tag>,
+                showSizeChanger: true,
+                showLessItems: true,
+                defaultCurrent: 1,
+                defaultPageSize: 20,//默认初始的每页条数
+                current: getGroupChatUserList?.data?.data?.current || 1,
+                pageSize: getGroupChatUserList?.data?.data?.size || 20,
+                onChange: (page, pageSize) => {
+                    getList({ ...listData, pageNum: page, pageSize })
+                }
+            }}
+        />
+    </>
+}
+export default React.memo(UserContent)

+ 75 - 0
src/pages/weComTask/page/weAssociation/list/userTableConfig.tsx

@@ -0,0 +1,75 @@
+import { tableDfixed } from "@/utils/tableDfixed";
+import { copy } from "@/utils/utils";
+import { Badge } from "antd";
+import { ColumnsType } from "antd/es/table";
+export function UserTableConfig(): ColumnsType<any> {
+
+    const arr: ColumnsType<any> = [
+        {
+            title: '成员id',
+            dataIndex: 'userId',
+            key: 'userId',
+            width: 200,
+            ellipsis: true,
+            render: (a) => {
+                return <a onClick={() => { copy(a) }}>{a}</a>
+            }
+        },
+        {
+            title: '成员名称',
+            dataIndex: 'userName',
+            key: 'userName',
+            align: 'center',
+            width: 80,
+        },
+        {
+            title: '群内昵称',
+            dataIndex: 'groupNickName',
+            key: 'groupNickName',
+            align: 'center',
+            width: 80,
+            render: (a, b) => {
+                return a || b?.userName
+            }
+        },
+        {
+            title: '是否管理员',
+            dataIndex: 'isManager',
+            key: 'isManager',
+            align: 'center',
+            width: 80,
+            render: (a) => {
+                return <Badge text={a ? '是' : '否'} status={a ? 'success' : 'default'} />
+            }
+        },
+        {
+            title: '成员类型',
+            dataIndex: 'userType',
+            key: 'userType',
+            align: 'center',
+            width: 80,
+        },
+        {
+            title: '入群方式',
+            dataIndex: 'joinScene',
+            key: 'joinScene',
+            align: 'center',
+            width: 80,
+        },
+        {
+            title: '入群时间',
+            dataIndex: 'joinTime',
+            key: 'joinTime',
+            align: 'center',
+            width: 120,
+        },
+        {
+            title: '邀请人名称',
+            dataIndex: 'invitorUserName',
+            key: 'invitorUserName',
+            align: 'center',
+            width: 80,
+        },
+    ]
+    return arr
+}

+ 75 - 0
src/pages/weComTask/page/weAssociation/list/zpMp.tsx

@@ -0,0 +1,75 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getBindMpListApi } from "@/pages/weComTask/API/corpUserAssign"
+import { setQLMpApi } from "@/pages/weComTask/API/global"
+import { App, Form, Modal, Select } from "antd"
+import React, { useEffect } from "react"
+
+interface Props {
+    editSelectedRow: any[],
+    corpId: string,
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+/**
+ * 指派公众号
+ * @returns 
+ */
+const ZpMp: React.FC<Props> = ({ visible, onClose, onChange, editSelectedRow, corpId }) => {
+
+    /*******************************/
+    const [form] = Form.useForm()
+    const { message } = App.useApp()
+
+    const setQLMp = useAjax((params) => setQLMpApi(params))
+    const getBindMpList = useAjax(() => getBindMpListApi())
+    /*******************************/
+
+    useEffect(() => {
+        getBindMpList.run()
+    }, [])
+
+    const handleOk = async () => {
+        const values = await form.validateFields()
+        setQLMp.run({ chatIds: editSelectedRow.map(item => item.chatId), corpId, ...values }).then(res => {
+            if (res?.data) {
+                message.success('指派成功')
+                onChange?.()
+            }
+        })
+    }
+
+    return <Modal
+        title="指派公众号"
+        open={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        maskClosable={false}
+        confirmLoading={setQLMp.loading}
+    >
+        <Form
+            name="basic_oper_zp_mp"
+            form={form}
+            labelCol={{ span: 4 }}
+            wrapperCol={{ span: 20 }}
+            autoComplete="off"
+            labelAlign="left"
+            colon={false}
+        >
+            <Form.Item label="公众号" name="mpAccountId" rules={[{ required: true, message: '请选择公众号!' }]}>
+                <Select
+                    showSearch
+                    allowClear
+                    placeholder="选择公众号"
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    {getBindMpList?.data?.data?.map((item: any) => <Select.Option value={item.id} key={item.id}>{item.name}</Select.Option>)}
+                </Select>
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(ZpMp)