瀏覽代碼

选择标签

wjx 1 周之前
父節點
當前提交
e6fc16c4d1
共有 19 個文件被更改,包括 999 次插入237 次删除
  1. 1 0
      src/pages/weComTask/API/corpUserManage/typings.d.ts
  2. 27 8
      src/pages/weComTask/components/materialMould/index.tsx
  3. 1 0
      src/pages/weComTask/components/previewMsg/index.less
  4. 10 7
      src/pages/weComTask/components/previewMsg/lookPyq.tsx
  5. 3 0
      src/pages/weComTask/components/selectCwTag/index.less
  6. 244 0
      src/pages/weComTask/components/selectCwTag/index.tsx
  7. 78 109
      src/pages/weComTask/page/businessPlan/create/components/friends/content.tsx
  8. 277 23
      src/pages/weComTask/page/businessPlan/create/components/friends/index.tsx
  9. 162 0
      src/pages/weComTask/page/businessPlan/create/components/friends/materialFriendsNoTextMould.tsx
  10. 97 0
      src/pages/weComTask/page/businessPlan/create/components/friends/previewFriendsStrategy.tsx
  11. 9 9
      src/pages/weComTask/page/businessPlan/create/components/friends/settingsFriends.tsx
  12. 59 0
      src/pages/weComTask/page/businessPlan/create/components/friends/showFriendsContent.tsx
  13. 12 70
      src/pages/weComTask/page/businessPlan/create/components/friends/strategy.tsx
  14. 1 1
      src/pages/weComTask/page/businessPlan/create/components/highMassSending/content.tsx
  15. 1 1
      src/pages/weComTask/page/businessPlan/create/components/massSending/content.tsx
  16. 4 4
      src/pages/weComTask/page/businessPlan/create/index.tsx
  17. 6 0
      src/pages/weComTask/page/businessPlan/create/typings.d.ts
  18. 6 5
      src/pages/weComTask/page/corpUserManage/index.tsx
  19. 1 0
      src/pages/weComTask/page/corpUserManage/selectCorpUser.tsx

+ 1 - 0
src/pages/weComTask/API/corpUserManage/typings.d.ts

@@ -12,6 +12,7 @@ declare namespace CORP_USER_ASSIGN_API {
         status?: number,
         stopUse?: boolean,
         sysUserId?: number
+        corpUserName?: string
     }
 
     interface PutCorpUserTOProps {

+ 27 - 8
src/pages/weComTask/components/materialMould/index.tsx

@@ -49,14 +49,12 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
         }
     }, [value])
 
-    useUpdateEffect(() => {
-        onChange?.(data)
-    }, [data])
     useEffect(() => {
         if (dataNum) {
             setDataLength(dataNum)
         }
     }, [dataNum])
+
     return <div>
         <List
             header={null}
@@ -75,6 +73,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                         mediaHeight: imgData.height.toString()
                     }
                     setData(NewData)
+                    onChange?.(NewData)
                 }} />}
                 {!(noShowType?.some(i => i === 'video')) && <UploadButton text="视频" mediaSize={mediaSize} disabled={data.length === dataLength} type='video' tooltip="发送视频传送比较耗时,如果发送消息间隔设置又较短,可能会出现消息乱序,所以视频尽量压缩小一点" onChange={async (url, file) => {
                     let NewData: MediaContentProps[] = JSON.parse(JSON.stringify(data))
@@ -89,6 +88,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                         mediaPlayTime: videoInfo[0]?.videoLength?.toString()
                     }
                     setData(NewData)
+                    onChange?.(NewData)
                 }} />}
                 {!(noShowType?.some(i => i === 'file')) && <UploadButton mediaSize={mediaSize} text="文件" disabled={data.length === dataLength} type='file' onChange={(url, file, name) => {
                     let NewData: MediaContentProps[] = JSON.parse(JSON.stringify(data))
@@ -100,6 +100,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                         mediaFormat: file?.name?.split('.')[1],
                     }
                     setData(NewData)
+                    onChange?.(NewData)
                 }} />}
                 {!(noShowType?.some(i => i === 'link')) && <Button disabled={data.length === dataLength} onClick={() => { setOpenLink(true); setSubscript(data.length); setIsInsert(false) }}>链接</Button>}
                 {/* {!(noShowType?.some(i => i === 'voice')) && <UploadButton mediaSize={mediaSize} text="语音" disabled={data.length === dataLength} type='voice' onChange={async (url, file) => {
@@ -113,6 +114,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                         mediaFormat: file?.name?.split('.')[1],
                     }
                     setData(NewData)
+                    onChange?.(NewData)
                 }} />} */}
                 {!(noShowType?.some(i => i === 'miniprogram')) && <Button disabled={data.length === dataLength} onClick={() => { setOpenMiniprogram(true); setSubscript(data.length); setIsInsert(false) }}>小程序</Button>}
                 {!(noShowType?.some(i => i === 'sck')) && <Button disabled={data.length === dataLength} onClick={() => { setOpenMateria(true); setSubscript(data.length); setIsInsert(false) }}>素材库</Button>}
@@ -159,6 +161,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                                             }
                                             NewData.splice(index + 1, 0, cont)
                                             setData(NewData)
+                                            onChange?.(NewData)
                                         } else if (mediaType === 'text') {
                                             setSubscript(index)
                                             setOpenText(true);
@@ -204,6 +207,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                                                 NewData[index].id = id
                                             }
                                             setData(NewData)
+                                            onChange?.(NewData)
                                         } else if (mediaType === 'text') {
                                             setSubscript(index)
                                             setOpenText(true);
@@ -216,19 +220,29 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                                         }
                                         break
                                     case 'del':
-                                        setData(data.filter((item, index1) => index !== index1))
+                                        const newDelData = data.filter((item, index1) => index !== index1)
+                                        setData(newDelData)
+                                        onChange?.(newDelData)
                                         break
                                     case 'down':
-                                        setData(arrayMove(data, index, index + 1))
+                                        const newDownData = arrayMove(data, index, index + 1)
+                                        setData(newDownData)
+                                        onChange?.(newDownData)
                                         break
                                     case 'downV':
-                                        setData(arrayMove(data, index, data.length))
+                                        const newDownVData = arrayMove(data, index, data.length)
+                                        setData(newDownVData)
+                                        onChange?.(newDownVData)
                                         break
                                     case 'up':
-                                        setData(arrayMove(data, index, index - 1))
+                                        const newUpData = arrayMove(data, index, index - 1)
+                                        setData(newUpData)
+                                        onChange?.(newUpData)
                                         break;
                                     case 'upV':
-                                        setData(arrayMove(data, index, 0))
+                                        const newUpVData = arrayMove(data, index, 0)
+                                        setData(newUpVData)
+                                        onChange?.(newUpVData)
                                         break
                                 }
                             }}
@@ -261,6 +275,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                     }
                 }
                 setData(NewData)
+                onChange?.(NewData)
                 setOpenText(false)
             }}
             onClose={() => setOpenText(false)}
@@ -291,6 +306,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                     }
                 }
                 setData(NewData)
+                onChange?.(NewData)
                 setOpenLink(false)
             }}
         />}
@@ -318,6 +334,7 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                     }
                 }
                 setData(NewData)
+                onChange?.(NewData)
                 setOpenMiniprogram(false)
             }}
         />}
@@ -338,8 +355,10 @@ const MaterialMould: React.FC<Props> = ({ value, onChange, boxHeight, dataNum, n
                         value.unshift((subscript + 1) as any, 0 as any)
                         Array.prototype.splice.apply(NewData, value as any);
                         setData(NewData)
+                        onChange?.(NewData)
                     } else {
                         setData([...NewData, ...value])
+                        onChange?.([...NewData, ...value])
                     }
                 }
             }}

+ 1 - 0
src/pages/weComTask/components/previewMsg/index.less

@@ -53,6 +53,7 @@
         flex-shrink: 0;
         display: flex;
         flex-direction: column;
