feat(publish): Wechat OffiAccount (#2386)

* feat: OffiAccount fe

* feat: offiaccount

* fix: wecom requires AES key

* fix: OffiAccountEditModal

* chore: change wechat svg icon

* chore: add offiaccount svg

* chore: hide unimplemented wecom entries
This commit is contained in:
Finley Ge
2024-08-15 12:06:13 +08:00
committed by GitHub
parent 5bbaa8264a
commit f8b8fcc172
14 changed files with 486 additions and 13 deletions

View File

@@ -3,5 +3,6 @@ export enum PublishChannelEnum {
iframe = 'iframe',
apikey = 'apikey',
feishu = 'feishu',
wecom = 'wecom'
wecom = 'wecom',
officialAccount = 'official_account'
}

View File

@@ -25,7 +25,18 @@ export interface WecomAppType {
// TODO: unused
export interface WechatAppType {}
export type OutlinkAppType = FeishuAppType | WecomAppType | undefined;
export interface OffiAccountAppType {
appId: string;
isVerified?: boolean; // if isVerified, we could use '客服接口' to reply
secret: string;
CallbackToken: string;
CallbackEncodingAesKey?: string;
timeoutReply?: string; // if timeout (15s), will reply this content.
// timeout reply is optional, but when isVerified is false, the wechat will reply a default message which is `该公众号暂时无法提供服务,请稍后再试`
// because we can not reply anything in 15s. Thus, the wechat server will treat this request as a failed request.
}
export type OutlinkAppType = FeishuAppType | WecomAppType | OffiAccountAppType | undefined;
export type OutLinkSchema<T extends OutlinkAppType = undefined> = {
_id: string;

View File

@@ -1,6 +1,7 @@
export enum TmpDataEnum {
FeishuAccessToken = 'feishu_access_token',
WecomAccessToken = 'wecom_access_token'
WecomAccessToken = 'wecom_access_token',
OffiAccountAccessToken = 'offiaccount_access_token'
}
type _TmpDataMetadata = {
@@ -11,6 +12,9 @@ type _TmpDataMetadata = {
CorpId: string;
AgentId: string;
};
[TmpDataEnum.OffiAccountAccessToken]: {
AppId: string;
};
};
type _TmpDataType = {
@@ -20,11 +24,15 @@ type _TmpDataType = {
[TmpDataEnum.WecomAccessToken]: {
accessToken: string;
};
[TmpDataEnum.OffiAccountAccessToken]: {
accessToken: string;
};
};
export const TmpDataExpireTime = {
[TmpDataEnum.FeishuAccessToken]: 1000 * 60 * 60 * 1.5, // 1.5 hours
[TmpDataEnum.WecomAccessToken]: 1000 * 60 * 60 * 2 // 2 hours
[TmpDataEnum.WecomAccessToken]: 1000 * 60 * 60 * 2, // 2 hours
[TmpDataEnum.OffiAccountAccessToken]: 1000 * 60 * 60 * 2 // 2 hours
};
export type TmpDataMetadata<T extends TmpDataEnum> = _TmpDataMetadata[T];

View File

@@ -76,6 +76,8 @@ export const iconPaths = {
'core/app/logsLight': () => import('./icons/core/app/logsLight.svg'),
'core/app/markLight': () => import('./icons/core/app/markLight.svg'),
'core/app/publish/lark': () => import('./icons/core/app/publish/lark.svg'),
'core/app/publish/offiaccount': () => import('./icons/core/app/publish/offiaccount.svg'),
'core/app/publish/wechat': () => import('./icons/core/app/publish/wechat.svg'),
'core/app/publish/wecom': () => import('./icons/core/app/publish/wecom.svg'),
'core/app/questionGuide': () => import('./icons/core/app/questionGuide.svg'),
'core/app/schedulePlan': () => import('./icons/core/app/schedulePlan.svg'),

View File

@@ -0,0 +1,22 @@
<svg width="22" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="new_bg_logo_primary69a6bf 1" clip-path="url(#clip0_9425_7305)">
<g id="Group">
<g id="Group_2">
<g id="Group_3">
<path id="Vector" d="M18.8275 7.82108C17.1159 5.55442 14.1091 4.95306 11.3799 5.92448C11.4724 5.97074 11.5649 5.97074 11.6574 6.017C15.6819 7.3585 17.8098 11.7068 16.4683 15.7313C16.0982 16.7952 15.5431 17.7204 14.8493 18.5068C15.7282 18.2755 16.6071 17.9054 17.3472 17.3503C20.4003 14.9911 21.0016 10.6428 18.8275 7.82108Z" fill="#07C160"/>
</g>
<g id="Group_4">
<path id="Vector_2" d="M8.78947 5.55441C8.97451 5.41564 9.11328 5.32312 9.29832 5.18434C9.29832 5.18434 9.29832 5.18434 9.34458 5.18434C9.52961 5.09183 9.71464 4.99931 9.85342 4.86053C9.85342 4.86053 9.89968 4.86053 9.89968 4.81428C10.2697 4.62924 10.6398 4.49047 11.0099 4.35169C11.0561 4.35169 11.0561 4.35169 11.1024 4.30543C11.2874 4.25917 11.4725 4.16666 11.6575 4.1204H11.7038C11.8888 4.07414 12.0738 4.02788 12.2589 4.02788C12.3051 4.02788 12.3051 4.02788 12.3514 4.02788C12.6289 3.98162 12.814 3.98162 12.999 3.98162C13.0453 3.98162 13.0915 3.98162 13.1378 3.98162C13.3228 3.98162 13.5541 3.93536 13.7391 3.93536C13.9704 3.93536 14.2017 3.93536 14.433 3.98162C14.4793 3.98162 14.4793 3.98162 14.5255 3.98162C14.7568 3.98162 14.9418 4.02788 15.1731 4.07414C15.2194 4.07414 15.2657 4.07414 15.2657 4.07414C15.497 4.1204 15.682 4.16666 15.867 4.21292C15.9133 4.21292 15.9133 4.21292 15.9595 4.25917C16.1908 4.30543 16.4221 4.35169 16.6072 4.44421C16.5146 4.25917 16.4221 4.1204 16.4221 4.1204C15.0806 1.99251 12.8602 0.74353 10.4548 0.74353C9.02077 0.74353 6.80036 1.25237 5.13505 3.24149C4.02485 4.58298 3.65478 6.15577 3.88608 7.72856C4.02485 8.7925 4.62621 10.2265 5.50512 11.1054C5.82893 8.83876 7.03165 6.84965 8.78947 5.55441Z" fill="#07C160"/>
</g>
<g id="Group_5">
<path id="Vector_3" d="M10.4548 15.0375C9.85343 15.0375 9.25207 14.945 8.69697 14.8525C8.65071 14.8525 8.55819 14.8525 8.46568 14.8525C8.28064 14.8525 8.14187 14.8987 8.00309 14.9912L6.15275 16.194C6.1065 16.2402 6.06024 16.2402 5.96772 16.2402C5.82894 16.2402 5.69017 16.1015 5.64391 15.9627C5.64391 15.8702 5.64391 15.8239 5.69017 15.7314C5.69017 15.6851 5.8752 14.8062 6.01398 14.2511C6.01398 14.2049 6.06024 14.1123 6.01398 14.0661C6.01398 13.881 5.92146 13.696 5.73643 13.6035C3.74731 12.262 2.40582 10.1341 2.12827 7.95996C1.61942 8.74635 1.38813 9.34771 1.11058 10.2729C0.139154 13.6498 2.26704 17.6742 5.69017 18.7844C9.6684 20.0797 13.4153 18.6457 14.7106 15.2688C14.8493 14.8525 15.0344 14.1586 15.0806 13.6035C13.7391 14.5749 12.3051 15.0375 10.4548 15.0375Z" fill="#07C160"/>
</g>
</g>
</g>
</g>
<defs>
<clipPath id="clip0_9425_7305">
<rect width="21" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.2306 7.29455C13.9169 7.25216 13.5948 7.22673 13.2641 7.22673C9.66084 7.22673 6.73584 9.67694 6.74432 12.6782C6.74432 13.0343 6.78671 13.3735 6.86301 13.7041C6.30345 13.6278 5.77779 13.4837 5.27758 13.3056L2.93757 14.3146L3.79388 12.5935C2.27627 11.593 1.30127 10.0585 1.30127 8.32042C1.30127 5.31064 4.22627 2.8689 7.82953 2.8689C11.0089 2.8689 13.6541 4.77651 14.2306 7.29455ZM10.3114 5.38107C10.2127 5.34017 10.1069 5.31911 9.99997 5.31911C9.78411 5.31911 9.57708 5.40487 9.42445 5.5575C9.27181 5.71014 9.18606 5.91717 9.18606 6.13303C9.18606 6.34889 9.27181 6.55591 9.42445 6.70855C9.57708 6.86119 9.78411 6.94694 9.99997 6.94694C10.1069 6.94694 10.2127 6.92589 10.3114 6.88499C10.4102 6.84408 10.4999 6.78413 10.5755 6.70855C10.6511 6.63297 10.711 6.54325 10.7519 6.4445C10.7928 6.34575 10.8139 6.23991 10.8139 6.13303C10.8139 6.02614 10.7928 5.92031 10.7519 5.82156C10.711 5.72281 10.6511 5.63308 10.5755 5.5575C10.4999 5.48193 10.4102 5.42197 10.3114 5.38107ZM5.0751 6.71703C5.22773 6.86967 5.43476 6.95542 5.65062 6.95542C5.86648 6.95542 6.0735 6.86967 6.22614 6.71703C6.37878 6.56439 6.46453 6.35737 6.46453 6.14151C6.46453 5.92564 6.37878 5.71862 6.22614 5.56598C6.0735 5.41334 5.86648 5.32759 5.65062 5.32759C5.43476 5.32759 5.22773 5.41334 5.0751 5.56598C4.92246 5.71862 4.83671 5.92564 4.83671 6.14151C4.83671 6.35737 4.92246 6.56439 5.0751 6.71703Z" fill="#2DBC00"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.264 8.3204C16.2653 8.3204 18.6986 10.2704 18.6986 12.6782C18.6986 14.0771 17.8677 15.298 16.596 16.095L17.6134 18.1298L14.6799 16.8665C14.2305 16.9682 13.7557 17.0361 13.264 17.0361C10.2627 17.0361 7.82944 15.0861 7.82944 12.6782C7.82944 10.2704 10.2627 8.3204 13.264 8.3204ZM11.1755 12.2698C11.3094 12.3592 11.4667 12.4069 11.6277 12.4069C12.0771 12.4069 12.4501 12.0424 12.4416 11.593C12.4416 11.432 12.3939 11.2747 12.3044 11.1408C12.215 11.007 12.0879 10.9027 11.9392 10.8411C11.7905 10.7794 11.6268 10.7633 11.4689 10.7947C11.311 10.8261 11.166 10.9037 11.0522 11.0175C10.9384 11.1313 10.8608 11.2763 10.8294 11.4342C10.798 11.5921 10.8141 11.7558 10.8757 11.9045C10.9373 12.0532 11.0417 12.1803 11.1755 12.2698ZM14.3163 12.1685C14.469 12.3212 14.676 12.4069 14.8918 12.4069C15.1077 12.4069 15.3147 12.3212 15.4674 12.1685C15.62 12.0159 15.7057 11.8089 15.7057 11.593C15.7057 11.3771 15.62 11.1701 15.4674 11.0175C15.3147 10.8648 15.1077 10.7791 14.8918 10.7791C14.676 10.7791 14.469 10.8648 14.3163 11.0175C14.1637 11.1701 14.0779 11.3771 14.0779 11.593C14.0779 11.8089 14.1637 12.0159 14.3163 12.1685Z" fill="#2DBC00"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -31,5 +31,12 @@
"edit_modal_title": "编辑企微机器人",
"create_modal_title": "创建企微机器人",
"api": "企微 API"
},
"official_account": {
"name": "微信公众号接入",
"desc": "通过 API 直接接入微信公众号",
"edit_modal_title": "编辑微信公众号接入",
"create_modal_title": "创建微信公众号接入",
"api": "微信公众号 API"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

View File

@@ -0,0 +1,29 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { plusRequest } from '@fastgpt/service/common/api/plusRequest';
export type OutLinkOffiAccountQuery = any;
export type OutLinkOffiAccountBody = any;
export type OutLinkOffiAccountResponse = {};
async function handler(
req: ApiRequestProps<OutLinkOffiAccountBody, OutLinkOffiAccountQuery>,
res: ApiResponseType<any>
): Promise<any> {
const { token, type } = req.query;
const result = await plusRequest({
url: `support/outLink/offiaccount/${token}`,
params: {
...req.query,
type
},
data: req.body
});
if (result.data?.data?.message) {
res.send(result.data.data.message);
}
res.send('');
}
export default handler;

View File

@@ -9,6 +9,8 @@ async function handler(
req: ApiRequestProps<OutLinkWecomBody, OutLinkWecomQuery>,
res: ApiResponseType<any>
): Promise<any> {
// WARN: it is not supported yet.
return {};
const { token, type } = req.query;
const result = await plusRequest({
url: `support/outLink/wecom/${token}`,

View File

@@ -0,0 +1,153 @@
import React from 'react';
import { Flex, Box, Button, ModalBody, Input, Link } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
import type { OffiAccountAppType, OutLinkEditType } from '@fastgpt/global/support/outLink/type';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';
import { createShareChat, updateShareChat } from '@/web/support/outLink/api';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import BasicInfo from '../components/BasicInfo';
import { getDocPath } from '@/web/common/system/doc';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
const OffiAccountEditModal = ({
appId,
defaultData,
onClose,
onCreate,
onEdit,
isEdit = false
}: {
appId: string;
defaultData: OutLinkEditType<OffiAccountAppType>;
onClose: () => void;
onCreate: (id: string) => void;
onEdit: () => void;
isEdit?: boolean;
}) => {
const { t } = useTranslation();
const {
register,
setValue,
handleSubmit: submitShareChat
} = useForm({
defaultValues: defaultData
});
const { runAsync: onclickCreate, loading: creating } = useRequest2(
(e) =>
createShareChat({
...e,
appId,
type: PublishChannelEnum.officialAccount
}),
{
errorToast: t('common:common.Create Failed'),
successToast: t('common:common.Create Success'),
onSuccess: onCreate
}
);
const { runAsync: onclickUpdate, loading: updating } = useRequest2((e) => updateShareChat(e), {
errorToast: t('common:common.Update Failed'),
successToast: t('common:common.Update Success'),
onSuccess: onEdit
});
const { feConfigs } = useSystemStore();
return (
<MyModal
iconSrc="/imgs/modal/shareFill.svg"
title={
isEdit
? t('publish:official_account.edit_modal_title')
: t('publish:official_account.create_modal_title')
}
minW={['auto', '60rem']}
>
<ModalBody display={'grid'} gridTemplateColumns={['1fr', '1fr 1fr']} fontSize={'14px'} p={0}>
<Box p={8} minH={['auto', '400px']} borderRight={'base'}>
<BasicInfo register={register} setValue={setValue} defaultData={defaultData} />
</Box>
<Flex p={8} minH={['auto', '400px']} flexDirection="column" gap={6}>
<Flex alignItems="center">
<Box color="myGray.600">{t('publish:official_account.api')}</Box>
{feConfigs?.docUrl && (
<Link
href={feConfigs.openAPIDocUrl || getDocPath('/docs/use-cases/official_account')}
target={'_blank'}
ml={2}
color={'primary.500'}
fontSize={'sm'}
>
<Flex alignItems={'center'}>
<MyIcon name="book" mr="1" />
{t('common:common.Read document')}
</Flex>
</Link>
)}
</Flex>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 6.25rem'} required>
App ID
</FormLabel>
<Input
placeholder="App ID"
{...register('app.appId', {
required: true
})}
/>
</Flex>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 6.25rem'} required>
Secret
</FormLabel>
<Input
placeholder="Secret"
{...register('app.secret', {
required: true
})}
/>
</Flex>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 6.25rem'} required>
Token
</FormLabel>
<Input
placeholder="Token"
{...register('app.CallbackToken', {
required: true
})}
/>
</Flex>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 6.25rem'}>AES Key</FormLabel>
<Input placeholder="AES Key" {...register('app.CallbackEncodingAesKey')} />
</Flex>
<Box flex={1}></Box>
<Flex justifyContent={'end'}>
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
{t('common:common.Close')}
</Button>
<Button
isLoading={creating || updating}
onClick={submitShareChat((data) =>
isEdit ? onclickUpdate(data) : onclickCreate(data)
)}
>
{t('common:common.Confirm')}
</Button>
</Flex>
</Flex>
</ModalBody>
</MyModal>
);
};
export default OffiAccountEditModal;

View File

@@ -0,0 +1,227 @@
import React, { useMemo, useState } from 'react';
import {
Flex,
Box,
Button,
TableContainer,
Table,
Thead,
Tr,
Th,
Td,
Tbody,
useDisclosure
} from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useLoading } from '@fastgpt/web/hooks/useLoading';
import { getShareChatList, delShareChatById } from '@/web/support/outLink/api';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { defaultOutLinkForm } from '@/web/core/app/constants';
import type { OutLinkEditType, OffiAccountAppType } from '@fastgpt/global/support/outLink/type.d';
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import dayjs from 'dayjs';
import dynamic from 'next/dynamic';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
const OffiAccountEditModal = dynamic(() => import('./OffiAccountEditModal'));
const ShowShareLinkModal = dynamic(() => import('../components/showShareLinkModal'));
const OffiAccount = ({ appId }: { appId: string }) => {
const { t } = useTranslation();
const { Loading, setIsLoading } = useLoading();
const { feConfigs } = useSystemStore();
const [editOffiAccountData, setEditOffiAccountData] =
useState<OutLinkEditType<OffiAccountAppType>>();
const [isEdit, setIsEdit] = useState<boolean>(false);
const baseUrl = useMemo(
() => feConfigs?.customApiDomain || `${location.origin}/api`,
[feConfigs?.customApiDomain]
);
const {
data: shareChatList = [],
loading: isFetching,
runAsync: refetchShareChatList
} = useRequest2(
() => getShareChatList<OffiAccountAppType>({ appId, type: PublishChannelEnum.officialAccount }),
{
manual: false
}
);
const {
onOpen: openShowShareLinkModal,
isOpen: showShareLinkModalOpen,
onClose: closeShowShareLinkModal
} = useDisclosure();
const [showShareLink, setShowShareLink] = useState<string | null>(null);
return (
<Box position={'relative'} pt={3} px={5} minH={'50vh'}>
<Flex justifyContent={'space-between'} flexDirection="row">
<Box fontWeight={'bold'} fontSize={['md', 'lg']}>
{t('publish:official_account.name')}
</Box>
<Button
variant={'primary'}
colorScheme={'blue'}
size={['sm', 'md']}
leftIcon={<MyIcon name={'common/addLight'} w="1.25rem" color="white" />}
ml={3}
{...(shareChatList.length >= 10
? {
isDisabled: true,
title: t('common:core.app.share.Amount limit tip')
}
: {})}
onClick={() => {
setEditOffiAccountData(defaultOutLinkForm as any); // HACK
setIsEdit(false);
}}
>
{t('common:add_new')}
</Button>
</Flex>
<TableContainer mt={3}>
<Table variant={'simple'} w={'100%'} overflowX={'auto'} fontSize={'sm'}>
<Thead>
<Tr>
<Th>{t('common:common.Name')} </Th>
<Th> {t('common:support.outlink.Usage points')} </Th>
{feConfigs?.isPlus && (
<>
<Th>{t('common:core.app.share.Ip limit title')} </Th>
<Th> {t('common:common.Expired Time')} </Th>
</>
)}
<Th>{t('common:common.Last use time')} </Th>
<Th> </Th>
</Tr>
</Thead>
<Tbody>
{shareChatList.map((item) => (
<Tr key={item._id}>
<Td>{item.name} </Td>
<Td>
{Math.round(item.usagePoints)}
{feConfigs?.isPlus
? `${
item.limit?.maxUsagePoints && item.limit.maxUsagePoints > -1
? ` / ${item.limit.maxUsagePoints}`
: ` / ${t('common:common.Unlimited')}`
}`
: ''}
</Td>
{feConfigs?.isPlus && (
<>
<Td>{item?.limit?.QPM || '-'} </Td>
<Td>
{item?.limit?.expiredTime
? dayjs(item.limit?.expiredTime).format('YYYY/MM/DD\nHH:mm')
: '-'}
</Td>
</>
)}
<Td>
{item.lastTime
? t(formatTimeToChatTime(item.lastTime) as any)
: t('common:common.Un used')}
</Td>
<Td display={'flex'} alignItems={'center'}>
<Button
onClick={() => {
setShowShareLink(`${baseUrl}/support/outLink/offiaccount/${item.shareId}`);
openShowShareLinkModal();
}}
size={'sm'}
mr={3}
variant={'whitePrimary'}
>
{t('publish:request_address')}
</Button>
<MyMenu
Button={
<MyIcon
name={'more'}
_hover={{ bg: 'myGray.100' }}
cursor={'pointer'}
borderRadius={'md'}
w={'14px'}
p={2}
/>
}
menuList={[
{
children: [
{
label: t('common:common.Edit'),
icon: 'edit',
onClick: () => {
setEditOffiAccountData({
_id: item._id,
name: item.name,
limit: item.limit,
app: item.app,
responseDetail: item.responseDetail,
defaultResponse: item.defaultResponse,
immediateResponse: item.immediateResponse
});
setIsEdit(true);
}
},
{
label: t('common:common.Delete'),
icon: 'delete',
onClick: async () => {
setIsLoading(true);
try {
await delShareChatById(item._id);
refetchShareChatList();
} catch (error) {
console.log(error);
}
setIsLoading(false);
}
}
]
}
]}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
{editOffiAccountData && (
<OffiAccountEditModal
appId={appId}
defaultData={editOffiAccountData}
onCreate={() => Promise.all([refetchShareChatList(), setEditOffiAccountData(undefined)])}
onEdit={() => Promise.all([refetchShareChatList(), setEditOffiAccountData(undefined)])}
onClose={() => setEditOffiAccountData(undefined)}
isEdit={isEdit}
/>
)}
{shareChatList.length === 0 && !isFetching && (
<EmptyTip text={t('common:core.app.share.Not share link')}> </EmptyTip>
)}
<Loading loading={isFetching} fixed={false} />
{showShareLinkModalOpen && (
<ShowShareLinkModal
shareLink={showShareLink ?? ''}
onClose={closeShowShareLinkModal}
img="/imgs/outlink/offiaccount-copylink-instruction.png"
/>
)}
</Box>
);
};
export default React.memo(OffiAccount);

View File

@@ -138,9 +138,7 @@ const WecomEditModal = ({
</FormLabel>
<Input
placeholder="AES Key"
{...register('app.CallbackEncodingAesKey', {
required: true
})}
{...(register('app.CallbackEncodingAesKey'), { required: true })}
/>
</Flex>

View File

@@ -16,7 +16,8 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
const Link = dynamic(() => import('./Link'));
const API = dynamic(() => import('./API'));
const FeiShu = dynamic(() => import('./FeiShu'));
const Wecom = dynamic(() => import('./Wecom'));
// const Wecom = dynamic(() => import('./Wecom'));
const OffiAccount = dynamic(() => import('./OffiAccount'));
const OutLink = () => {
const { t } = useTranslation();
@@ -47,11 +48,18 @@ const OutLink = () => {
value: PublishChannelEnum.feishu,
isProFn: true
},
// {
// icon: 'core/app/publish/wecom',
// title: t('publish:wecom.bot'),
// desc: t('publish:wecom.bot_desc'),
// value: PublishChannelEnum.wecom,
// isProFn: true
// },
{
icon: 'core/app/publish/wecom',
title: t('publish:wecom.bot'),
desc: t('publish:wecom.bot_desc'),
value: PublishChannelEnum.wecom,
icon: 'core/app/publish/offiaccount',
title: t('publish:official_account.name'),
desc: t('publish:official_account.desc'),
value: PublishChannelEnum.officialAccount,
isProFn: true
}
]);
@@ -106,7 +114,8 @@ const OutLink = () => {
)}
{linkType === PublishChannelEnum.apikey && <API appId={appId} />}
{linkType === PublishChannelEnum.feishu && <FeiShu appId={appId} />}
{linkType === PublishChannelEnum.wecom && <Wecom appId={appId} />}
{/* {linkType === PublishChannelEnum.wecom && <Wecom appId={appId} />} */}
{linkType === PublishChannelEnum.officialAccount && <OffiAccount appId={appId} />}
</Flex>
</Box>
);