返回 AiToEarn
1 import styles from './image.module.scss';
2 import { useShallow } from 'zustand/react/shallow';
3 import { useImagePageStore } from './useImagePageStore';
4 import ImageLeftSetting from './components/ImageLeftSetting/ImageLeftSetting';
5 import ImageRightSetting from './components/ImageRightSetting/ImageRightSetting';
6 import { Button, message, Modal, Popconfirm, Space } from 'antd';
7 import React, { useEffect, useMemo, useRef, useState } from 'react';
8 import usePubParamsVerify, {
9 PubParamsErrStatusEnum,
10 } from '../../hooks/usePubParamsVerify';
11 import PubAccountDetModule, {
12 IPubAccountDetModuleRef,
13 } from '../../components/PubAccountDetModule/PubAccountDetModule';
14 import { PublishProgressRes } from '../../../../../electron/main/plat/pub/PubItemVideo';
15 import PubProgressModule from '../../components/PubProgressModule/PubProgressModule';
16 import { onImgTextPublishProgress } from '@/icp/receiveMsg';
17 import {
18 icpCreateImgTextPubRecord,
19 icpCreatePubRecord,
20 icpPubImgText,
21 } from '@/icp/publish';
22 import { PubType } from '@@/publish/PublishEnum';
23 import { useCommontStore } from '@/store/commont';
24 import { useNavigate } from 'react-router-dom';
25 import { usePubStroe } from '@/store/pubStroe';
26 import { ExclamationCircleFilled } from '@ant-design/icons';
27 import { signInApi } from '@/api/signIn';
28 import { toolsApi } from '@/api/tools';
29 import { IImageAccountItem } from '@/views/publish/children/imagePage/imagePage.type';
30 import { sensitivityLoading } from '@/utils';
31
32 const { confirm } = Modal;
33
34 export default function Page() {
35 const {
36 clear,
37 images,
38 imageAccounts,
39 setActivePlat,
40 setPlatActiveAccountMap,
41 setErrParamsMap,
42 updateAccounts,
43 setTempSaveParams,
44 commonPubParams,
45 } = useImagePageStore(
46 useShallow((state) => ({
47 clear: state.clear,
48 images: state.images,
49 imageAccounts: state.imageAccounts,
50 setPlatActiveAccountMap: state.setPlatActiveAccountMap,
51 setActivePlat: state.setActivePlat,
52 setErrParamsMap: state.setErrParamsMap,
53 updateAccounts: state.updateAccounts,
54 commonPubParams: state.commonPubParams,
55 setTempSaveParams: state.setTempSaveParams,
56 })),
57 );
58 const { errParamsMap, warnParamsMap } = usePubParamsVerify(
59 imageAccounts.map((v) => {
60 return {
61 id: v.account.id,
62 account: v.account,
63 pubParams: v.pubParams,
64 } as any;
65 }),
66 {
67 moreWranVerifyCallback(item, wranParamsMapTemp, platInfo) {
68 const { imgTextConfig } = platInfo.commonPubParamsConfig;
69 if (imgTextConfig) {
70 const { imagesMax } = imgTextConfig;
71 if (images.length > imagesMax) {
72 wranParamsMapTemp.set(item.id, {
73 message: '参数警告',
74 errType: PubParamsErrStatusEnum.PARAMS,
75 parErrMsg: `${platInfo.name}的图片上传数最多不能超过${imagesMax}个,多余的图片会被系统过滤!`,
76 });
77 }
78 }
79 },
80 },
81 );
82 const pubAccountDetModuleRef = useRef<IPubAccountDetModuleRef>(null);
83 const [loading, setLoading] = useState(false);
84 const [pubProgressModuleOpen, setPubProgressModuleOpen] = useState(false);
85 // 主进程传过来的发布进度数据,key为用户id value为发布进度数据
86 const [pubProgressMap, setPubProgressMap] = useState<
87 Map<number, PublishProgressRes>
88 >(new Map());
89 const navigate = useNavigate();
90 // 敏感词检测
91 const [sensitiveDetLoading, setSensitiveDetLoading] = useState(false);
92
93 useEffect(() => {
94 setErrParamsMap(errParamsMap, warnParamsMap);
95 }, [errParamsMap, warnParamsMap]);
96
97 useEffect(() => {
98 const destroy = onImgTextPublishProgress((progressData) => {
99 setPubProgressMap((prev) => {
100 const newMap = new Map(prev);
101 newMap.set(progressData.account.id!, progressData);
102 return newMap;
103 });
104 });
105 const history = usePubStroe.getState().getImgTextPubSaveData();
106 let confirmRes: { destroy: () => void };
107
108 if (history) {
109 confirmRes = confirm({
110 title: '恢复草稿',
111 icon: <ExclamationCircleFilled />,
112 content: '您之前有未发布的图文记录,是否需要恢复?',
113 okText: '恢复',
114 cancelText: '放弃',
115 onOk() {
116 setTempSaveParams(history);
117 },
118 onCancel() {
119 usePubStroe.getState().clearImgTextPubSave();
120 },
121 });
122 }
123
124 return () => {
125 clear();
126 destroy();
127 confirmRes?.destroy();
128 };
129 }, []);
130
131 // 发布
132 const pubCore = async () => {
133 setPubProgressModuleOpen(true);
134 setLoading(true);
135 await signInApi.createSignInRecord();
136
137 const err = () => {
138 setLoading(false);
139 message.error('网络繁忙,请稍后重试!');
140 };
141 // 创建一级记录
142 const recordRes = await icpCreatePubRecord({
143 title: commonPubParams.title,
144 desc: commonPubParams.describe,
145 type: PubType.ImageText,
146 coverPath: images[0].imgPath,
147 });
148 if (!recordRes) return err();
149
150 for (const vData of imageAccounts) {
151 const account = vData.account!;
152 // 创建二级记录
153 await icpCreateImgTextPubRecord({
154 ...vData.pubParams,
155 type: account.type,
156 accountId: account.id,
157 pubRecordId: recordRes.id,
158 publishTime: new Date(),
159 desc: vData.pubParams.describe,
160 coverPath: images[0].imgPath,
161 imagesPath: images.map((v) => v.imgPath),
162 });
163 }
164 const okRes = await icpPubImgText(recordRes.id);
165 setLoading(false);
166 setPubProgressModuleOpen(false);
167 usePubStroe.getState().clearImgTextPubSave();
168 const successList = okRes.filter((v) => v.code === 1);
169 useCommontStore.getState().notification!.open({
170 message: '发布结果',
171 description: (
172 <>
173 一共发布 {okRes.length} 条数据,成功 {successList.length} 条,失败{' '}
174 {okRes.length - successList.length} 条
175 </>
176 ),
177 showProgress: true,
178 btn: (
179 <Space>
180 <Button
181 type="primary"
182 size="small"
183 onClick={() => {
184 navigate('/publish/pubRecord');
185 }}
186 >
187 查看发布记录
188 </Button>
189 </Space>
190 ),
191 key: Date.now(),
192 });
193 };
194
195 // 进度
196 const pubProgressData = useMemo(() => {
197 return imageAccounts.map((v) => {
198 const progress = pubProgressMap.get(v.account!.id);
199 return {
200 account: v.account!,
201 progress: progress?.progress || 0,
202 msg: progress?.msg || '',
203 };
204 });
205 }, [pubProgressMap, imageAccounts]);
206
207 // 一键发布点击
208 const pubClick = () => {
209 pubAccountDetModuleRef.current!.startDet();
210 setLoading(true);
211 };
212
213 const sensitiveDetCore = async (
214 content: string,
215 accountItem: IImageAccountItem,
216 ) => {
217 const res = await toolsApi.textModeration(content);
218
219 return {
220 sensitive: res !== 'Normal',
221 accountItem,
222 };
223 };
224
225 return (
226 <div className={styles.image}>
227 <PubProgressModule
228 open={pubProgressModuleOpen}
229 pubProgressData={pubProgressData as any}
230 onClose={() => setPubProgressModuleOpen(false)}
231 />
232 <PubAccountDetModule
233 isCheckProxy={true}
234 ref={pubAccountDetModuleRef}
235 accounts={imageAccounts
236 .map((v) => v.account)
237 .filter((v) => v !== undefined)}
238 onClose={() => {
239 setLoading(false);
240 }}
241 onPubClick={() => {
242 pubCore();
243 }}
244 onRestartLoginFinish={(account) => {
245 updateAccounts([account]);
246 }}
247 onDetFinish={(accounts) => {
248 updateAccounts(accounts);
249 }}
250 />
251 <div className="image-wrapper">
252 <ImageLeftSetting />
253 <ImageRightSetting />
254 </div>
255 <div className="image-footer">
256 <Popconfirm
257 title="温馨提示"
258 description="是否确认清空内容和账号?"
259 onConfirm={() => {
260 clear();
261 usePubStroe.getState().clearImgTextPubSave();
262 }}
263 okText="确认"
264 cancelText="取消"
265 >
266 <Button>一键清空</Button>
267 </Popconfirm>
268 <Button
269 loading={sensitiveDetLoading}
270 color="danger"
271 variant="solid"
272 disabled={imageAccounts.length === 0 || images.length === 0}
273 onClick={async () => {
274 setSensitiveDetLoading(true);
275 const core = async () => {
276 const tasks: Promise<{
277 // 作品
278 accountItem: IImageAccountItem;
279 // 是否敏感 true=敏感 false=正常
280 sensitive: boolean;
281 }>[] = [];
282 // 如果检测内容重复不会进行检测
283 const contentSet = new Set<string>();
284
285 imageAccounts.map((v) => {
286 const content = `
287 ${v.pubParams.title}
288 ${v.pubParams.describe}
289 `;
290 if (content.trim() !== '' && !contentSet.has(content)) {
291 contentSet.add(content);
292 tasks.push(sensitiveDetCore(content, v));
293 }
294 });
295 return await Promise.all(tasks);
296 };
297
298 const taskRes = await Promise.all([core(), sensitivityLoading()]);
299 const res = taskRes[0];
300
301 setSensitiveDetLoading(false);
302
303 if (res.length === 0) return message.success('检测正常');
304
305 if (res.every((v) => !v.sensitive)) {
306 message.success('检测正常');
307 return;
308 }
309 for (const { sensitive, accountItem } of res) {
310 if (sensitive) {
311 message.warning('检测到此条作品存在敏感信息!');
312 setActivePlat(accountItem.account.type);
313 const platActiveAccountMap = new Map(
314 useImagePageStore.getState().platActiveAccountMap,
315 );
316 platActiveAccountMap.set(accountItem.account.type, accountItem);
317 setPlatActiveAccountMap(platActiveAccountMap);
318 break;
319 }
320 }
321 }}
322 >
323 内容安全检测
324 </Button>
325 <Button
326 loading={loading}
327 type="primary"
328 disabled={imageAccounts.length === 0 || images.length === 0}
329 onClick={() => {
330 for (const [key, errVideoItem] of errParamsMap) {
331 if (errVideoItem) {
332 const imageAccount = imageAccounts.find(
333 (v) => v.account.id === +key,
334 )!;
335 const platType = imageAccount!.account.type;
336 const platActiveAccountMap = new Map(
337 useImagePageStore.getState().platActiveAccountMap,
338 );
339 setActivePlat(platType);
340 platActiveAccountMap.set(platType, imageAccount);
341 setPlatActiveAccountMap(platActiveAccountMap);
342 message.warning(errVideoItem.parErrMsg);
343 return;
344 }
345 }
346 pubClick();
347 }}
348 >
349 一键发布
350 </Button>
351 </div>
352 </div>
353 );
354 }
355
355 lines Plain Text