|
|
@@ -1,5 +1,5 @@
|
|
|
-import React, { useEffect, useState } from 'react';
|
|
|
-import { Space, Table, Button, Input, Select, DatePicker, message, Typography, Spin, Popconfirm } from 'antd';
|
|
|
+import React, { useEffect, useMemo, useState } from 'react';
|
|
|
+import { Space, Table, Button, Input, Select, DatePicker, message, Typography, Spin, Popconfirm, Modal, InputNumber } from 'antd';
|
|
|
import type { TableProps } from 'antd';
|
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|
|
import copy from 'copy-to-clipboard';
|
|
|
@@ -12,7 +12,8 @@ import { useXcxPlanList, XcxPlanDataType } from './hooks/useXcxPlanList';
|
|
|
import { useAudiencePackageOptions } from './hooks/useAudiencePackageOptions';
|
|
|
import { VideoItem } from '@src/views/publishContent/weGZH/components/types';
|
|
|
import http from '@src/http';
|
|
|
-import { deleteXcxPlanApi, getShareQrLink, saveXcxPlanApi } from '@src/http/api';
|
|
|
+import { deleteXcxPlanApi, getShareQrLink, saveXcxPlanApi, xcxPlanMultiLinkApi } from '@src/http/api';
|
|
|
+import { getUserInfo } from '@src/http/sso';
|
|
|
|
|
|
const { RangePicker } = DatePicker;
|
|
|
const TableHeight = window.innerHeight - 380;
|
|
|
@@ -27,8 +28,13 @@ const XcxTouliuContent: React.FC = () => {
|
|
|
const [pageNum, setPageNum] = useState<number>(1);
|
|
|
const [pageSize, setPageSize] = useState<number>(10);
|
|
|
const [playingPlan, setPlayingPlan] = useState<XcxPlanDataType | null>(null);
|
|
|
+ const [multiLinkPlan, setMultiLinkPlan] = useState<XcxPlanDataType | null>(null);
|
|
|
+ const [multiLinkCount, setMultiLinkCount] = useState<number>(10);
|
|
|
+ const [multiLinkLoading, setMultiLinkLoading] = useState<boolean>(false);
|
|
|
const { xcxPlanList, getXcxPlanList, totalSize } = useXcxPlanList();
|
|
|
const { options: audiencePackageOptions } = useAudiencePackageOptions();
|
|
|
+ const userType = useMemo(() => getUserInfo()?.type, []);
|
|
|
+ const canMultiLink = userType === 2 || userType === 3;
|
|
|
|
|
|
const columns: TableProps<XcxPlanDataType>['columns'] = [
|
|
|
{
|
|
|
@@ -61,13 +67,6 @@ const XcxTouliuContent: React.FC = () => {
|
|
|
<img src={record.cover} referrerPolicy="no-referrer" className="w-[80px] h-auto" />
|
|
|
) : '-',
|
|
|
},
|
|
|
- {
|
|
|
- title: '备注',
|
|
|
- dataIndex: 'remark',
|
|
|
- key: 'remark',
|
|
|
- width: 180,
|
|
|
- ellipsis: true,
|
|
|
- },
|
|
|
{
|
|
|
title: '计划创建时间',
|
|
|
dataIndex: 'createTimestamp',
|
|
|
@@ -87,6 +86,9 @@ const XcxTouliuContent: React.FC = () => {
|
|
|
<Button type="link" onClick={() => downloadFile(record.shareCover || record.cover, (record.title || 'cover') + '_cover')}>下载封面</Button>
|
|
|
<Button type="link" onClick={() => showQrCodeModal(record.pageUrl)}>二维码</Button>
|
|
|
<Button type="link" onClick={() => copyToClipboard(record.pageUrl)}>复制链接</Button>
|
|
|
+ {canMultiLink && (
|
|
|
+ <Button type="link" onClick={() => openMultiLinkModal(record)}>复制多链接</Button>
|
|
|
+ )}
|
|
|
<Popconfirm
|
|
|
title="确定删除该计划吗?"
|
|
|
okText="确定"
|
|
|
@@ -140,6 +142,47 @@ const XcxTouliuContent: React.FC = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+ const openMultiLinkModal = (record: XcxPlanDataType) => {
|
|
|
+ setMultiLinkPlan(record);
|
|
|
+ setMultiLinkCount(10);
|
|
|
+ };
|
|
|
+
|
|
|
+ const closeMultiLinkModal = () => {
|
|
|
+ if (multiLinkLoading) return;
|
|
|
+ setMultiLinkPlan(null);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleMultiLink = async () => {
|
|
|
+ if (!multiLinkPlan) return;
|
|
|
+ if (!multiLinkCount || multiLinkCount < 1 || multiLinkCount > 200) {
|
|
|
+ message.warning('生成数量必须在 1-200 之间');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ setMultiLinkLoading(true);
|
|
|
+ try {
|
|
|
+ const res = await http.post<XcxPlanDataType[]>(xcxPlanMultiLinkApi, {
|
|
|
+ planId: multiLinkPlan.id,
|
|
|
+ count: multiLinkCount,
|
|
|
+ });
|
|
|
+ if (res?.code === 0 && Array.isArray(res.data) && res.data.length > 0) {
|
|
|
+ const links = res.data.map((p) => p.pageUrl).filter(Boolean).join('\n');
|
|
|
+ if (links && copy(links)) {
|
|
|
+ message.success(`已生成 ${res.data.length} 条并复制到剪贴板`);
|
|
|
+ } else {
|
|
|
+ message.warning('生成成功但复制失败,请手动到列表中获取');
|
|
|
+ }
|
|
|
+ setMultiLinkPlan(null);
|
|
|
+ refresh();
|
|
|
+ } else {
|
|
|
+ message.error(res?.msg || '生成失败');
|
|
|
+ }
|
|
|
+ } catch (err: any) {
|
|
|
+ message.error(err?.msg || '生成失败');
|
|
|
+ } finally {
|
|
|
+ setMultiLinkLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
const deletePlan = async (record: XcxPlanDataType) => {
|
|
|
setIsLoading(true);
|
|
|
const res = await http
|
|
|
@@ -166,7 +209,6 @@ const XcxTouliuContent: React.FC = () => {
|
|
|
setIsSubmiting(true);
|
|
|
const payload = {
|
|
|
audiencePackage: params.audiencePackage,
|
|
|
- remark: params.remark,
|
|
|
videoList: (params.videoList || []).map((v) => ({
|
|
|
videoId: v.videoId,
|
|
|
title: v.customTitle || v.title,
|
|
|
@@ -301,6 +343,39 @@ const XcxTouliuContent: React.FC = () => {
|
|
|
videoUrl={playingPlan?.video || ''}
|
|
|
title={playingPlan?.title || ''}
|
|
|
/>
|
|
|
+
|
|
|
+ <Modal
|
|
|
+ title="批量生成链接"
|
|
|
+ open={!!multiLinkPlan}
|
|
|
+ onCancel={closeMultiLinkModal}
|
|
|
+ onOk={handleMultiLink}
|
|
|
+ confirmLoading={multiLinkLoading}
|
|
|
+ maskClosable={false}
|
|
|
+ closable={!multiLinkLoading}
|
|
|
+ okText="生成并复制"
|
|
|
+ cancelText="取消"
|
|
|
+ destroyOnClose
|
|
|
+ >
|
|
|
+ <div className="text-gray-600 text-sm mb-3">
|
|
|
+ 将基于该计划生成 N 份相同视频/人群包的新计划,生成完成后所有链接将自动复制到剪贴板。
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
+ <span>生成数量:</span>
|
|
|
+ <InputNumber
|
|
|
+ min={1}
|
|
|
+ max={200}
|
|
|
+ value={multiLinkCount}
|
|
|
+ onChange={(v) => setMultiLinkCount(Number(v) || 1)}
|
|
|
+ disabled={multiLinkLoading}
|
|
|
+ />
|
|
|
+ <span className="text-gray-400 text-xs">(1-200)</span>
|
|
|
+ </div>
|
|
|
+ {multiLinkLoading && (
|
|
|
+ <div className="text-orange-500 text-xs mt-3">
|
|
|
+ 链接生成耗时较长,请勿关闭窗口...
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </Modal>
|
|
|
</div>
|
|
|
</Spin>
|
|
|
);
|