mirror of
https://github.com/cs-lazy-tools/ChatGPT-On-CS.git
synced 2026-09-01 15:00:59 +08:00
feat: 添加应用的样式
This commit is contained in:
@@ -46,8 +46,6 @@ class BKServer {
|
||||
|
||||
private systemService: SystemService;
|
||||
|
||||
private ptfs: any[] = [];
|
||||
|
||||
constructor(port: number, mainWindow: BrowserWindow) {
|
||||
this.app = express();
|
||||
this.app.use(bodyParser.json());
|
||||
@@ -150,26 +148,15 @@ class BKServer {
|
||||
}),
|
||||
);
|
||||
|
||||
// 使用平台
|
||||
this.app.post(
|
||||
'/api/v1/base/platform',
|
||||
asyncHandler(async (req, res) => {
|
||||
const ids = req.body;
|
||||
console.log(`ids: ${ids}`);
|
||||
const data = await this.strategyService.updateStrategies(ids);
|
||||
res.json({ success: true, data });
|
||||
}),
|
||||
);
|
||||
|
||||
// 获取所有平台
|
||||
this.app.get(
|
||||
'/api/v1/base/platform/all',
|
||||
asyncHandler(async (req, res) => {
|
||||
if (this.ptfs.length === 0) {
|
||||
this.ptfs = await this.strategyService.getAllPlatforms();
|
||||
}
|
||||
|
||||
res.json({ success: true, data: this.ptfs });
|
||||
const data = await this.strategyService.getAllPlatforms();
|
||||
res.json({
|
||||
success: data && data.length > 0,
|
||||
data,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -199,7 +186,6 @@ class BKServer {
|
||||
is_paused: isPaused,
|
||||
is_keyword_match: isKeywordMatch,
|
||||
is_use_gpt: isUseGptReply,
|
||||
ids,
|
||||
} = req.body;
|
||||
try {
|
||||
if (isPaused) {
|
||||
@@ -213,7 +199,6 @@ class BKServer {
|
||||
}
|
||||
|
||||
this.messageService.updateKeywordMatch(isKeywordMatch, isUseGptReply);
|
||||
await this.strategyService.updateStrategies(ids);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
@@ -299,11 +284,9 @@ class BKServer {
|
||||
const { total, autoReplies } = await autoReplyController.list(query);
|
||||
|
||||
const data = autoReplies;
|
||||
if (this.ptfs.length === 0) {
|
||||
this.ptfs = await this.strategyService.getAllPlatforms();
|
||||
}
|
||||
const ptfs = await this.strategyService.getAllPlatforms();
|
||||
|
||||
const ptfMap = new Map(this.ptfs.map((ptf) => [ptf.id, ptf]));
|
||||
const ptfMap = new Map(ptfs.map((ptf) => [ptf.id, ptf]));
|
||||
const results: any[] = [];
|
||||
data.forEach((item) => {
|
||||
const ptfId = item.platform_id;
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
import { Platform } from '../types';
|
||||
|
||||
export const ALL_PLATFORMS: Platform[] = [
|
||||
{
|
||||
id: 'sannysoft',
|
||||
type: 'OTHER',
|
||||
name: 'Sannysoft',
|
||||
avatar: '',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'bilibili',
|
||||
type: 'HOT',
|
||||
name: '哔哩哔哩',
|
||||
avatar: 'https://bilibili.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'zhihu',
|
||||
type: 'ME_MEDIA',
|
||||
name: '知乎',
|
||||
avatar: 'https://www.zhihu.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'test',
|
||||
type: 'OTHER',
|
||||
name: 'Test',
|
||||
avatar: '',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'jinritemai',
|
||||
type: 'E_COMMERCE',
|
||||
name: '抖店',
|
||||
avatar:
|
||||
'https://lf3-static.bytednsdoc.com/obj/eden-cn/yvahlyj_upfbvk_zlp/ljhwZthlaukjlkulzlp/pc_creator/favicon_v2_7145ff0.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'douyin',
|
||||
type: 'ME_MEDIA',
|
||||
name: '抖音',
|
||||
avatar:
|
||||
'https://lf3-static.bytednsdoc.com/obj/eden-cn/yvahlyj_upfbvk_zlp/ljhwZthlaukjlkulzlp/pc_creator/favicon_v2_7145ff0.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'baijiahao',
|
||||
type: 'ME_MEDIA',
|
||||
name: '百家号',
|
||||
avatar: 'https://baijiahao.baidu.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'buyin_jinritemai',
|
||||
type: 'ME_MEDIA',
|
||||
name: '百应巨量',
|
||||
avatar: 'https://buyin.jinritemai.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'douyin_mp',
|
||||
type: 'ME_MEDIA',
|
||||
name: '抖音企业号',
|
||||
avatar: 'https://buyin.jinritemai.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'ctrip_imvendor',
|
||||
type: 'HOT',
|
||||
name: '携程供应商',
|
||||
avatar: 'https://ctrip.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'dianping',
|
||||
type: 'E_COMMERCE',
|
||||
name: '美团开店宝',
|
||||
avatar: 'https://awp-assets.meituan.net/nibfe/epassport-html/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'findlaw',
|
||||
type: 'LAW',
|
||||
name: '找法网',
|
||||
avatar: 'https://findlaw.cn/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'lawtime',
|
||||
type: 'LAW',
|
||||
name: '法律快车',
|
||||
avatar: 'https://lawtime.cn/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'lvlin',
|
||||
type: 'LAW',
|
||||
name: '律临',
|
||||
avatar: 'https://lvlin-base.cdn.bcebos.com/sp-saas/common/ae_logo.png',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'meituan_mtm',
|
||||
type: 'E_COMMERCE',
|
||||
name: '美团电商',
|
||||
avatar:
|
||||
'https://p0.meituan.net/publiconline/62580fcd548dba6697eb64f2769cc9b21804.png',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'meituan_my',
|
||||
type: 'E_COMMERCE',
|
||||
name: '美团买药',
|
||||
avatar: 'https://epassport.meituan.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'meituan',
|
||||
type: 'ME_MEDIA',
|
||||
name: '美团直播助手',
|
||||
avatar: 'https://live.meituan.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'oceanengine',
|
||||
type: 'E_COMMERCE',
|
||||
name: '小6客服',
|
||||
avatar: 'https://sf1-cdn-tos.toutiaostatic.com/obj/ttfe/cg/fe/im/im.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'pinduoduo',
|
||||
type: 'E_COMMERCE',
|
||||
name: '拼多多',
|
||||
avatar: 'https://mms.pinduoduo.com/login/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'toutiao',
|
||||
type: 'ME_MEDIA',
|
||||
name: '头条号',
|
||||
avatar: 'https://sf3-cdn-tos.toutiaostatic.com/obj/mp/logo-toutiao.png',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: '51job',
|
||||
type: 'RECRUIT',
|
||||
name: '前程无忧',
|
||||
avatar: 'https://ehire.51job.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: '58',
|
||||
type: 'RECRUIT',
|
||||
name: '58同城',
|
||||
avatar: 'https://passport.58.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: '66law',
|
||||
type: 'LAW',
|
||||
name: '华律',
|
||||
avatar: 'https://login.66law.cn/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: '1688',
|
||||
type: 'E_COMMERCE',
|
||||
name: '1688 找工厂',
|
||||
avatar: 'https://img.alicdn.com/tfs/TB1uh..zbj1gK0jSZFuXXcrHpXa-16-16.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: '64365',
|
||||
type: 'LAW',
|
||||
name: '律图律师',
|
||||
avatar: 'https://login.64365.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'weibo',
|
||||
type: 'HOT',
|
||||
name: '微博私信',
|
||||
avatar: 'https://conchfairy.sinajs.cn/chat/favicon.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'whatsapp',
|
||||
type: 'HOT',
|
||||
name: 'Whatsapp',
|
||||
avatar: 'https://web.whatsapp.com/img/favicon/1x/favicon.png',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'weixin_channels',
|
||||
type: 'ME_MEDIA',
|
||||
name: '微信视频号',
|
||||
avatar:
|
||||
'https://res.wx.qq.com/t/wx_fed/finder/helper/finder-helper-web/res/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'xiaohongshu_pro',
|
||||
type: 'ME_MEDIA',
|
||||
name: '小红书私信',
|
||||
avatar: 'https://www.xiaohongshu.com/favicon.ico',
|
||||
desc: '\n### 小红书平台支持\n\n发送关联链接(笔记、商品),可以使用以下规则编写回复:\n\n```\n【g-数字】:关联商品\n【n-数字】:关联笔记\n\n这个数字是商品或者笔记所在页面的排序\n```\n\n例如:【g-1】是指第一条关联商品,【n-2】是第二条关联笔记,\n\n如果要连发两条后再混着一条文案回复可以这样写: `【g-1】|【n-2】|你好`,这样就会先发送 【g-1】商品,然后发送【n-2】商品,最后发送“你好”。\n',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'xiaohongshu',
|
||||
type: 'ME_MEDIA',
|
||||
name: '小红书评论',
|
||||
avatar: 'https://www.xiaohongshu.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'zhuxiaobang',
|
||||
type: 'HOT',
|
||||
name: '住小帮',
|
||||
avatar:
|
||||
'https://lf3-static.bytednsdoc.com/obj/eden-cn/jpahaz_lm_kltvo/ljhwZthlaukjlkulzlp/homed_merchant_admin/favicon.ico',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'nowsecure',
|
||||
type: 'OTHER',
|
||||
name: 'nowsecure',
|
||||
avatar: '',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'antoinevastel',
|
||||
type: 'OTHER',
|
||||
name: 'antoinevastel',
|
||||
avatar: '',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'fingerprintjs',
|
||||
type: 'OTHER',
|
||||
name: 'fingerprintjs',
|
||||
avatar: '',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'incolumitas',
|
||||
type: 'OTHER',
|
||||
name: 'incolumitas',
|
||||
avatar: '',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'recaptcha',
|
||||
type: 'OTHER',
|
||||
name: 'recaptcha',
|
||||
avatar: '',
|
||||
desc: '',
|
||||
impl: false,
|
||||
},
|
||||
{
|
||||
id: 'win_wechat',
|
||||
type: 'HOT',
|
||||
name: '微信',
|
||||
avatar: 'https://res.wx.qq.com/a/wx_fed/assets/res/NTI4MWU5.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'win_wecom',
|
||||
type: 'HOT',
|
||||
name: '企业微信(Bate版)',
|
||||
avatar:
|
||||
'https://wwcdn.weixin.qq.com/node/wwnl/wwnl/style/images/independent/favicon/favicon_32h$a41a4426.png',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'win_qianniu',
|
||||
type: 'E_COMMERCE',
|
||||
name: '千牛',
|
||||
avatar: 'https://qianniu.1688.com/favicon.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
id: 'win_jinmai',
|
||||
type: 'E_COMMERCE',
|
||||
name: '京卖',
|
||||
avatar: 'https://shop.jd.com/jdm/favicon.ico',
|
||||
desc: '',
|
||||
impl: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import socketIo from 'socket.io';
|
||||
import { Platform, StrategyServiceStatusEnum } from '../types';
|
||||
import { ALL_PLATFORMS } from '../constants';
|
||||
|
||||
export class StrategyService {
|
||||
private io: socketIo.Server;
|
||||
|
||||
private platformInfo: Platform[];
|
||||
|
||||
constructor(io: socketIo.Server) {
|
||||
this.io = io;
|
||||
this.platformInfo = [];
|
||||
}
|
||||
|
||||
async emitAndWait<T>(
|
||||
@@ -50,17 +46,6 @@ export class StrategyService {
|
||||
return response;
|
||||
}
|
||||
|
||||
async updateStrategies(ids: string[]): Promise<any> {
|
||||
try {
|
||||
return await this.emitAndWait('strategyService-updateStrategies', {
|
||||
ids,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to update strategies', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async updateStatus(status: StrategyServiceStatusEnum): Promise<any> {
|
||||
try {
|
||||
return await this.emitAndWait('strategyService-updateStatus', { status });
|
||||
@@ -70,24 +55,28 @@ export class StrategyService {
|
||||
}
|
||||
}
|
||||
|
||||
public async getPlatformInfoById(id: string): Promise<Platform | null> {
|
||||
const platforms = await this.getAllPlatforms();
|
||||
return platforms.find((platform) => platform.id === id) || null;
|
||||
}
|
||||
|
||||
public async getAllPlatforms(): Promise<Platform[]> {
|
||||
if (this.platformInfo.length > 0) {
|
||||
return this.platformInfo;
|
||||
const maxRetries = 10;
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const data = await this.emitAndWait<Platform[]>(
|
||||
'strategyService-getAppsInfo',
|
||||
);
|
||||
return data;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
attempt++;
|
||||
console.error(`Attempt ${attempt} failed to update strategies`, error);
|
||||
if (attempt >= maxRetries) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await this.emitAndWait<Platform[]>(
|
||||
'strategyService-getAllPlatforms',
|
||||
);
|
||||
return data.length > 0 ? data : ALL_PLATFORMS;
|
||||
} catch (error) {
|
||||
console.error('Failed to update strategies', error);
|
||||
return ALL_PLATFORMS;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,9 +38,8 @@ export interface ReplyDTO {
|
||||
export interface Platform {
|
||||
id: string;
|
||||
name: string;
|
||||
impl: boolean;
|
||||
type?: string;
|
||||
urls?: string[];
|
||||
avatar?: string;
|
||||
desc?: string;
|
||||
env?: string;
|
||||
}
|
||||
|
||||
@@ -3,23 +3,42 @@ import {
|
||||
Box,
|
||||
Flex,
|
||||
Input,
|
||||
Image,
|
||||
InputGroup,
|
||||
Tooltip,
|
||||
InputRightElement,
|
||||
Badge,
|
||||
Text,
|
||||
VStack,
|
||||
HStack,
|
||||
IconButton,
|
||||
Spacer,
|
||||
Stack,
|
||||
Skeleton,
|
||||
} from '@chakra-ui/react';
|
||||
import { SearchIcon } from '@chakra-ui/icons';
|
||||
import { SearchIcon, SettingsIcon } from '@chakra-ui/icons';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import defaultPlatformIcon from '../../../../../assets/base/default-platform-icon.png';
|
||||
import windowsIcon from '../../../../../assets/base/windows.png';
|
||||
import { getPlatformList } from '../../../services/platform/controller';
|
||||
|
||||
const AppManagerComponent = () => {
|
||||
const { data, isLoading } = useQuery(['platformList'], getPlatformList);
|
||||
const high = '42vh'; // 调整为合适的全局高度
|
||||
|
||||
if (isLoading || !data || !data.data) {
|
||||
return (
|
||||
<Stack>
|
||||
<Skeleton height="20px" />
|
||||
<Skeleton height="20px" />
|
||||
<Skeleton height="20px" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex h={high}>
|
||||
<Box w="40%" bg="brand.50" display="flex" flexDirection="column">
|
||||
<Box p={4} position="sticky" top="0" zIndex="1">
|
||||
<Box p={2} position="sticky" top="0" zIndex="1">
|
||||
<Flex align="center">
|
||||
<InputGroup>
|
||||
<Input placeholder="搜索" />
|
||||
@@ -36,20 +55,74 @@ const AppManagerComponent = () => {
|
||||
</Flex>
|
||||
</Box>
|
||||
<VStack spacing={3} align="stretch" overflowY="auto" flex="1" p={4}>
|
||||
{[...Array(20)].map((_, i) => (
|
||||
<Flex key={i} bg="red.100" borderRadius="md" p={3} align="center">
|
||||
<Box w="100%" h="50px" bg="gray.200" borderRadius="md" />
|
||||
<Spacer />
|
||||
{i === 1 && (
|
||||
<Badge ml="1" colorScheme="red">
|
||||
2
|
||||
</Badge>
|
||||
)}
|
||||
{i === 2 && (
|
||||
<Badge ml="1" colorScheme="red">
|
||||
5
|
||||
</Badge>
|
||||
{[...data.data].map((app, i) => (
|
||||
<Flex
|
||||
key={i}
|
||||
bg="gray.100"
|
||||
borderRadius="md"
|
||||
p={3}
|
||||
align="center"
|
||||
position="relative"
|
||||
>
|
||||
{/* 红点标记 */}
|
||||
{10 > 0 && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top="-5px"
|
||||
right="-5px"
|
||||
bg="red.500"
|
||||
color="white"
|
||||
borderRadius="full"
|
||||
width="20px"
|
||||
height="20px"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
fontSize="12px"
|
||||
>
|
||||
{10}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Avatar 及其右上角的 windowsIcon */}
|
||||
<Box position="relative" marginRight="12px">
|
||||
<Image
|
||||
src={app.avatar || defaultPlatformIcon}
|
||||
fallbackSrc={defaultPlatformIcon}
|
||||
boxSize="25px"
|
||||
/>
|
||||
{app.env === 'desktop' && (
|
||||
<Tooltip label="客户端应用,需要先手动打开该应用">
|
||||
<Image
|
||||
src={windowsIcon}
|
||||
boxSize="15px"
|
||||
position="absolute"
|
||||
top="-5px"
|
||||
right="-5px"
|
||||
alt="windows"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<HStack align="center" flex="1">
|
||||
<Badge colorScheme="gray">{app.name}</Badge>
|
||||
</HStack>
|
||||
|
||||
{/* 设置按钮固定在右下角 */}
|
||||
<Box position="absolute" bottom="5px" right="-5px">
|
||||
<Tooltip label={`设置 ${app.name} 平台`}>
|
||||
<IconButton
|
||||
variant="borderless"
|
||||
aria-label={`设置 ${app.name} 平台`}
|
||||
fontSize="15px"
|
||||
w={4}
|
||||
h={4}
|
||||
icon={<SettingsIcon />}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</VStack>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Platform, Reply, Config, Message } from './platform';
|
||||
import { GET, POST } from '../common/api/request';
|
||||
|
||||
export async function getPlatformList() {
|
||||
console.log('getPlatformList');
|
||||
const data = await GET<{
|
||||
data: Platform[];
|
||||
}>('/api/v1/base/platform/all');
|
||||
|
||||
+1
-2
@@ -3,9 +3,8 @@ import { PlatformTypeEnum } from './constant';
|
||||
export interface Platform {
|
||||
id: string;
|
||||
name: string;
|
||||
impl: boolean;
|
||||
env: string;
|
||||
type?: PlatformTypeEnum;
|
||||
urls?: string[];
|
||||
avatar?: string;
|
||||
desc?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user