FEAT => 本次更新项目为:
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// 设备选择项接口
|
||||
export interface DeviceSelectionItem {
|
||||
id: number;
|
||||
name: string;
|
||||
memo: string;
|
||||
imei: string;
|
||||
wechatId: string;
|
||||
status: "online" | "offline";
|
||||
|
||||
@@ -100,7 +100,7 @@ const DeviceSelection: React.FC<DeviceSelectionProps> = ({
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
【 {device.name}】 - {device.wechatId}
|
||||
【 {device.memo}】 - {device.wechatId}
|
||||
</div>
|
||||
{!readonly && (
|
||||
<Button
|
||||
|
||||
@@ -44,7 +44,7 @@ const SelectionPopup: React.FC<SelectionPopupProps> = ({
|
||||
setDevices(
|
||||
res.list.map((d: any) => ({
|
||||
id: d.id?.toString() || "",
|
||||
name: d.memo || d.imei || "",
|
||||
memo: d.memo || d.imei || "",
|
||||
imei: d.imei || "",
|
||||
wechatId: d.wechatId || "",
|
||||
status: d.alive === 1 ? "online" : "offline",
|
||||
@@ -169,7 +169,7 @@ const SelectionPopup: React.FC<SelectionPopupProps> = ({
|
||||
/>
|
||||
<div className={style.deviceInfo}>
|
||||
<div className={style.deviceInfoRow}>
|
||||
<span className={style.deviceName}>{device.name}</span>
|
||||
<span className={style.deviceName}>{device.memo}</span>
|
||||
<div
|
||||
className={
|
||||
device.status === "online"
|
||||
|
||||
@@ -22,7 +22,7 @@ interface WechatGroup {
|
||||
interface SelectionPopupProps {
|
||||
visible: boolean;
|
||||
onVisibleChange: (visible: boolean) => void;
|
||||
selectedGroups: GroupSelectionItem[];
|
||||
selectedOptions: GroupSelectionItem[];
|
||||
onSelect: (groups: GroupSelectionItem[]) => void;
|
||||
onSelectDetail?: (groups: WechatGroup[]) => void;
|
||||
readonly?: boolean;
|
||||
@@ -35,7 +35,7 @@ interface SelectionPopupProps {
|
||||
export default function SelectionPopup({
|
||||
visible,
|
||||
onVisibleChange,
|
||||
selectedGroups,
|
||||
selectedOptions,
|
||||
onSelect,
|
||||
onSelectDetail,
|
||||
readonly = false,
|
||||
@@ -78,9 +78,9 @@ export default function SelectionPopup({
|
||||
const handleGroupToggle = (group: GroupSelectionItem) => {
|
||||
if (readonly) return;
|
||||
|
||||
const newSelectedGroups = selectedGroups.some(g => g.id === group.id)
|
||||
? selectedGroups.filter(g => g.id !== group.id)
|
||||
: selectedGroups.concat(group);
|
||||
const newSelectedGroups = selectedOptions.some(g => g.id === group.id)
|
||||
? selectedOptions.filter(g => g.id !== group.id)
|
||||
: selectedOptions.concat(group);
|
||||
|
||||
onSelect(newSelectedGroups);
|
||||
|
||||
@@ -97,8 +97,8 @@ export default function SelectionPopup({
|
||||
const handleConfirm = () => {
|
||||
if (onConfirm) {
|
||||
onConfirm(
|
||||
selectedGroups.map(g => g.id),
|
||||
selectedGroups,
|
||||
selectedOptions.map(g => g.id),
|
||||
selectedOptions,
|
||||
);
|
||||
}
|
||||
onVisibleChange(false);
|
||||
@@ -155,7 +155,7 @@ export default function SelectionPopup({
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
loading={loading}
|
||||
selectedCount={selectedGroups.length}
|
||||
selectedCount={selectedOptions.length}
|
||||
onPageChange={setCurrentPage}
|
||||
onCancel={() => onVisibleChange(false)}
|
||||
onConfirm={handleConfirm}
|
||||
@@ -172,7 +172,7 @@ export default function SelectionPopup({
|
||||
{groups.map(group => (
|
||||
<div key={group.id} className={style.groupItem}>
|
||||
<Checkbox
|
||||
checked={selectedGroups.some(g => g.id === group.id)}
|
||||
checked={selectedOptions.some(g => g.id === group.id)}
|
||||
onChange={() => !readonly && handleGroupToggle(group)}
|
||||
disabled={readonly}
|
||||
style={{ marginRight: 12 }}
|
||||
|
||||
@@ -4,7 +4,7 @@ import ContentSelection from "@/components/ContentSelection";
|
||||
import { ContentItem } from "@/components/ContentSelection/data";
|
||||
|
||||
interface ContentSelectorProps {
|
||||
selectedContent: ContentItem[];
|
||||
selectedOptions: ContentItem[];
|
||||
onPrevious: () => void;
|
||||
onNext: (data: {
|
||||
contentGroups: string[];
|
||||
@@ -18,7 +18,7 @@ export interface ContentSelectorRef {
|
||||
}
|
||||
|
||||
const ContentSelector = forwardRef<ContentSelectorRef, ContentSelectorProps>(
|
||||
({ selectedContent, onNext }, ref) => {
|
||||
({ selectedOptions, onNext }, ref) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// 暴露方法给父组件
|
||||
@@ -53,7 +53,9 @@ const ContentSelector = forwardRef<ContentSelectorRef, ContentSelectorProps>(
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{ contentGroups: selectedContent.map(c => c.id) }}
|
||||
initialValues={{
|
||||
contentGroups: selectedOptions.map(c => Number(c.id)),
|
||||
}}
|
||||
>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<h2 style={{ margin: 0, fontSize: 18, fontWeight: 600 }}>
|
||||
@@ -73,7 +75,7 @@ const ContentSelector = forwardRef<ContentSelectorRef, ContentSelectorProps>(
|
||||
]}
|
||||
>
|
||||
<ContentSelection
|
||||
selectedContent={selectedContent}
|
||||
selectedOptions={selectedOptions}
|
||||
onSelect={handleLibrariesChange}
|
||||
placeholder="选择内容库"
|
||||
showInput={true}
|
||||
|
||||
@@ -76,7 +76,7 @@ const GroupSelector = forwardRef<GroupSelectorRef, GroupSelectorProps>(
|
||||
]}
|
||||
>
|
||||
<GroupSelection
|
||||
selectedGroups={selectedGroups}
|
||||
selectedOptions={selectedGroups}
|
||||
onSelect={handleGroupSelect}
|
||||
placeholder="选择要推送的群组"
|
||||
readonly={false}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
forwardRef,
|
||||
} from "react";
|
||||
import { Form, Select, Card } from "antd";
|
||||
import request from "@/api/request";
|
||||
|
||||
// 京东社交媒体接口
|
||||
interface JdSocialMedia {
|
||||
id: string;
|
||||
name: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 京东推广站点接口
|
||||
interface JdPromotionSite {
|
||||
id: string;
|
||||
name: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface JingDongLinkProps {
|
||||
defaultValues?: {
|
||||
socialMediaId?: string;
|
||||
promotionSiteId?: string;
|
||||
};
|
||||
onNext?: (values: any) => void;
|
||||
onSave?: (values: any) => void;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export interface JingDongLinkRef {
|
||||
validate: () => Promise<boolean>;
|
||||
getValues: () => any;
|
||||
}
|
||||
|
||||
const JingDongLink = forwardRef<JingDongLinkRef, JingDongLinkProps>(
|
||||
(
|
||||
{
|
||||
defaultValues = {
|
||||
socialMediaId: undefined,
|
||||
promotionSiteId: undefined,
|
||||
},
|
||||
onNext,
|
||||
onSave,
|
||||
loading = false,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [form] = Form.useForm();
|
||||
const [socialMediaList, setSocialMediaList] = useState<JdSocialMedia[]>([]);
|
||||
const [promotionSiteList, setPromotionSiteList] = useState<
|
||||
JdPromotionSite[]
|
||||
>([]);
|
||||
const [loadingSocialMedia, setLoadingSocialMedia] = useState(false);
|
||||
const [loadingPromotionSite, setLoadingPromotionSite] = useState(false);
|
||||
|
||||
// 暴露方法给父组件
|
||||
useImperativeHandle(ref, () => ({
|
||||
validate: async () => {
|
||||
try {
|
||||
await form.validateFields();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log("JingDongLink 表单验证失败:", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
getValues: () => {
|
||||
return form.getFieldsValue();
|
||||
},
|
||||
}));
|
||||
|
||||
// 获取京东社交媒体列表
|
||||
const fetchSocialMediaList = async () => {
|
||||
setLoadingSocialMedia(true);
|
||||
try {
|
||||
const response = await request(
|
||||
"/v1/workbench/getJdSocialMedia",
|
||||
{},
|
||||
"GET",
|
||||
);
|
||||
if (response && Array.isArray(response)) {
|
||||
setSocialMediaList(response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取京东社交媒体列表失败:", error);
|
||||
} finally {
|
||||
setLoadingSocialMedia(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取京东推广站点列表
|
||||
const fetchPromotionSiteList = async (socialMediaId: string) => {
|
||||
setLoadingPromotionSite(true);
|
||||
try {
|
||||
const response = await request(
|
||||
"/v1/workbench/getJdPromotionSite",
|
||||
{ socialMediaId },
|
||||
"GET",
|
||||
);
|
||||
if (response && Array.isArray(response)) {
|
||||
setPromotionSiteList(response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取京东推广站点列表失败:", error);
|
||||
} finally {
|
||||
setLoadingPromotionSite(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时获取社交媒体列表
|
||||
useEffect(() => {
|
||||
fetchSocialMediaList();
|
||||
}, []);
|
||||
|
||||
// 监听社交媒体选择变化
|
||||
const handleSocialMediaChange = (value: string) => {
|
||||
// 清空推广站点选择
|
||||
form.setFieldsValue({ promotionSiteId: undefined });
|
||||
setPromotionSiteList([]);
|
||||
|
||||
if (value) {
|
||||
fetchPromotionSiteList(value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<Card title="京东联盟">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={defaultValues}
|
||||
onValuesChange={(changedValues, allValues) => {
|
||||
// 可以在这里处理表单值变化
|
||||
}}
|
||||
>
|
||||
{/* 京东社交媒体选择 */}
|
||||
<Form.Item label="京东联盟" style={{ marginBottom: 16 }}>
|
||||
<div style={{ display: "flex", gap: 12, alignItems: "flex-end" }}>
|
||||
<Form.Item
|
||||
name="socialMediaId"
|
||||
noStyle
|
||||
rules={[{ required: true, message: "请选择社交媒体" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择社交媒体"
|
||||
style={{ width: 200 }}
|
||||
loading={loadingSocialMedia}
|
||||
onChange={handleSocialMediaChange}
|
||||
options={socialMediaList.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="promotionSiteId"
|
||||
noStyle
|
||||
rules={[{ required: true, message: "请选择推广站点" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择推广站点"
|
||||
style={{ width: 200 }}
|
||||
loading={loadingPromotionSite}
|
||||
disabled={!form.getFieldValue("socialMediaId")}
|
||||
options={promotionSiteList.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
JingDongLink.displayName = "JingDongLink";
|
||||
|
||||
export default JingDongLink;
|
||||
@@ -30,5 +30,8 @@ export interface FormData {
|
||||
isEnabled: boolean;
|
||||
contentGroups: string[];
|
||||
wechatGroups: string[];
|
||||
// 京东联盟相关字段
|
||||
socialMediaId?: string;
|
||||
promotionSiteId?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import GroupSelector, { GroupSelectorRef } from "./components/GroupSelector";
|
||||
import ContentSelector, {
|
||||
ContentSelectorRef,
|
||||
} from "./components/ContentSelector";
|
||||
import type { ContentLibrary, FormData } from "./index.data";
|
||||
import JingDongLink, { JingDongLinkRef } from "./components/JingDongLink";
|
||||
import type { FormData } from "./index.data";
|
||||
import NavCommon from "@/components/NavCommon";
|
||||
import { GroupSelectionItem } from "@/components/GroupSelection/data";
|
||||
import { ContentItem } from "@/components/ContentSelection/data";
|
||||
@@ -50,6 +51,7 @@ const NewGroupPush: React.FC = () => {
|
||||
const basicSettingsRef = useRef<BasicSettingsRef>(null);
|
||||
const groupSelectorRef = useRef<GroupSelectorRef>(null);
|
||||
const contentSelectorRef = useRef<ContentSelectorRef>(null);
|
||||
const jingDongLinkRef = useRef<JingDongLinkRef>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
@@ -85,14 +87,18 @@ const NewGroupPush: React.FC = () => {
|
||||
window.alert("请输入任务名称");
|
||||
return;
|
||||
}
|
||||
if (formData.groups.length === 0) {
|
||||
if (formData.wechatGroups.length === 0) {
|
||||
window.alert("请选择至少一个社群");
|
||||
return;
|
||||
}
|
||||
if (formData.contentLibraries.length === 0) {
|
||||
if (formData.contentGroups.length === 0) {
|
||||
window.alert("请选择至少一个内容库");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取京东联盟数据
|
||||
const jingDongLinkValues = jingDongLinkRef.current?.getValues();
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const apiData = {
|
||||
@@ -106,8 +112,11 @@ const NewGroupPush: React.FC = () => {
|
||||
isLoopPush: formData.isLoopPush,
|
||||
isImmediatePush: formData.isImmediatePush,
|
||||
isEnabled: formData.isEnabled,
|
||||
targetGroups: formData.groups.map(g => g.name),
|
||||
contentLibraries: formData.contentLibraries.map(c => c.name),
|
||||
targetGroups: formData.wechatGroups,
|
||||
contentLibraries: formData.contentGroups,
|
||||
// 京东联盟数据
|
||||
socialMediaId: jingDongLinkValues?.socialMediaId,
|
||||
promotionSiteId: jingDongLinkValues?.promotionSiteId,
|
||||
pushMode: formData.isImmediatePush
|
||||
? ("immediate" as const)
|
||||
: ("scheduled" as const),
|
||||
@@ -239,15 +248,13 @@ const NewGroupPush: React.FC = () => {
|
||||
{currentStep === 3 && (
|
||||
<ContentSelector
|
||||
ref={contentSelectorRef}
|
||||
selectedContent={contentGroupsOptions}
|
||||
selectedOptions={contentGroupsOptions}
|
||||
onPrevious={() => setCurrentStep(2)}
|
||||
onNext={handleLibrariesChange}
|
||||
/>
|
||||
)}
|
||||
{currentStep === 4 && (
|
||||
<div style={{ padding: 32, textAlign: "center", color: "#888" }}>
|
||||
京东联盟设置(此步骤为占位,实际功能待开发)
|
||||
</div>
|
||||
<JingDongLink ref={jingDongLinkRef} loading={loading} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { MinusOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
import style from "./index.module.scss";
|
||||
import request from "@/api/request";
|
||||
|
||||
import StepIndicator from "@/components/StepIndicator";
|
||||
import {
|
||||
createMomentsSync,
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
import DeviceSelection from "@/components/DeviceSelection";
|
||||
import ContentSelection from "@/components/ContentSelection";
|
||||
import NavCommon from "@/components/NavCommon";
|
||||
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
|
||||
import { ContentItem } from "@/components/ContentSelection/data";
|
||||
|
||||
const steps = [
|
||||
{ id: 1, title: "基础设置", subtitle: "基础设置" },
|
||||
@@ -31,8 +33,8 @@ const defaultForm = {
|
||||
syncType: 1, // 1=业务号 2=人设号
|
||||
accountType: "business" as "business" | "personal", // 仅UI用
|
||||
enabled: true,
|
||||
selectedDevices: [] as string[],
|
||||
selectedLibraries: [] as any[], // 存完整内容库对象数组
|
||||
deveiceGroups: [] as any[],
|
||||
contentGroups: [] as any[], // 存完整内容库对象数组
|
||||
contentTypes: ["text", "image", "video"],
|
||||
targetTags: [] as string[],
|
||||
filterKeywords: [] as string[],
|
||||
@@ -45,6 +47,12 @@ const NewMomentsSync: React.FC = () => {
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [formData, setFormData] = useState({ ...defaultForm });
|
||||
const [deveiceGroupsOptions, setSelectedDevicesOptions] = useState<
|
||||
DeviceSelectionItem[]
|
||||
>([]);
|
||||
const [contentGroupsOptions, setContentGroupsOptions] = useState<
|
||||
ContentItem[]
|
||||
>([]);
|
||||
|
||||
// 获取详情(编辑)
|
||||
const fetchDetail = useCallback(async () => {
|
||||
@@ -62,13 +70,15 @@ const NewMomentsSync: React.FC = () => {
|
||||
syncType: res.accountType === 1 ? 1 : 2,
|
||||
accountType: res.accountType === 1 ? "business" : "personal",
|
||||
enabled: res.status === 1,
|
||||
selectedDevices: res.config?.devices || [],
|
||||
deveiceGroups: res.config?.devices || [],
|
||||
// 关键:用id字符串数组回填
|
||||
selectedLibraries: res.config?.contentLibraries || [], // 直接用对象数组
|
||||
contentGroups: res.config?.contentGroups || [], // 直接用对象数组
|
||||
contentTypes: res.config?.contentTypes || ["text", "image", "video"],
|
||||
targetTags: res.config?.targetTags || [],
|
||||
filterKeywords: res.config?.filterKeywords || [],
|
||||
});
|
||||
setSelectedDevicesOptions(res.config?.deveiceGroupsOptions || []);
|
||||
setContentGroupsOptions(res.config?.contentGroupsOptions || []);
|
||||
}
|
||||
} catch {
|
||||
message.error("获取详情失败");
|
||||
@@ -98,18 +108,25 @@ const NewMomentsSync: React.FC = () => {
|
||||
syncType: type === "business" ? 1 : 2,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleDevicesChange = (devices: DeviceSelectionItem[]) => {
|
||||
setSelectedDevicesOptions(devices);
|
||||
updateForm({ deveiceGroups: devices.map(d => d.id) });
|
||||
};
|
||||
const handleContentChange = (libs: ContentItem[]) => {
|
||||
setContentGroupsOptions(libs);
|
||||
updateForm({ contentGroups: libs });
|
||||
};
|
||||
// 提交
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.taskName.trim()) {
|
||||
message.error("请输入任务名称");
|
||||
return;
|
||||
}
|
||||
if (formData.selectedDevices.length === 0) {
|
||||
if (formData.deveiceGroups.length === 0) {
|
||||
message.error("请选择设备");
|
||||
return;
|
||||
}
|
||||
if (formData.selectedLibraries.length === 0) {
|
||||
if (formData.contentGroups.length === 0) {
|
||||
message.error("请选择内容库");
|
||||
return;
|
||||
}
|
||||
@@ -117,8 +134,8 @@ const NewMomentsSync: React.FC = () => {
|
||||
try {
|
||||
const params = {
|
||||
name: formData.taskName,
|
||||
devices: formData.selectedDevices,
|
||||
contentLibraries: formData.selectedLibraries.map((lib: any) => lib.id),
|
||||
devices: formData.deveiceGroups,
|
||||
contentLibraries: formData.contentGroups.map((lib: any) => lib.id),
|
||||
syncInterval: formData.syncInterval,
|
||||
syncCount: formData.syncCount,
|
||||
syncType: formData.syncType, // 账号类型真实传参
|
||||
@@ -243,8 +260,8 @@ const NewMomentsSync: React.FC = () => {
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>选择设备</div>
|
||||
<DeviceSelection
|
||||
selectedOptions={formData.selectedDevices}
|
||||
onSelect={devices => updateForm({ selectedDevices: devices })}
|
||||
selectedOptions={deveiceGroupsOptions}
|
||||
onSelect={handleDevicesChange}
|
||||
placeholder="请选择设备"
|
||||
showSelectedList={true}
|
||||
selectedListMaxHeight={200}
|
||||
@@ -259,15 +276,15 @@ const NewMomentsSync: React.FC = () => {
|
||||
<div className={style.formItem}>
|
||||
<div className={style.formLabel}>选择内容库</div>
|
||||
<ContentSelection
|
||||
selectedOptions={formData.selectedLibraries}
|
||||
onSelect={libs => updateForm({ selectedLibraries: libs })}
|
||||
selectedOptions={contentGroupsOptions}
|
||||
onSelect={handleContentChange}
|
||||
placeholder="请选择内容库"
|
||||
showSelectedList={true}
|
||||
selectedListMaxHeight={200}
|
||||
/>
|
||||
{formData.selectedLibraries.length > 0 && (
|
||||
{formData.contentGroups.length > 0 && (
|
||||
<div className={style.selectedTip}>
|
||||
已选内容库: {formData.selectedLibraries.length}个
|
||||
已选内容库: {formData.contentGroups.length}个
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user