+        overflow: hidden;
 
         .name {
             display: flex;

+ 10 - 7
src/pages/weComTask/components/previewMsg/lookPyq.tsx

@@ -1,6 +1,6 @@
 import useNewToken from "@/Hook/useNewToken"
 import { Avatar, Typography } from "antd"
-import React, { useEffect, useState } from "react"
+import React, { useEffect, useRef, useState } from "react"
 import { MediaContentProps } from "../../API/weMaterial/weMaterial"
 import AudioNews from "../newsMould/audioNews"
 import FileNews from "../newsMould/fileNews"
@@ -11,6 +11,7 @@ import VideoNews from "../newsMould/videoNews"
 import style from './index.less'
 import { UserOutlined } from '@ant-design/icons';
 import { emoList } from "../textEditor/Expression"
+import { useSize } from "ahooks"
 
 interface Props {
     content: {
@@ -28,6 +29,8 @@ const LookPyq: React.FC<Props> = ({ content, title, subTitle, avatar }) => {
     const { token } = useNewToken()
     const [text, setText] = useState('')
     const [length, setLength] = useState<number>(0)
+    const ref = useRef<HTMLDivElement>(null)
+    const size = useSize(ref)
     /********************************/
 
     useEffect(() => {
@@ -56,7 +59,7 @@ const LookPyq: React.FC<Props> = ({ content, title, subTitle, avatar }) => {
     return <div className={style.lookPyq}>
         <div className={style.lookPyq_item} style={{ padding: token.padding, borderBottom: `1px solid ${token.colorBorderSecondary}` }}>
             <Avatar shape="square" src={avatar} icon={<UserOutlined />} />
-            <div className={style.left}>
+            <div className={style.left} ref={ref}>
                 <div className={style.name}>
                     <Typography.Title level={5} style={{ margin: 0, lineHeight: 'normal' }}>{title}</Typography.Title>
                     <Typography.Text style={{ margin: 0, lineHeight: 'normal' }} type="warning">{subTitle}</Typography.Text>
@@ -65,20 +68,20 @@ const LookPyq: React.FC<Props> = ({ content, title, subTitle, avatar }) => {
                 <div className={style.scBox}>
                     {content?.data?.map((item, index) => {
                         return <div key={index} className={style.scBox_item} style={{ width: length === 1 ? '100%' : length === 2 ? '50%' : length === 3 ? '33.333%' : length === 4 ? '50%' : '33.333%', textAlign: 'left' }}>
-                            {item?.mediaType === 'image' ? <ImgNews
+                            {item?.mediaType === 'image' && size?.width ? <ImgNews
                                 src={item?.imageUrl}
                                 preview
                                 imgStyle={{
                                     objectFit: 'cover',
-                                    width: length === 1 ? 250 : length === 2 ? 240 / 2 : length === 3 ? 240 / 3 : length === 4 ? 240 / 2 : 240 / 3,
-                                    height: length === 1 ? 250 : length === 2 ? 240 / 2 : length === 3 ? 240 / 3 : length === 4 ? 240 / 2 : 240 / 3,
+                                    width: length === 1 ? 250 : length === 2 ? size?.width / 2 : length === 3 ? size?.width / 3 : length === 4 ? size?.width / 2 : size?.width / 3,
+                                    height: length === 1 ? 250 : length === 2 ? size?.width / 2 : length === 3 ? size?.width / 3 : length === 4 ? size?.width / 2 : size?.width / 3,
                                 }}
                             />
                                 : item?.mediaType === 'video' ? <VideoNews src={item?.videoUrl} preview />
                                     : item?.mediaType === 'voice' ? <AudioNews url={item?.voiceUrl} times={item.mediaPlayTime} />
                                         : item?.mediaType === 'miniprogram' ? <MiniprogramNews {...item} isDetails/>
-                                            : item?.mediaType === 'link' ? <LinkNews width={240} isBlank content={{ url: item.linkUrl as string, title: item.linkTitle as string, desc: item.linkDesc as string, cover: item.linkPicurl as string }} />
-                                                : item?.mediaType === 'file' ? <FileNews width={225} fileName={item?.fileName} fileSize={item.mediaSize} fileUrl={item.fileUrl} />
+                                            : item?.mediaType === 'link' && size?.width ? <LinkNews width={size?.width - 5} isBlank content={{ url: item.linkUrl as string, title: item.linkTitle as string, desc: item.linkDesc as string, cover: item.linkPicurl as string }} />
+                                                : item?.mediaType === 'file' && size?.width ? <FileNews width={size?.width - 20} fileName={item?.fileName} fileSize={item.mediaSize} fileUrl={item.fileUrl} />
                                                     : null}
                         </div>
                     })}

+ 3 - 0
src/pages/weComTask/components/selectCwTag/index.less

@@ -0,0 +1,3 @@
+.site-tree-search-value {
+    color: #f50;
+}

+ 244 - 0
src/pages/weComTask/components/selectCwTag/index.tsx

@@ -0,0 +1,244 @@
+import { useAjax } from "@/Hook/useAjax";
+import { Alert, Input, Modal, Space, Typography, Spin, Button, App, TreeDataNode, Tree, TreeProps, Switch, Checkbox, Tag } from "antd";
+import React, { useEffect, useMemo, useState } from "react"
+import style from './index.less'
+import { addTaskApi } from "../../API/businessPlan/create";
+
+interface Props {
+    editSelectedRow: any[],
+    corpId: string,
+    type: 'ADD' | 'DEL',
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+
+/**
+ * 添加本地标签给客户
+ * @param param0 
+ * @returns 
+ */
+const SelectCwTag: React.FC<Props> = (props) => {
+
+    /**********************************/
+    const [sysTagsList, setSysTagsList] = useState<TreeDataNode[]>([])
+
+    // const getChatGroupList = useAjax((params) => getChatGroupListApi(params))
+    const getChatGroupList = useAjax((params) => addTaskApi(params))
+    /**********************************/
+
+    useEffect(() => {
+        getChatGroupList.run({ corpId: props.corpId }).then(res => {
+            setSysTagsList(res?.data?.map((item: { corpExternalTagList: { tagName: any; tagId: any }[]; tagGroupName: any; tagGroupId: any }) => {
+                let children = item?.corpExternalTagList?.map((c: { tagName: any; tagId: any }) => ({ title: c?.tagName, key: c?.tagId, value: c?.tagId }))
+                return { title: item?.tagGroupName, value: 'group' + '_' + item?.tagGroupId, key: 'group' + '_' + item?.tagGroupId, children }
+            }) || [])
+        })
+    }, [props.corpId])
+
+    return getChatGroupList.loading ? <div style={{
+        position: 'fixed',
+        top: 0,
+        left: 0,
+        width: '100%',
+        height: '100vh',
+        background: 'rgba(0,0,0,0.5)',
+        zIndex: 1000,
+        textAlign: 'center',
+        lineHeight: '100vh',
+        color: '#FFF'
+    }}>
+        <Spin size="large" /> <span style={{ marginLeft: 20, fontSize: 20 }}>加载中。。。</span>
+    </div> : <SelectCwTagEle {...props} tagsList={sysTagsList} />
+}
+
+const getParentKey = (key: React.Key, tree: TreeDataNode[]): React.Key => {
+    let parentKey: React.Key;
+    for (let i = 0; i < tree.length; i++) {
+        const node = tree[i];
+        if (node.children) {
+            if (node.children.some((item) => item.key === key)) {
+                parentKey = node.key;
+            } else if (getParentKey(key, node.children)) {
+                parentKey = getParentKey(key, node.children);
+            }
+        }
+    }
+    return parentKey!;
+};
+
+interface EleProps extends Props {
+    tagsList: TreeDataNode[]
+}
+const SelectCwTagEle: React.FC<EleProps> = ({ tagsList, corpId, type, editSelectedRow, visible, onClose, onChange }) => {
+
+    /**********************************/
+    const [checkedList, setCheckedList] = useState<any[]>([]);
+
+    const { message } = App.useApp()
+    const [searchValue, setSearchValue] = useState<string>('');
+    const [groupAll, setGroupAll] = useState<boolean>(true)
+    const [dataList, setDataList] = useState<{ key: React.Key; title: string }[]>([]);
+    const [autoExpandParent, setAutoExpandParent] = useState<boolean>(true);
+    const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
+    /**********************************/
+
+    useEffect(() => {
+        const dataList: { key: React.Key; title: string }[] = [];
+        const generateList = (data: TreeDataNode[]) => {
+            for (let i = 0; i < data.length; i++) {
+                const node = data[i];
+                const { key, title } = node;
+                dataList.push({ key, title: title as string });
+                if (node.children) {
+                    generateList(node.children);
+                }
+            }
+        };
+        generateList(tagsList)
+        setDataList(dataList)
+    }, [tagsList])
+
+
+    const handleOk = () => {
+        if (checkedList?.length > 0) {
+            let params = {
+                corpId,
+                tagIdList: checkedList.map(item => item.key),
+                chatIdList: editSelectedRow.map(item => item.chatId)
+            }
+            // if (type === 'ADD') {
+            //     addChatLocalTags.run(params).then(res => {
+            //         if (res.data) {
+            //             message.success('操作成功')
+            //             onChange?.()
+            //         }
+            //     })
+            // } else if (type === 'DEL') {
+            //     delChatLocalTags.run(params).then(res => {
+            //         if (res.data) {
+            //             message.success('操作成功')
+            //             onChange?.()
+            //         }
+            //     })
+            // }
+        } else {
+            message.error('请选择本地标签')
+        }
+    }
+
+    const onExpand = (newExpandedKeys: React.Key[]) => {
+        setExpandedKeys(newExpandedKeys);
+        setAutoExpandParent(false);
+    };
+
+    const onSearch = (value?: string) => {
+        const newExpandedKeys = dataList.map((item) => {
+            if (value && item.title.indexOf(value) > -1) {
+                return getParentKey(item.key, tagsList);
+            }
+            return null;
+        }).filter((item, i, self): item is React.Key => !!(item && self.indexOf(item) === i));
+        setExpandedKeys(newExpandedKeys);
+        setSearchValue(value || '')
+        setAutoExpandParent(true)
+    }
+
+    const onCheck: TreeProps['onCheck'] = (checkedKeys, info) => {
+        setCheckedList(info.checkedNodes?.filter(item => !item?.children))
+    };
+
+
+
+    const treeData = useMemo(() => {
+        if (!searchValue) return tagsList;
+        const loop = (data: TreeDataNode[]): TreeDataNode[] => data.reduce((pre, cur) => {
+            const strTitle = cur.title as string;
+            const index = strTitle.indexOf(searchValue);
+            if (index > -1) {
+                const beforeStr = strTitle.substring(0, index);
+                const afterStr = strTitle.slice(index + searchValue.length);
+                const title = <span>
+                    {beforeStr}
+                    <span className={style['site-tree-search-value']}>{searchValue}</span>
+                    {afterStr}
+                </span>;
+                if (!groupAll) {
+                    pre.push({ ...cur, title })
+                } else {
+                    if (cur.children) {
+                        let children = loop(cur.children)
+                        pre.push({ ...cur, title, children })
+                    } else {
+                        pre.push({ ...cur, title })
+                    }
+                }
+            } else {
+                if (cur.children) {
+                    let children = loop(cur.children)
+                    if (children?.length > 0) {
+                        pre.push({ ...cur, children })
+                    }
+                }
+            }
+            return pre
+        }, [] as TreeDataNode[])
+
+        return loop(tagsList);
+    }, [searchValue, groupAll]);
+
+    return <Modal
+        open={visible}
+        title={type === 'ADD' ? '添加群新标签' : '删除群标签'}
+        onCancel={onClose}
+        onOk={handleOk}
+        width={750}
+        destroyOnClose
+        styles={{ body: { paddingBottom: 0 } }}
+    >
+        <Space direction="vertical" style={{ width: '100%' }}>
+            <Alert message={<span>已选 <span style={{ fontSize: 14, fontWeight: 'bold' }}>{editSelectedRow.length}</span> 个群</span>} type="error" />
+            <Space align="center">
+                <Input.Search placeholder="搜索标签" onSearch={onSearch} allowClear /><Checkbox checked={groupAll} onChange={(e) => setGroupAll(e.target.checked)}>组内标签条件不满足过滤</Checkbox>
+            </Space>
+            <div style={{ display: 'flex', borderTop: '1px solid #0e0e0e08' }}>
+                <div style={{ height: 350, overflow: 'hidden', width: 450, backgroundColor: 'rgba(247, 247, 247, 0.6)' }}>
+                    <Tree
+                        checkable
+                        height={350}
+                        onCheck={onCheck}
+                        onExpand={onExpand}
+                        selectable={false}
+                        expandedKeys={expandedKeys}
+                        treeData={treeData}
+                        checkedKeys={checkedList.map(item => item.key)}
+                        autoExpandParent={autoExpandParent}
+                    />
+                </div>
+                <div style={{ width: 'calc(100% - 450px)', height: 350, borderLeft: '1px solid #0e0e0e08', padding: '10px 10px 0', boxSizing: 'border-box' }}>
+                    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                        <Typography.Text type='success' >已选标签:({checkedList?.length || 0})</Typography.Text>
+                        <Button size='small' type='primary' onClick={() => {
+                            setCheckedList([])
+                        }}>清空选择</Button>
+                    </div>
+                    <div style={{ height: 310, overflowY: 'auto', marginTop: 10 }}>
+                        {checkedList?.map(item => <Tag
+                            key={item.key}
+                            closable
+                            style={{ marginBottom: 4 }}
+                            onClose={(e) => {
+                                e.preventDefault();
+                                setCheckedList(checkedList.filter(d => d?.key !== item?.key))
+                            }}
+                        >
+                            <Typography.Text ellipsis={{ tooltip: true }} style={{ maxWidth: 90 }}>{item.title}</Typography.Text>
+                        </Tag>)}
+                    </div>
+                </div>
+            </div>
+        </Space>
+    </Modal>
+}
+
+export default React.memo(SelectCwTag)

+ 78 - 109
src/pages/weComTask/page/businessPlan/create/components/friends/content.tsx

@@ -4,15 +4,15 @@ import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } f
 import '../../global.less';
 import { welcomeContentData } from '../../const';
 import { PlusOutlined, MinusOutlined } from '@ant-design/icons'
-import MaterialNoTextMould from '../massSending/materialNoTextMould';
+import MaterialFriendsNoTextMould from './materialFriendsNoTextMould';
 
 
 /**
- * 群发内容
+ * 朋友圈内容配置
  * @param param0 
  * @returns 
  */
-const Content = forwardRef(({ massSendingStrategy, value, onChange }: TASK_CREATE.ContentProps, ref: React.ForwardedRef<{ handleOk: (type: string) => void }>) => {
+const Content = forwardRef(({ friendsStrategy, value, onChange }: TASK_CREATE.FriendsContentProps, ref: React.ForwardedRef<{ handleOk: (type: string) => void }>) => {
 
     /****************************************/
     const { message } = App.useApp()
@@ -30,7 +30,7 @@ const Content = forwardRef(({ massSendingStrategy, value, onChange }: TASK_CREAT
 
     // 回填
     useEffect(() => {
-        if (!massSendingStrategy?.strategySettings) {
+        if (!friendsStrategy?.strategySettings) {
             message.error('请先设置群发策略')
             return
         }
@@ -40,56 +40,40 @@ const Content = forwardRef(({ massSendingStrategy, value, onChange }: TASK_CREAT
             form.setFieldsValue(value)
         } else {
             const data = {
-                massSendingContentDTO: massSendingStrategy.strategySettings.map(item => {
-                    return {
-                        sendContentDto: item.sendData.map(() => ({ contentDTO: [undefined], sendMode: undefined }))
-                    }
-                })
+                friendsContentDTO: friendsStrategy.strategySettings.map(() => ({ contentDTO: [undefined], sendMode: undefined }))
             }
             filedUpdateChange(data)
             form.setFieldsValue(data)
         }
-    }, [value, massSendingStrategy])
+    }, [value, friendsStrategy])
 
     const handleOk = (type: string) => {
         form.validateFields().then((values) => {
-            console.log(values)
             onChange(values, type)
         }).catch(() => {
             form.submit()
         });
     };
 
-    const filedUpdateChange = ({ massSendingContentDTO }: { massSendingContentDTO: { sendContentDto: any }[] }) => {
-        const stepsData = massSendingContentDTO.map((item, index) => {
+    const filedUpdateChange = ({ friendsContentDTO }: { friendsContentDTO: { contentDTO: any[], sendMode?: number }[] }) => {
+        const stepsData = friendsContentDTO.map((item, index) => {
 
-            const children = item.sendContentDto.map((ci, c_index) => {
-
-                const content = ci?.contentDTO?.map((i, i_index) => {
-                    return { title: `内容${i_index + 1}`, checked: (i?.attachmentList?.length || i?.text?.content), id: 'clientId' + '_' + index + '_' + c_index + 'contentDTO' }
-                })
-
-                const contentChildren = [
-                    { title: '发送模式', checked: ci?.sendMode || ci?.sendMode === 0, id: 'clientId' + '_' + index + '_' + c_index + 'sendMode' },
-                    ...content
-                ]
-
-                return {
-                    title: `发送对象 ${c_index + 1}`,
-                    id: 'clientId' + '_' + index + '_' + c_index,
-                    children: contentChildren,
-                    checked: (ci?.sendMode || ci?.sendMode === 0) && contentChildren?.every(item => item.checked)
-                }
+            const content = item?.contentDTO?.map((i, i_index) => {
+                return { title: `内容${i_index + 1}`, checked: (i?.attachmentList?.length || i?.text?.content), id: 'clientId' + '_' + index + '_' + 'contentDTO' }
             })
 
+            const contentChildren = [
+                { title: '发送模式', checked: item?.sendMode || item?.sendMode === 0, id: 'clientId' + '_' + index + '_' + 'sendMode' },
+                ...content
+            ]
+
             return {
                 title: '策略' + (index + 1),
                 id: 'clientId' + '_' + index,
                 children: [
-                    ...children,
-                    { title: '完成', checked: children?.every(item => item.checked) }
+                    ...contentChildren
                 ],
-                checked: children?.every(item => item.checked)
+                checked: contentChildren?.every(item => item.checked)
             }
         })
 
@@ -112,7 +96,7 @@ const Content = forwardRef(({ massSendingStrategy, value, onChange }: TASK_CREAT
         <div className={`body_content`} ref={ref1}>
             <Form
                 form={form}
-                name="newContent"
+                name="newFriendsContent"
                 labelAlign='left'
                 labelCol={{ span: 5 }}
                 colon={false}
@@ -124,94 +108,79 @@ const Content = forwardRef(({ massSendingStrategy, value, onChange }: TASK_CREAT
                     message.error(errorFields?.[0]?.errors?.[0])
                 }}
                 onFinish={handleOk}
-                initialValues={{ massSendingContentDTO: [undefined] }}
+                initialValues={{ friendsContentDTO: [undefined] }}
                 onFieldsChange={() => {
-                    console.log(form.getFieldsValue())
+                    // console.log('fieldsChange')
                     filedUpdateChange(form.getFieldsValue())
                 }}
                 preserve={true}
             >
-                <Form.List name='massSendingContentDTO'>
+                <Form.List name='friendsContentDTO'>
                     {(fields) => (
                         <>
                             {fields.map(({ key, name, ...restField }, index) => {
-                                return <Card key={index} title={<strong>策略 {index + 1}</strong>} style={{ background: '#fff', marginBottom: 10 }} id={'clientId' + '_' + index}>
-                                    <Form.List {...restField} name={[name, 'sendContentDto']}>
-                                        {(fields) => (
-                                            <>
-                                                {fields.map(({ key, name, ...restField }, i) => {
-                                                    return <Card
-                                                        key={i}
-                                                        title={<strong>发送对象{i + 1} 内容配置</strong>}
-                                                        style={{ background: '#fff', marginBottom: 10 }}
-                                                        id={'clientId' + '_' + index + '_' + i}
-                                                    >
-                                                        <div id={'clientId' + '_' + index + '_' + i + 'sendMode'}>
+                                return <Card key={index} title={<strong>策略名称:{friendsStrategy?.strategySettings?.[index]?.strategyName || '<空>'}</strong>} style={{ background: '#fff', marginBottom: 10 }} id={'clientId' + '_' + index}>
+                                    <div id={'clientId' + '_' + index + '_' + 'sendMode'}>
+                                        <Form.Item
+                                            {...restField}
+                                            label={<strong>内容组发送模式</strong>}
+                                            name={[name, 'sendMode']}
+                                            rules={[{ required: true, message: '请选择内容组发送模式!' }]}
+                                        >
+                                            <Select
+                                                showSearch
+                                                style={{ width: 358 }}
+                                                allowClear
+                                                placeholder="请选择内容组发送模式"
+                                                filterOption={(input, option) =>
+                                                    ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
+                                                }
+                                                options={welcomeContentData}
+                                            />
+                                        </Form.Item>
+                                    </div>
+                                    <div id={'clientId' + '_' + index + '_' + 'contentDTO'}>
+                                        <Form.List {...restField} name={[name, 'contentDTO']} >
+                                            {(fields, { add, remove }) => {
+                                                return <>
+                                                    {fields.map(({ key, name, ...restField }, index) => {
+                                                        return <div key={index}>
+                                                            <h3 style={{ display: 'flex', justifyContent: 'space-between', margin: 0 }}>内容{index + 1}
+                                                                {fields?.length > 1 && <Button
+                                                                    type="dashed"
+                                                                    danger
+                                                                    onClick={() => remove(index)}
+                                                                    icon={<MinusOutlined />}
+                                                                    size='small'
+                                                                >
+                                                                    移除内容
+                                                                </Button>}
+                                                            </h3>
                                                             <Form.Item
                                                                 {...restField}
-                                                                label={<strong>内容组发送模式</strong>}
-                                                                name={[name, 'sendMode']}
-                                                                rules={[{ required: true, message: '请选择内容组发送模式!' }]}
-                                                            >
-                                                                <Select
-                                                                    showSearch
-                                                                    style={{ width: 358 }}
-                                                                    allowClear
-                                                                    placeholder="请选择内容组发送模式"
-                                                                    filterOption={(input, option) =>
-                                                                        ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
+                                                                name={name}
+                                                                rules={[{
+                                                                    validator: (_, value) => {
+                                                                        if ((value && !(value?.text?.content || value?.attachmentList?.length)) || !value) {
+                                                                            return Promise.reject('请填写内容或上传附件');
+                                                                        }
+                                                                        return Promise.resolve();
                                                                     }
-                                                                    options={welcomeContentData}
-                                                                />
+                                                                }]}
+                                                            >
+                                                                <MaterialFriendsNoTextMould />
                                                             </Form.Item>
                                                         </div>
-                                                        <div id={'clientId' + '_' + index + '_' + i + 'contentDTO'}>
-                                                            <Form.List {...restField} name={[name, 'contentDTO']} >
-                                                                {(fields, { add, remove }) => {
-                                                                    return <>
-                                                                        {fields.map(({ key, name, ...restField }, index) => {
-                                                                            return <div key={index}>
-                                                                                <h3 style={{ display: 'flex', justifyContent: 'space-between', margin: 0 }}>内容{index + 1}
-                                                                                    {fields?.length > 1 && <Button
-                                                                                        type="dashed"
-                                                                                        danger
-                                                                                        onClick={() => remove(index)}
-                                                                                        icon={<MinusOutlined />}
-                                                                                        size='small'
-                                                                                    >
-                                                                                        移除内容
-                                                                                    </Button>}
-                                                                                </h3>
-                                                                                <Form.Item
-                                                                                    {...restField}
-                                                                                    name={name}
-                                                                                    rules={[{
-                                                                                        validator: (_, value) => {
-                                                                                            if ((value && !(value?.text?.content || value?.attachmentList?.length)) || !value) {
-                                                                                                return Promise.reject('请填写内容或上传附件');
-                                                                                            }
-                                                                                            return Promise.resolve();
-                                                                                        }
-                                                                                    }]}
-                                                                                >
-                                                                                    <MaterialNoTextMould />
-                                                                                </Form.Item>
-                                                                            </div>
-                                                                        })}
-                                                                        <Form.Item noStyle>
-                                                                            <Button type="dashed" onClick={() => add()} style={{ width: '100%' }} icon={<PlusOutlined />}>
-                                                                                新增内容
-                                                                            </Button>
-                                                                        </Form.Item>
-                                                                    </>
-                                                                }}
-                                                            </Form.List>
-                                                        </div>
-                                                    </Card>
-                                                })}
-                                            </>
-                                        )}
-                                    </Form.List>
+                                                    })}
+                                                    <Form.Item noStyle>
+                                                        <Button type="dashed" onClick={() => add()} style={{ width: '100%' }} icon={<PlusOutlined />}>
+                                                            新增内容
+                                                        </Button>
+                                                    </Form.Item>
+                                                </>
+                                            }}
+                                        </Form.List>
+                                    </div>
                                 </Card>
                             })}
                         </>

+ 277 - 23
src/pages/weComTask/page/businessPlan/create/components/friends/index.tsx

@@ -11,6 +11,9 @@ import FilterUserText from '@/pages/weComTask/components/filterUser/filterUserTe
 import { RcFile } from 'antd/es/upload';
 import { groupBy, readFileAsBuffer } from '@/utils/utils';
 import { saveAs } from 'file-saver';
+import SettingsFriends from './settingsFriends';
+import PreviewFriendsStrategy from './previewFriendsStrategy';
+import ShowFriendsContent from './showFriendsContent';
 
 const Friends: React.FC = () => {
 
@@ -24,6 +27,257 @@ const Friends: React.FC = () => {
     const [downloadLoading, setDownloadLoading] = useState<boolean>(false)
     /***************************************************/
 
+    const exportExcel = async () => {
+        setDownloadLoading(true)
+        const workbook = new ExcelJS.Workbook();  // 创建空工作簿
+        const worksheet = workbook.addWorksheet('Sheet1');  // 添加工作表
+        const headerRowIndex = 1; // 表头行索引
+
+        // 表头冻结
+        worksheet.views = [
+            {
+                state: 'frozen',
+                ySplit: 1,          // 冻结行数
+                topLeftCell: 'A2'   // 冻结后可见区域的起始单元格
+            }
+        ];
+
+        // 设置全局禁止
+        worksheet.protect('yourPwd', {
+            deleteColumns: false,       // 禁止删除列
+            sort: false,                // 禁止排序
+            autoFilter: false           // 禁止自动筛选
+        });
+
+        worksheet.columns = [
+            { key: 'A1', width: 20, header: '账号' },
+            { key: 'A2', width: 20, header: '群发标题' },
+            { key: 'A3', width: 30, header: '策略信息' },
+            { key: 'A4', width: 40, header: '发送对象' },
+            { key: 'A5', width: 70, header: '群发内容' },
+            { key: 'A6', width: 30, header: '图文链接' },
+            { key: 'A7', width: 30, header: '小程序APPID' },
+            { key: 'A8', width: 30, header: '小程序路径' },
+        ];
+
+        // 表头设置样式
+        worksheet.getRow(headerRowIndex).height = 30;
+        Array(8).fill(0).forEach((_, index) => {
+            const col = index + 1; // 从第1列开始
+            // 设置表头字体样式
+            worksheet.getCell(headerRowIndex, col).style = index >= 5 ? headerJsMustStyle as any : headerJsStyle as any;
+        })
+
+
+        const data = getGroupData(settings)
+        console.log('数据', data)
+
+        const corpUserListLength = settings?.corpUsers?.length || 0;
+        const dataLength = data.length || 0;
+        // 合并单元集合
+        const mergeCells = [];
+        // 解放填写限制区域
+        const unLockArea = [];
+        let dataRow = 1
+        // 数据内容
+        settings?.corpUsers?.forEach((item, i) => {
+            // 1、客服号 群发标题
+
+            const startRow = 2 + (i * dataLength)
+            const endRow = startRow + dataLength - 1
+            mergeCells.push({
+                startRow,
+                startColumn: 1,
+                endRow,
+                endColumn: 1,
+            })
+            mergeCells.push({
+                startRow,
+                startColumn: 2,
+                endRow,
+                endColumn: 2,
+            })
+
+            let strategyIndex = 0
+
+            data.forEach((dataItem, index, row) => {
+                if (strategyIndex !== dataItem.strategyIndex) {
+                    const startRow = i * row.length + (index + 2)
+                    mergeCells.push({
+                        startRow,
+                        startColumn: 3,
+                        endRow: startRow + dataItem.strategyDataCount - 1,
+                        endColumn: 3,
+                    })
+                }
+                if (dataItem.sendDataRowSpan) {
+                    const startRow = i * row.length + (index + 2)
+                    mergeCells.push({
+                        startRow,
+                        startColumn: 4,
+                        endRow: startRow + dataItem.sendDataRowSpan - 1,
+                        endColumn: 4,
+                    })
+                }
+                const mediaItem = JSON.parse(JSON.stringify(dataItem?.content?.attachmentList || []))
+                if (dataItem?.content?.text?.content) {
+                    mediaItem.push({
+                        msgType: 'TASK_CONTENT_TEXT',
+                        textContent: dataItem?.content?.text?.content
+                    })
+                }
+                let linkPlaceholder = '<空>'
+                let miniprogramPlaceholder = '<空>'
+                const contentRichText = mediaItem.map(item => {
+                    // 'TASK_CONTENT_TEXT' | 'TASK_CONTENT_IMAGE' | 'TASK_CONTENT_LINK' | 'TASK_STATUS_MINIPROGRAM' | 'TASK_STATUS_VIDEO' | 'TASK_STATUS_FILE'
+                    switch (item.msgType) {
+                        case 'TASK_CONTENT_LINK':
+                            linkPlaceholder = '请输入'
+                            return { text: `链接:${item.link.title}_${item.link.desc}\n`, font: { color: { argb: "FF0000" } } }
+                        case 'TASK_STATUS_MINIPROGRAM':
+                            miniprogramPlaceholder = '请输入'
+                            return { text: `小程序:${item.miniprogram.title}\n`, font: { color: { argb: "FF0000" } } }
+                        case 'TASK_STATUS_FILE':
+                            return { text: `文件:${item.file.fileUrl}\n` }
+                        case 'TASK_STATUS_VIDEO':
+                            return { text: `视频:${item.video.videoUrl}\n` }
+                        case 'TASK_CONTENT_IMAGE':
+                            return { text: `图片:${item.image.picUrl}\n` }
+                        case 'TASK_CONTENT_TEXT':
+                            return { text: `文本:${item.textContent}\n` }
+                        default:
+                            return { text: `该类型没有请联系管理员` }
+                    }
+                })
+
+                const richText = {
+                    richText: [
+                        { text: `(内容${dataItem?.contentIndex})\n`, font: { bold: true } },
+                        ...contentRichText
+                    ]
+                }
+
+                if (linkPlaceholder !== '<空>') {
+                    unLockArea.push(`${dataRow}_6`)
+                }
+                if (miniprogramPlaceholder !== '<空>') {
+                    unLockArea.push(`${dataRow}_7`, `${dataRow}_8`)
+                }
+                dataRow++;
+                strategyIndex = dataItem.strategyIndex
+                worksheet.addRow([
+                    // 账号
+                    `${item.name}(ID:${item.corpUserId})`,
+                    // 群发标题
+                    `${dataItem?.groupSendName}`,
+                    // 策略信息
+                    {
+                        richText: [
+                            { text: `(策略${dataItem?.strategyIndex})\n`, font: { bold: true } },
+                            { text: `名称:${dataItem?.strategyData?.strategyName || '<空>'}\n` },
+                            {
+                                text: (`执行类型:${TIME_TYPE[dataItem?.strategyData?.timeRepeatType]}\n`) +
+                                    (dataItem?.strategyData?.sendDay ? `执行时间:${dataItem?.strategyData?.sendDay}\n` : '') +
+                                    (dataItem?.strategyData?.startTime ? `执行日期:${dataItem?.strategyData?.startTime}~${dataItem?.strategyData?.endTime ? dataItem?.strategyData?.endTime : '长期执行'}\n` : '') +
+                                    (dataItem?.strategyData?.sendTime ? '执行时间:' + dataItem?.strategyData?.sendTime + '\n' : '') +
+                                    (dataItem?.strategyData?.repeatArray ? `执行天数:${dataItem?.strategyData?.repeatArray.join('、')}\n` : '')
+                            }
+                        ]
+                    },
+                    // 发送对象
+                    {
+                        richText: [
+                            { text: `(发送对象${dataItem?.sendDataIndex})\n`, font: { bold: true } },
+                            { text: `发送模式:${welcomeContentData?.find(i => i.value === dataItem?.sendMode)?.label}\n` },
+                            {
+                                text: `类型:${dataItem?.sendData?.externalUserType === 'all' ? '全部' : '指定'}\n` +
+                                    (dataItem?.sendData?.externalUserType === 'specify' && dataItem?.sendData?.externalUserFilter ? FilterUserText({
+                                        data: dataItem?.sendData?.externalUserFilter?.configContent,
+                                        configName: dataItem?.sendData?.externalUserFilter?.configName,
+                                        bookCityList: bookPlatForm?.map(item => ({ label: item.platformName, value: item.platformKey })),
+                                        bookPlatForm,
+                                        bookList
+                                    }) : '')
+                            }
+                        ]
+                    },
+                    // 群发内容
+                    richText,
+                    // 图文链接
+                    linkPlaceholder,
+                    // 小程序APPID
+                    miniprogramPlaceholder,
+                    // 小程序路径
+                    miniprogramPlaceholder
+                ])
+            })
+        })
+
+        // Array(corpUserListLength * dataLength).fill(0).forEach((_, index) => {
+        //     worksheet.getRow(index + headerRowIndex + 1).height = 100; // 设置行高
+        //     worksheet.getRow(index + headerRowIndex + 1).alignment = {
+        //         vertical: 'middle',
+        //         wrapText: true
+        //     }
+        //     // 分组设置背景色
+        //     if (index % (dataLength * 2) < dataLength) {
+        //         worksheet.getRow(index + headerRowIndex + 1).eachCell(function (cell, rowNumber) {
+        //             if (rowNumber <= 8) {
+        //                 cell.border = {
+        //                     top: { style: 'thin', color: { argb: 'D4D4D4' } },
+        //                     bottom: { style: 'thin', color: { argb: 'D4D4D4' } },
+        //                     left: { style: 'thin', color: { argb: 'D4D4D4' } },
+        //                     right: { style: 'thin', color: { argb: 'D4D4D4' } }
+        //                 }
+        //                 cell.fill = {
+        //                     type: 'pattern',
+        //                     pattern: 'solid',
+        //                     fgColor: { argb: 'f0f0f0' }
+        //                 }
+        //             }
+        //         })
+        //     } else {
+        //         worksheet.getRow(index + headerRowIndex + 1).eachCell(function (cell, rowNumber) {
+        //             if (rowNumber <= 8) {
+        //                 cell.border = {
+        //                     top: { style: 'thin', color: { argb: 'D4D4D4' } },
+        //                     bottom: { style: 'thin', color: { argb: 'D4D4D4' } },
+        //                     left: { style: 'thin', color: { argb: 'D4D4D4' } },
+        //                     right: { style: 'thin', color: { argb: 'D4D4D4' } }
+        //                 }
+        //             }
+        //         })
+        //     }
+        // })
+
+        // // 合并单元格
+        // mergeCells.forEach(({ startRow, startColumn, endRow, endColumn }) => {
+        //     worksheet.mergeCells(startRow, startColumn, endRow, endColumn); // 合并单元格
+        // })
+
+        // // 设置可填写区域
+        // for (let rowNum = 1; rowNum <= corpUserListLength * dataLength; rowNum++) {
+        //     for (let colNum = 1; colNum <= 3; colNum++) {
+        //         if (unLockArea.includes(`${rowNum}_${colNum + 5}`)) {
+        //             const cell = worksheet.getCell(rowNum + 1, colNum + 5);
+        //             cell.protection = { locked: false };
+        //             cell.fill = {
+        //                 type: 'pattern',
+        //                 pattern: 'solid',
+        //                 fgColor: { argb: 'ffadd2' }
+        //             }
+        //         }
+        //     }
+        // }
+        // 生成文件内容
+        workbook.xlsx.writeBuffer().then(buffer => {
+            // 通过 Blob 下载
+            const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+            saveAs(blob, `朋友圈内容配置_${dayjs().format('YYYYMMDDHHmmss')}.xlsx`);
+            setTimeout(() => setDownloadLoading(false), 1000)
+        });
+    }
+
     return <>
         <div className={`${style.settingsBody_content_row}`}>
             <div className={`${style.settingsBody_content_col}`}>
@@ -33,9 +287,9 @@ const Friends: React.FC = () => {
                 <div className={style.detail}>
                     <div className={style.detail_title}>朋友圈策略配置</div>
                     <div className={style.detail_body}>
-                        {/* {settings?.massSendingStrategy && Object.keys(settings?.massSendingStrategy).length > 0 ? <>
-                            <PreviewMassSendingStrategy massSendingStrategy={settings?.massSendingStrategy} />
-                        </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />} */}
+                        {settings?.friendsStrategy && Object.keys(settings?.friendsStrategy).length > 0 ? <>
+                            <PreviewFriendsStrategy friendsStrategy={settings?.friendsStrategy} />
+                        </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                     </div>
                 </div>
                 <div className={style.detail_footer}>
@@ -45,15 +299,15 @@ const Friends: React.FC = () => {
             <div className={`${style.settingsBody_content_col}`}>
                 <div className={style.title}>
                     <span></span>
-                    {/* {(settings?.massSendingStrategy && Object.keys(settings?.massSendingStrategy).length > 0) && <Space>
-                        {settings?.massSendingContent?.massSendingContentDTO?.some(item => item?.sendContentDto?.some(si => si?.contentDTO?.some(i => i?.attachmentList?.some(a => ['TASK_CONTENT_LINK', 'TASK_STATUS_MINIPROGRAM'].includes(a?.msgType))))) && <>
+                    {(settings?.friendsStrategy && Object.keys(settings?.friendsStrategy).length > 0) && <Space>
+                        {settings?.friendsContent?.friendsContentDTO?.some(item => item?.contentDTO?.some(i => i?.attachmentList?.some(a => ['link', 'miniprogram'].includes(a?.mediaType)))) && <>
                             <Button
                                 type="link"
                                 style={{ padding: 0, fontSize: 12 }}
                                 loading={downloadLoading}
                                 onClick={() => { exportExcel() }}
                             >下载</Button>
-                            <Upload
+                            {/* <Upload
                                 accept={'.xlsx'}
                                 action="#"
                                 showUploadList={false}
@@ -67,7 +321,7 @@ const Friends: React.FC = () => {
                                     type="link"
                                     style={{ padding: 0, fontSize: 12 }}
                                 >{settings?.corpUsers?.every(item => item?.groupMsgContent?.length) ? 'Excel内容已上传,重新上传' : '上传Excel'}</Button>
-                            </Upload>
+                            </Upload> */}
                         </>}
                         <Button
                             type="link"
@@ -75,29 +329,29 @@ const Friends: React.FC = () => {
                             style={{ padding: 0, fontSize: 12 }}
                             onClick={() => {
                                 const corpUsers = settings?.corpUsers?.map(item => {
-                                    delete item?.groupMsgContent
+                                    delete item?.friendsMsgContent
                                     return item
                                 })
                                 setSettings({
                                     ...settings,
                                     corpUsers,
-                                    massSendingStrategy: undefined,
-                                    massSendingContent: undefined
+                                    friendsStrategy: undefined,
+                                    friendsContent: undefined
                                 })
                                 onPreviewReset()
                             }}
                         >清空</Button>
-                    </Space>} */}
+                    </Space>}
                 </div>
                 <div className={style.detail}>
                     <div className={style.detail_title}>朋友圈内容配置</div>
                     <div className={style.detail_body} style={{ padding: 0 }}>
-                        {/* {settings?.massSendingContent && Object.keys(settings?.massSendingContent) ? <>
-                            <ShowContent
-                                strategySettings={settings?.massSendingStrategy?.strategySettings}
-                                massSendingContent={settings?.massSendingContent}
+                        {settings?.friendsContent && Object.keys(settings?.friendsContent) ? <>
+                            <ShowFriendsContent
+                                strategySettings={settings?.friendsStrategy?.strategySettings}
+                                friendsContent={settings?.friendsContent}
                             />
-                        </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />} */}
+                        </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                     </div>
                 </div>
                 <div className={style.detail_footer}>
@@ -106,7 +360,7 @@ const Friends: React.FC = () => {
                         style={{ padding: 0, fontSize: 12, color: token.colorPrimary }}
                         size='small'
                         onClick={() => {
-                            if (!(settings?.massSendingStrategy && Object.keys(settings?.massSendingStrategy))) {
+                            if (!(settings?.friendsStrategy && Object.keys(settings?.friendsStrategy).length)) {
                                 message.error('请先配置策略')
                                 return
                             }
@@ -118,22 +372,22 @@ const Friends: React.FC = () => {
         </div>
 
         {/* 模板配置 */}
-        {/* {newVisible && <SettingsMassSending
+        {newVisible && <SettingsFriends
             visible={newVisible}
             createType={createType}
             value={{
-                massSendingContent: settings?.massSendingContent,
-                massSendingStrategy: settings?.massSendingStrategy
+                friendsContent: settings?.friendsContent,
+                friendsStrategy: settings?.friendsStrategy
             }}
             onClose={() => {
                 setNewVisible(false)
             }}
             onChange={(values, type) => {
                 const corpUsers = settings?.corpUsers?.map(item => {
-                    delete item?.groupMsgContent
+                    delete item?.friendsMsgContent
                     return item
                 })
-                if (!values?.massSendingContent && settings?.corpUsers?.some(item => item?.groupMsgContent?.length)) {
+                if (!values?.friendsContent && settings?.corpUsers?.some(item => item?.friendsMsgContent?.length)) {
                     setSettings({
                         ...settings,
                         ...values,
@@ -152,7 +406,7 @@ const Friends: React.FC = () => {
                 }
                 onPreviewReset()
             }}
-        />} */}
+        />}
     </>
 };
 

+ 162 - 0
src/pages/weComTask/page/businessPlan/create/components/friends/materialFriendsNoTextMould.tsx

@@ -0,0 +1,162 @@
+import { Radio, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import { TextEditor } from "@/pages/weComTask/components/textEditor"
+import MaterialMould from "@/pages/weComTask/components/materialMould"
+
+interface Props {
+    value?: TASK_CREATE.ContentDTOProps
+    onChange?: (value?: TASK_CREATE.ContentDTOProps) => void
+}
+
+const types = [
+    { label: '图片', value: 'image' },
+    { label: '视频', value: 'video' },
+    { label: '音频', value: 'voice' },
+    { label: '链接', value: 'link' },
+    { label: '小程序', value: 'miniprogram' },
+    { label: '文件', value: 'file' },
+]
+const mediaSizeObj = {
+    'image': {
+        size: 10 * 1024 * 1024,
+        evalS: '>',
+        width: 1440,
+        evalW: '>',
+        height: 1080,
+        evalH: '>',
+        msg: '朋友圈类型图片不超过1440 x 1080,10MB,支持JPG,PNG格式!'
+    },
+    'video': {
+        size: 10 * 1024 * 1024,
+        evalS: '>',
+        duration: 30,
+        evalD: '>',
+        msg: '朋友圈类型视频时长不超过30秒,10MB,支持MP4格式!'
+    },
+}
+
+/**
+ * 朋友圈内容设置
+ * @returns 
+ */
+const MaterialFriendsNoTextMould: React.FC<Props> = ({ onChange, value }) => {
+
+    /***************************************/
+    const [type, setType] = useState<'text' | 'video' | 'file' | 'link' | 'voice' | 'miniprogram' | 'image'>('image')//当前选择的素材类型
+    const [dataNum, setDataNum] = useState<any>(9)
+    const [noShowType, setNoShowType] = useState<any[]>(['video', 'voice', 'link', 'miniprogram', 'file'])//不可选的素材
+    const [textContent, setTextContent] = useState<string>()
+    const [attachmentList, setAttachmentList] = useState<any>()
+    /***************************************/
+    
+    /** 回填 */
+    useEffect(() => {
+        if (value) {
+            if (value?.text?.content && !textContent) {
+                const newValue = value?.text?.content
+                setTextContent(newValue)
+            }
+            if (value?.attachmentList && value?.attachmentList?.length > 0 && !attachmentList) {
+                const newAttachmentList: any = value?.attachmentList
+                const newType = newAttachmentList?.[0]?.mediaType || 'image'
+                setType(newType)
+                setAttachmentList(newAttachmentList)
+            }
+        }
+    }, [value, textContent, attachmentList])
+
+    /** 返回 */
+    const handleChange = (textContent: any, attachmentList?: any) => {
+        let newAttachmentList: TASK_CREATE.Attachment[] = []
+        let text: any = {}
+        if (textContent) {
+            text = {
+                content: textContent
+            }
+        }
+        if (attachmentList && attachmentList?.length > 0) {
+            newAttachmentList = attachmentList
+        }
+        if (text?.content) {
+            text.content = text.content.replace(/\n$/, '')
+        }
+        onChange?.({
+            attachmentList: newAttachmentList as any,
+            text
+        })
+    }
+
+    return <Space style={{ width: '100%' }} direction='vertical'>
+        <TextEditor
+            maxStr={2000}
+            value={textContent || ''}
+            onChange={(value) => {
+                setTextContent(value)
+                handleChange(value, attachmentList)
+            }}
+        />
+        <Space>
+            <span>附件:</span>
+            <Radio.Group
+                defaultValue={'image'}
+                value={type}
+                onChange={(e) => {
+                    const v = e.target.value
+                    setType(v)
+                    setDataNum(v !== 'image' ? 1 : 9)
+                    const noArr = types.filter(i => i.value !== v).map(item => item.value)
+                    setNoShowType([...noArr, 'text'])
+                    setAttachmentList([])
+                    handleChange(textContent, [])
+                }} >
+                {
+                    types?.map((item, index) => {
+                        if (item.label === '图片' || item.label === '视频' || item.label === '链接') {
+                            return <Radio value={item.value} key={item.value || index}>{item.label}</Radio>
+                        }
+                    })
+                }
+            </Radio.Group>
+        </Space>
+        <MaterialMould
+            dataNum={dataNum}
+            noShowType={noShowType}
+            type={type}
+            textCount={1}
+            mediaSize={mediaSizeObj[type as keyof typeof mediaSizeObj] as any || null}
+            value={attachmentList}
+            onChange={(value) => {
+                if (value?.length > 0) {
+                    const newText: TASK_CREATE.MediaContentProps[] = []
+                    const newAttachmentList: TASK_CREATE.MediaContentProps[] = []
+                    value?.forEach((cur: TASK_CREATE.MediaContentProps) => {
+                        if (cur.mediaType === 'text') {
+                            newText.push(cur)
+                        } else {
+                            newAttachmentList.push(cur)
+                        }
+                    })
+                    if (newText.length > 0) {
+                        if (textContent) {
+                            setTextContent('')
+                            setAttachmentList(newAttachmentList)
+                            handleChange(newText[0].textContent, newAttachmentList)
+                        } else {
+                            setTextContent('')
+                            setAttachmentList(newAttachmentList)
+                            handleChange(newText[0].textContent, newAttachmentList)
+                        }
+                    } else {
+                        setAttachmentList(value)
+                        handleChange(textContent, value)
+                    }
+                } else {
+                    setAttachmentList([])
+                    handleChange(textContent, [])
+                }
+            }} 
+        />
+    </Space>
+}
+
+export default React.memo(MaterialFriendsNoTextMould)

+ 97 - 0
src/pages/weComTask/page/businessPlan/create/components/friends/previewFriendsStrategy.tsx

@@ -0,0 +1,97 @@
+import { Card, Form, Input, Radio } from 'antd';
+import React, { useEffect } from 'react';
+import style from '../../index.less';
+import SendTimeSet from '@/pages/weComTask/components/sendTimeSet';
+import FilterUser from '@/pages/weComTask/components/filterUser';
+import dayjs from 'dayjs';
+import '../../global.less'
+
+/**
+ * 朋友圈预览
+ * @param param0 
+ * @returns 
+ */
+const PreviewFriendsStrategy: React.FC<{ friendsStrategy: { [x: string]: any }, configType?: BUSINES_SPLAN_API.ConfigTypeProps }> = ({ friendsStrategy, configType = 'USER_GROUP' }) => {
+
+    /************************************************/
+    const [form] = Form.useForm();
+    const strategySettings = Form.useWatch('strategySettings', form);
+    /************************************************/
+
+    useEffect(() => {
+        if (friendsStrategy) {
+            form.setFieldsValue({
+                ...friendsStrategy, strategySettings: friendsStrategy?.strategySettings?.map(item => {
+                    const { sendTime, startTime, endTime, sendDay, timeRepeatType } = item
+                    if (timeRepeatType === 'TIME_TYPE_SINGLE_PLACE') {
+                        return {
+                            ...item,
+                            timeRepeatType,
+                            sendDay: sendDay ? dayjs(sendDay + ' ' + sendTime) : undefined
+                        }
+                    }
+                    return {
+                        ...item,
+                        timeRepeatType,
+                        sendTime: sendTime ? dayjs('2025-04-25 ' + sendTime) : undefined,
+                        startTime: startTime ? dayjs(startTime) : undefined,
+                        endTime: endTime ? dayjs(endTime) : undefined,
+                        sendDay: sendDay ? dayjs(sendDay) : undefined
+                    }
+                })
+            })
+        }
+    }, [friendsStrategy])
+
+    return <Form
+        form={form}
+        name={'friendsStrategyShowzs' + configType}
+        labelAlign='left'
+        labelCol={{ span: 5 }}
+        colon={false}
+        preserve={true}
+    >
+        <Card
+            title={<strong>基础信息配置</strong>}
+            style={{ background: '#fff', marginBottom: 10 }}
+            id='basicInfo'
+        >
+            <div className='block_tm'>
+                <Form.Item label={<strong>朋友圈标题</strong>} name="momentJobTitle">
+                    <Input placeholder="请输入标题" />
+                </Form.Item>
+            </div>
+        </Card>
+
+        <Form.List name="strategySettings">
+            {(fields) => (
+                <div style={{ padding: '4px 2px 0', border: '1px dashed #d9d9d9', borderRadius: 8, background: '#f5f5f5' }}>
+                    {fields.map(({ key, name, ...restField }, index) => {
+                        const timeRepeatType = strategySettings?.[index]?.timeRepeatType
+
+                        return <Card
+                            key={key}
+                            title={<strong>策略{index + 1}配置</strong>}
+                            style={{ background: '#fff', marginBottom: 10 }}
+                        >
+                            <div className='block_tm'>
+                                <div className={style.strategy_item}>
+                                    <Form.Item
+                                        {...restField}
+                                        label={<strong>策略名称</strong>}
+                                        name={[name, 'strategyName']}
+                                    >
+                                        <Input placeholder="请输入标题" />
+                                    </Form.Item>
+                                    <SendTimeSet isShow active='all' form={form} restField={restField} name={name} timeRepeatType={timeRepeatType} />
+                                </div>
+                            </div>
+                        </Card>
+                    })}
+                </div>
+            )}
+        </Form.List>
+    </Form>
+};
+
+export default React.memo(PreviewFriendsStrategy);

+ 9 - 9
src/pages/weComTask/page/businessPlan/create/components/friends/settingsFriends.tsx

@@ -9,7 +9,7 @@ import Content from './content';
  * @param param0 
  * @returns 
  */
-const SettingsFriends: React.FC<TASK_CREATE.MassSendingProps<any>> = ({ value: { massSendingContent, massSendingStrategy }, createType, onChange, visible, onClose }) => {
+const SettingsFriends: React.FC<TASK_CREATE.FriendsProps<any>> = ({ value: { friendsContent, friendsStrategy }, createType, onChange, visible, onClose }) => {
 
     /****************************************/
     const { message } = App.useApp()
@@ -43,32 +43,32 @@ const SettingsFriends: React.FC<TASK_CREATE.MassSendingProps<any>> = ({ value: {
         {/* 策略 */}
         {type === 'STRATEGY' ? <Strategy
             ref={ref}
-            value={massSendingStrategy}
+            value={friendsStrategy}
             onChange={(value, type) => {
                 console.log(value, type)
                 if (type === 'CREATE_CONTENT') {
                     setType('CONTENT')
                 }
-                if (massSendingContent && (value?.strategySettings?.length !== massSendingContent?.massSendingContentDTO?.length || value?.strategySettings?.some((item, index) => massSendingContent?.massSendingContentDTO?.[index]?.sendContentDto?.length !== item?.sendData?.length))) {
+                if (friendsContent && (value?.strategySettings?.length !== friendsContent?.friendsContentDTO?.length)) {
                     message.error('策略配置和内容配置不一致,内容已重置,请重新配置!')
                     onChange({
-                        massSendingContent: undefined,
-                        massSendingStrategy: value
+                        friendsContent: undefined,
+                        friendsStrategy: value
                     }, type)
                     return
                 }
 
                 onChange({
-                    massSendingStrategy: value
+                    friendsStrategy: value
                 }, type)
             }}
         /> : <Content
             ref={ref1}
-            massSendingStrategy={massSendingStrategy}
-            value={massSendingContent}
+            friendsStrategy={friendsStrategy}
+            value={friendsContent}
             onChange={(value, type) => {
                 onChange({
-                    massSendingContent: value
+                    friendsContent: value
                 }, type)
             }}
         />}

+ 59 - 0
src/pages/weComTask/page/businessPlan/create/components/friends/showFriendsContent.tsx

@@ -0,0 +1,59 @@
+import React from 'react';
+import style from '../massSending/index.less'
+import { Tabs } from 'antd';
+import useNewToken from '@/Hook/useNewToken';
+import { welcomeContentData } from '../../const';
+import LookPyq from '@/pages/weComTask/components/previewMsg/lookPyq';
+
+
+interface ShowContentProps {
+    strategySettings: any[];
+    friendsContent?: { friendsContentDTO: any };
+}
+
+const ShowFriendsContent: React.FC<ShowContentProps> = ({ strategySettings, friendsContent }) => {
+
+    /*************************************/
+    const { token } = useNewToken()
+    /*************************************/
+
+    return <div className={style.showContent}>
+        <Tabs
+            tabPosition='left'
+            className='mindTags'
+            style={{ height: '100%', background: '#FFF' }}
+            size='small'
+            popupClassName={style.popupClassName}
+            items={strategySettings?.map((item, index) => {
+
+                const friendsContentDTO = friendsContent?.friendsContentDTO?.[index]
+                return {
+                    label: '策略' + (index + 1),
+                    key: `${index}`,
+                    children: <div className={style.showContent_content}>
+                        <div style={{ backgroundColor: token.colorPrimary, padding: 4, color: '#FFF', fontSize: 12, marginBottom: 5 }}>
+                            <div>策略名称:{item?.strategyName || '<空>'}</div>
+                        </div>
+                        {friendsContentDTO && <div>
+                            <div style={{ backgroundColor: token.colorPrimary, padding: 4, color: '#FFF', fontSize: 12 }}>
+                                <div>
+                                    发送模式:{welcomeContentData?.find(i => i.value === friendsContentDTO?.sendMode)?.label}
+                                </div>
+                            </div>
+                            {friendsContentDTO?.contentDTO?.map((item, index) => {
+                                return <div key={index} style={{ marginTop: 5, backgroundColor: '#f1f1f1', borderRadius: 6, padding: '5px 10px 16px' }}>
+                                    <h3 style={{ textAlign: 'center' }}>内容{index + 1}</h3>
+                                    <LookPyq
+                                        content={{ text: item?.text?.content, data: item?.attachmentList }}
+                                    />
+                                </div>
+                            })}
+                        </div>}
+                    </div>
+                }
+            })}
+        />
+    </div>
+};
+
+export default React.memo(ShowFriendsContent);

+ 12 - 70
src/pages/weComTask/page/businessPlan/create/components/friends/strategy.tsx

@@ -9,7 +9,7 @@ import dayjs from 'dayjs';
 import FilterUser from '@/pages/weComTask/components/filterUser';
 
 /**
- * 群发策略
+ * 朋友圈策略配置
  * @param param0 
  * @returns 
  */
@@ -22,9 +22,9 @@ const Strategy = forwardRef(({ value, onChange }: TASK_CREATE.StrategyProps, ref
     const strategySettings = Form.useWatch('strategySettings', form);
 
     const [stepsList, setStepsList] = useState<NewStepsItem[]>([
-        { title: '群发配置', description: '群发类型、群发标题、适用产品', id: 'basicInfo' },
+        { title: '朋友圈配置', description: '朋友圈类型、朋友圈标题、适用产品', id: 'basicInfo' },
         {
-            title: '群发策略', children: [{ title: `策略1`, id: 'strategy_0', children: [{ title: `发送时间`, id: `strategy_0_strategyName` }, { title: `发送对象1`, id: `strategy_0_sendData` }] }, { title: `完成` }]
+            title: '朋友圈策略', children: [{ title: `策略1`, id: 'strategy_0' }, { title: `完成` }]
         },
         { title: '完成' }
     ])
@@ -101,7 +101,7 @@ const Strategy = forwardRef(({ value, onChange }: TASK_CREATE.StrategyProps, ref
         });
     };
 
-    const filedUpdateChange = ({ groupSendName, strategySettings }: any) => {
+    const filedUpdateChange = ({ momentJobTitle, strategySettings }: any) => {
         const isChecked = (content: NewStepsItem[]) => {
             return content.every(item => {
                 if (item.children) {
@@ -119,29 +119,19 @@ const Strategy = forwardRef(({ value, onChange }: TASK_CREATE.StrategyProps, ref
                 (timeRepeatType === "TIME_TYPE_REPEAT_DAY" && startTime && sendTime) ||
                 ((timeRepeatType === "TIME_TYPE_REPEAT_WEEK" || timeRepeatType === "TIME_TYPE_REPEAT_MONTH") && startTime && sendTime && repeatArray)
 
-            const children = [
-                {
-                    title: `发送时间`, checked: sendTimeChecked, id: `strategy_${index}_strategyName`
-                },
-                ...item.sendData.map((i, n) => {
-                    return { title: `发送对象${n + 1}`, description: '对象筛选,人群包', id: `strategy_${index}_${n}_sendData`, checked: i?.externalUserType ? i?.externalUserType === 'all' ? true : i?.externalUserFilter && Object.values(i.externalUserFilter).some(item => item) : false }
-                })
-            ]
-
             return {
                 title: `策略${index + 1}`,
                 id: `strategy_${index}`,
-                children: children,
-                checked: isChecked(children)
+                checked: sendTimeChecked
             }
         })
 
-        const isChecked1 = !!groupSendName
+        const isChecked1 = !!momentJobTitle
         const isChecked2 = isChecked(content)
         const stepsData = [
-            { title: '群发配置', description: '群发标题', checked: isChecked1, id: 'basicInfo' },
+            { title: '朋友圈配置', description: '朋友圈标题', checked: isChecked1, id: 'basicInfo' },
             {
-                title: '群发策略', checked: isChecked2, children: [...content, { title: `完成`, checked: isChecked2 }]
+                title: '朋友圈策略', checked: isChecked2, children: [...content, { title: `完成`, checked: isChecked2 }]
             },
             { title: '完成', checked: isChecked1 && isChecked2 }
         ]
@@ -161,7 +151,7 @@ const Strategy = forwardRef(({ value, onChange }: TASK_CREATE.StrategyProps, ref
         <div className={`body_content`} ref={ref1}>
             <Form
                 form={form}
-                name="newMassSending"
+                name="newFriendsStrategy"
                 labelAlign='left'
                 labelCol={{ span: 5 }}
                 colon={false}
@@ -183,17 +173,16 @@ const Strategy = forwardRef(({ value, onChange }: TASK_CREATE.StrategyProps, ref
             >
                 <Card title={<strong>基础信息配置</strong>} style={{ background: '#fff', marginBottom: 10 }} hoverable id='basicInfo'>
 
-                    <Form.Item label={<strong>群发标题</strong>} name="groupSendName" rules={[{ required: true, message: '请输入标题!' }]}>
+                    <Form.Item label={<strong>朋友圈标题</strong>} name="momentJobTitle" rules={[{ required: true, message: '请输入标题!' }]}>
                         <Input placeholder="请输入标题" style={{ width: 358 }} allowClear />
                     </Form.Item>
                 </Card>
 
                 <Form.List name="strategySettings">
                     {(fields, { add, remove }) => (
-                        <>
+                        <div style={{ padding: '4px 2px 0', border: '1px dashed #d9d9d9', borderRadius: 8, background: '#f5f5f5' }}>
                             {fields.map(({ key, name, ...restField }, index) => {
                                 const timeRepeatType = strategySettings?.[index]?.timeRepeatType
-                                const sendData = strategySettings?.[index]?.sendData
 
                                 return <Card
                                     key={key}
@@ -213,53 +202,6 @@ const Strategy = forwardRef(({ value, onChange }: TASK_CREATE.StrategyProps, ref
                                             <Input placeholder="请输入标题" style={{ width: 358 }} allowClear />
                                         </Form.Item>
                                         <SendTimeSet active='all' form={form} restField={restField} name={name} timeRepeatType={timeRepeatType} />
-
-                                        <Form.List name={[name, 'sendData']}>
-                                            {(fields, { add, remove }) => (
-                                                <>
-                                                    {fields.map(({ key, name, ...restField }, i) => {
-                                                        return <Card
-                                                            key={i}
-                                                            title={<strong>策略{index + 1} 发送对象{i + 1}</strong>}
-                                                            style={{ background: '#fff', marginBottom: 10 }}
-                                                            extra={sendData?.length > 1 ? <Button icon={<DeleteOutlined />} type='link' style={{ color: 'red' }} onClick={() => remove(name)}></Button> : null}
-                                                            id={`strategy_${index}_${i}_sendData`}
-                                                        >
-                                                            <Form.Item
-                                                                label={<strong>发送对象配置</strong>}
-                                                                required
-                                                            >
-                                                                <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
-                                                                    <Form.Item
-                                                                        {...restField}
-                                                                        name={[name, 'externalUserType']}
-                                                                        rules={[{ required: true, message: '请选择转移对象!' }]}
-                                                                        noStyle
-                                                                    >
-                                                                        <Radio.Group options={[{ label: '全部', value: 'all' }, { label: '指定', value: 'specify' }]} />
-                                                                    </Form.Item>
-                                                                    {sendData?.[i]?.externalUserType === 'specify' && <div style={{ marginTop: 8, width: '100%' }}>
-                                                                        <Form.Item
-                                                                            {...restField}
-                                                                            name={[name, 'externalUserFilter']}
-                                                                            rules={[{ required: true, message: '请选择人群包!' }]}
-                                                                            noStyle
-                                                                        >
-                                                                            <FilterUser configType={'USER_GROUP'} />
-                                                                        </Form.Item>
-                                                                    </div>}
-                                                                </div>
-                                                            </Form.Item>
-                                                        </Card>
-                                                    })}
-                                                    <Form.Item>
-                                                        <Button type="dashed" onClick={() => add({ externalUserType: 'all' })} block icon={<PlusOutlined />}>
-                                                            新增发送对象
-                                                        </Button>
-                                                    </Form.Item>
-                                                </>
-                                            )}
-                                        </Form.List>
                                     </div>
                                 </Card>
                             })}
@@ -268,7 +210,7 @@ const Strategy = forwardRef(({ value, onChange }: TASK_CREATE.StrategyProps, ref
                                     新增策略组
                                 </Button>
                             </Form.Item>
-                        </>
+                        </div>
                     )}
                 </Form.List>
             </Form>

+ 1 - 1
src/pages/weComTask/page/businessPlan/create/components/highMassSending/content.tsx

@@ -135,7 +135,7 @@ const Content = forwardRef(({ massSendingStrategy, value, onChange }: TASK_CREAT
                     {(fields) => (
                         <>
                             {fields.map(({ key, name, ...restField }, index) => {
-                                return <Card key={index} title={<strong>策略 {index + 1}</strong>} style={{ background: '#fff', marginBottom: 10 }} id={'clientId' + '_' + index}>
+                                return <Card key={index} title={<strong>策略名称:{massSendingStrategy?.strategySettings?.[index]?.strategyName || '<空>'}</strong>} style={{ background: '#fff', marginBottom: 10 }} id={'clientId' + '_' + index}>
                                     <Form.List {...restField} name={[name, 'sendContentDto']}>
                                         {(fields) => (
                                             <>

+ 1 - 1
src/pages/weComTask/page/businessPlan/create/components/massSending/content.tsx

@@ -134,7 +134,7 @@ const Content = forwardRef(({ massSendingStrategy, value, onChange }: TASK_CREAT
                     {(fields) => (
                         <>
                             {fields.map(({ key, name, ...restField }, index) => {
-                                return <Card key={index} title={<strong>策略 {index + 1}</strong>} style={{ background: '#fff', marginBottom: 10 }} id={'clientId' + '_' + index}>
+                                return <Card key={index} title={<strong>策略名称:{massSendingStrategy?.strategySettings?.[index]?.strategyName || `<空>`}</strong>} style={{ background: '#fff', marginBottom: 10 }} id={'clientId' + '_' + index}>
                                     <Form.List {...restField} name={[name, 'sendContentDto']}>
                                         {(fields) => (
                                             <>

+ 4 - 4
src/pages/weComTask/page/businessPlan/create/index.tsx

@@ -33,7 +33,7 @@ const Create: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookLis
 
     const [settings, setSettings] = useState<TASK_CREATE.SettingsProps>();
 
-    console.log(settings)
+    console.log('settings--->', settings)
     const { message, modal } = App.useApp()
     const [msgJobTypeList, setMsgJobTypeList] = useState<{ value: string, label: string }[]>([])
     const [previewData, setPreviewData] = useState<TASK_CREATE.previewDataProps>({})
@@ -68,7 +68,6 @@ const Create: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookLis
             return
         }
 
-        const corpUsersLength = settings?.corpUsers?.length
         // 欢迎语
         if (settings?.welcomeMsgTemplateDTO && Object.keys(settings?.welcomeMsgTemplateDTO).length) {
             if (settings?.welcomeMsgTemplateDTO?.mediaContentList?.some(item => item?.some(i => i?.mediaType === 'link' || i?.mediaType === 'miniprogram')) && settings?.corpUsers?.every(item => !item?.welcomeMsgContent?.length)) {
@@ -584,7 +583,7 @@ const Create: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookLis
                                 showSearch
                                 allowClear
                                 placeholder="请选择书城"
-                                style={{ width: 200 }}
+                                style={{ width: 120 }}
                                 filterOption={(input, option) =>
                                     ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
                                 }
@@ -600,7 +599,7 @@ const Create: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookLis
                             <Button>适用产品</Button>
                             <Select
                                 showSearch
-                                style={{ width: 200 }}
+                                style={{ width: 150 }}
                                 allowClear
                                 placeholder="请选择模板适用产品"
                                 filterOption={(input, option) =>
@@ -672,6 +671,7 @@ const Create: React.FC<{ weComTaskStore: { data: { bookList: TASK_CREATE.BookLis
                         title="确定清空?"
                         onConfirm={() => {
                             setSettings(undefined)
+                            setPreviewData({})
                             localStorage.removeItem('TASK_CORP_CREATE')
                         }}
                     >

+ 6 - 0
src/pages/weComTask/page/businessPlan/create/typings.d.ts

@@ -66,6 +66,7 @@ declare namespace TASK_CREATE {
         highGroupMsgContent?: any[],
         externalUserTransferContent?: any[],
         externalUserList?: any[]
+        friendsMsgContent?: any[]
     }
     // 配置保存字段
     interface SettingsProps {
@@ -94,6 +95,11 @@ declare namespace TASK_CREATE {
         onChange?: (value?: { [x: string]: any }, type: string) => void;
         value?: { massSendingContentDTO: any };
     }
+    interface FriendsContentProps {
+        friendsStrategy: { [x: string]: any };
+        onChange?: (value?: { [x: string]: any }, type: string) => void;
+        value?: { friendsContentDTO: any };
+    }
     interface ContentDTOProps {
         attachmentList?: Attachment[],
         text?: {

+ 6 - 5
src/pages/weComTask/page/corpUserManage/index.tsx

@@ -100,12 +100,13 @@ const CorpUserManage: React.FC = () => {
                 >
                     <>
                         <Input onChange={(e) => setQueryFormNew({ ...queryFormNew, corpIds: e.target.value as any })} value={queryFormNew?.corpIds} placeholder="企微ID(多个,,空格换行)" allowClear />
+                        <Input onChange={(e) => setQueryFormNew({ ...queryFormNew, corpUserName: e.target.value as any })} value={queryFormNew?.corpUserName} placeholder="客服号名称" allowClear />
                         <Input onChange={(e) => setQueryFormNew({ ...queryFormNew, corpUserIds: e.target.value as any })} value={queryFormNew?.corpUserIds} placeholder="客服ID(多个,,空格换行)" allowClear />
                         <Select
                             value={queryFormNew?.operUserId}
                             onChange={(e) => setQueryFormNew({ ...queryFormNew, operUserId: e })}
                             showSearch
-                            style={{ width: 150 }}
+                            style={{ width: 120 }}
                             placeholder="运营"
                             filterOption={(input, option) =>
                                 ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
@@ -117,7 +118,7 @@ const CorpUserManage: React.FC = () => {
                             value={queryFormNew?.groupOwner}
                             onChange={(e) => setQueryFormNew({ ...queryFormNew, groupOwner: e })}
                             showSearch
-                            style={{ width: 150 }}
+                            style={{ width: 100 }}
                             placeholder="是否群主号"
                             filterOption={(input, option) =>
                                 ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
@@ -129,7 +130,7 @@ const CorpUserManage: React.FC = () => {
                             value={queryFormNew?.machine}
                             onChange={(e) => setQueryFormNew({ ...queryFormNew, machine: e })}
                             showSearch
-                            style={{ width: 150 }}
+                            style={{ width: 100 }}
                             placeholder="是否机器号"
                             filterOption={(input, option) =>
                                 ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
@@ -141,7 +142,7 @@ const CorpUserManage: React.FC = () => {
                             value={queryFormNew?.status}
                             onChange={(e) => setQueryFormNew({ ...queryFormNew, status: e })}
                             showSearch
-                            style={{ width: 150 }}
+                            style={{ width: 100 }}
                             placeholder="状态"
                             filterOption={(input, option) =>
                                 ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
@@ -153,7 +154,7 @@ const CorpUserManage: React.FC = () => {
                             value={queryFormNew?.stopUse}
                             onChange={(e) => setQueryFormNew({ ...queryFormNew, stopUse: e })}
                             showSearch
-                            style={{ width: 150 }}
+                            style={{ width: 100 }}
                             placeholder="是否停用"
                             filterOption={(input, option) =>
                                 ((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())

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

@@ -81,6 +81,7 @@ const SelectCorpUser: React.FC<Props> = ({ value = [], onChange, placeholder })
         content={<div className='selectCorpUserBody'>
             <div className='selectCorpUserBody_search'>
                 <Input style={{ width: 150 }} onChange={(e) => setQueryFormNew({ ...queryFormNew, corpIds: e.target.value as any })} value={queryFormNew?.corpIds} placeholder="企微ID(多个,,空格换行)" allowClear />
+                <Input style={{ width: 150 }} onChange={(e) => setQueryFormNew({ ...queryFormNew, corpUserName: e.target.value as any })} value={queryFormNew?.corpUserName} placeholder="客服号名称" allowClear />
                 <Input style={{ width: 150 }} onChange={(e) => setQueryFormNew({ ...queryFormNew, corpUserIds: e.target.value as any })} value={queryFormNew?.corpUserIds} placeholder="客服ID(多个,,空格换行)" allowClear />
                 <Select
                     value={queryFormNew?.groupId}