更新自動建群功能,調整 API 接口以支持朋友圈同步任務,優化表單結構及樣式,並整合導航組件以提升用戶體驗。更新資源引用以確保正確加載。
This commit is contained in:
18
Cunkebao/dist/.vite/manifest.json
vendored
18
Cunkebao/dist/.vite/manifest.json
vendored
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"_charts-D0fT04H8.js": {
|
||||
"file": "assets/charts-D0fT04H8.js",
|
||||
"_charts-DHgoott5.js": {
|
||||
"file": "assets/charts-DHgoott5.js",
|
||||
"name": "charts",
|
||||
"imports": [
|
||||
"_ui-qLeQLv1F.js",
|
||||
"_ui-Upu1eBzw.js",
|
||||
"_vendor-2vc8h_ct.js"
|
||||
]
|
||||
},
|
||||
@@ -11,8 +11,8 @@
|
||||
"file": "assets/ui-D0C0OGrH.css",
|
||||
"src": "_ui-D0C0OGrH.css"
|
||||
},
|
||||
"_ui-qLeQLv1F.js": {
|
||||
"file": "assets/ui-qLeQLv1F.js",
|
||||
"_ui-Upu1eBzw.js": {
|
||||
"file": "assets/ui-Upu1eBzw.js",
|
||||
"name": "ui",
|
||||
"imports": [
|
||||
"_vendor-2vc8h_ct.js"
|
||||
@@ -33,18 +33,18 @@
|
||||
"name": "vendor"
|
||||
},
|
||||
"index.html": {
|
||||
"file": "assets/index-Bos-kh2O.js",
|
||||
"file": "assets/index-Cqw-bDjj.js",
|
||||
"name": "index",
|
||||
"src": "index.html",
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"_vendor-2vc8h_ct.js",
|
||||
"_ui-qLeQLv1F.js",
|
||||
"_ui-Upu1eBzw.js",
|
||||
"_utils-6WF66_dS.js",
|
||||
"_charts-D0fT04H8.js"
|
||||
"_charts-DHgoott5.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/index-4EWIsBVv.css"
|
||||
"assets/index-Ta4vyxDJ.css"
|
||||
]
|
||||
}
|
||||
}
|
||||
8
Cunkebao/dist/index.html
vendored
8
Cunkebao/dist/index.html
vendored
@@ -11,13 +11,13 @@
|
||||
</style>
|
||||
<!-- 引入 uni-app web-view SDK(必须) -->
|
||||
<script type="text/javascript" src="/websdk.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-Bos-kh2O.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-Cqw-bDjj.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-2vc8h_ct.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/ui-qLeQLv1F.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/ui-Upu1eBzw.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/utils-6WF66_dS.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/charts-D0fT04H8.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/charts-DHgoott5.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/ui-D0C0OGrH.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-4EWIsBVv.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Ta4vyxDJ.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
Toast,
|
||||
ProgressBar,
|
||||
Tag,
|
||||
SpinLoading,
|
||||
} from "antd-mobile";
|
||||
import { Card, Button, Toast, ProgressBar, Tag } from "antd-mobile";
|
||||
import { TeamOutline, LeftOutline } from "antd-mobile-icons";
|
||||
import { AlertOutlined } from "@ant-design/icons";
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||
import NavCommon from "@/components/NavCommon/index";
|
||||
import style from "./index.module.scss";
|
||||
|
||||
interface GroupMember {
|
||||
@@ -280,37 +273,10 @@ const AutoGroupDetail: React.FC = () => {
|
||||
Toast.show({ content: "所有群组已创建完成" });
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Layout
|
||||
header={
|
||||
<div className={style.headerBar}>
|
||||
<Button fill="none" size="small" onClick={() => navigate(-1)}>
|
||||
<LeftOutline />
|
||||
</Button>
|
||||
<div className={style.title}>建群详情</div>
|
||||
</div>
|
||||
}
|
||||
footer={<MeauMobile />}
|
||||
loading={true}
|
||||
>
|
||||
<div style={{ minHeight: 300 }} />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
if (!taskDetail) {
|
||||
return (
|
||||
<Layout
|
||||
header={
|
||||
<div className={style.headerBar}>
|
||||
<Button fill="none" size="small" onClick={() => navigate(-1)}>
|
||||
<LeftOutline />
|
||||
</Button>
|
||||
<div className={style.title}>建群详情</div>
|
||||
</div>
|
||||
}
|
||||
footer={<MeauMobile />}
|
||||
header={<NavCommon title="建群详情" backFn={() => navigate(-1)} />}
|
||||
>
|
||||
<Card className={style.emptyCard}>
|
||||
<AlertOutlined style={{ fontSize: 48, color: "#ccc" }} />
|
||||
@@ -330,14 +296,12 @@ const AutoGroupDetail: React.FC = () => {
|
||||
return (
|
||||
<Layout
|
||||
header={
|
||||
<div className={style.headerBar}>
|
||||
<Button fill="none" size="small" onClick={() => navigate(-1)}>
|
||||
<LeftOutline />
|
||||
</Button>
|
||||
<div className={style.title}>{taskDetail.name} - 建群详情</div>
|
||||
</div>
|
||||
<NavCommon
|
||||
title={taskDetail.name + " - 建群详情"}
|
||||
backFn={() => navigate(-1)}
|
||||
/>
|
||||
}
|
||||
footer={<MeauMobile />}
|
||||
loading={loading}
|
||||
>
|
||||
<div className={style.autoGroupDetail}>
|
||||
<Card className={style.infoCard}>
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import request from "@/api/request";
|
||||
|
||||
// 新建自动建群任务
|
||||
export function createAutoGroup(data: any) {
|
||||
return request("/api/auto-group/create", data, "POST");
|
||||
}
|
||||
// 创建朋友圈同步任务
|
||||
export const createAutoGroup = (params: any) =>
|
||||
request("/v1/workbench/create", params, "POST");
|
||||
|
||||
// 编辑自动建群任务
|
||||
export function updateAutoGroup(id: string, data: any) {
|
||||
return request(`/api/auto-group/update/${id}`, data, "POST");
|
||||
}
|
||||
// 更新朋友圈同步任务
|
||||
export const updateAutoGroup = (params: any) =>
|
||||
request("/v1/workbench/update", params, "POST");
|
||||
|
||||
// 获取朋友圈同步任务详情
|
||||
export const getAutoGroupDetail = (id: string) =>
|
||||
request("/v1/workbench/detail", { id }, "GET");
|
||||
|
||||
// 获取朋友圈同步任务列表
|
||||
export const getAutoGroupList = (params: any) =>
|
||||
request("/v1/workbench/list", params, "GET");
|
||||
|
||||
@@ -22,13 +22,4 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.timeRangeRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.groupSizeRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,45 +1,36 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
Button,
|
||||
Toast,
|
||||
Switch,
|
||||
Selector,
|
||||
TextArea,
|
||||
NavBar,
|
||||
} from "antd-mobile";
|
||||
import { ArrowLeftOutlined } from "@ant-design/icons";
|
||||
import { Form, Toast, TextArea } from "antd-mobile";
|
||||
import { Input, InputNumber, Button, Switch } from "antd";
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
import style from "./index.module.scss";
|
||||
import { createAutoGroup, updateAutoGroup } from "./api";
|
||||
import { AutoGroupFormData } from "./types";
|
||||
import DeviceSelection from "@/components/DeviceSelection/index";
|
||||
import NavCommon from "@/components/NavCommon/index";
|
||||
import dayjs from "dayjs";
|
||||
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
|
||||
|
||||
const defaultForm = {
|
||||
const defaultForm: AutoGroupFormData = {
|
||||
name: "",
|
||||
deviceCount: 1,
|
||||
targetFriends: 0,
|
||||
createInterval: 300,
|
||||
maxGroupsPerDay: 10,
|
||||
timeRange: { start: "09:00", end: "21:00" },
|
||||
groupSize: { min: 20, max: 50 },
|
||||
targetTags: [],
|
||||
groupNameTemplate: "VIP客户交流群{序号}",
|
||||
groupDescription: "",
|
||||
type: 4,
|
||||
deveiceGroups: [], // 设备组
|
||||
deveiceGroupsOptions: [], // 设备组选项
|
||||
startTime: dayjs().format("HH:mm"), // 开始时间 (HH:mm)
|
||||
endTime: dayjs().add(1, "hour").format("HH:mm"), // 结束时间 (HH:mm)
|
||||
groupSizeMin: 20, // 群组最小人数
|
||||
groupSizeMax: 50, // 群组最大人数
|
||||
maxGroupsPerDay: 10, // 每日最大建群数
|
||||
groupNameTemplate: "VIP客户交流群{序号}", // 群名称模板
|
||||
groupDescription: "", // 群描述
|
||||
status: 1, // 是否启用 (1: 启用, 0: 禁用)
|
||||
};
|
||||
|
||||
const tagOptions = [
|
||||
{ label: "VIP客户", value: "VIP客户" },
|
||||
{ label: "高价值", value: "高价值" },
|
||||
{ label: "潜在客户", value: "潜在客户" },
|
||||
{ label: "中意向", value: "中意向" },
|
||||
];
|
||||
|
||||
const AutoGroupForm: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
const isEdit = Boolean(id);
|
||||
const [form, setForm] = useState<any>(defaultForm);
|
||||
const [form, setForm] = useState<AutoGroupFormData>(defaultForm);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -48,15 +39,15 @@ const AutoGroupForm: React.FC = () => {
|
||||
setForm({
|
||||
...defaultForm,
|
||||
name: "VIP客户建群",
|
||||
deviceCount: 2,
|
||||
targetFriends: 156,
|
||||
createInterval: 300,
|
||||
deveiceGroups: [],
|
||||
startTime: dayjs().format("HH:mm"),
|
||||
endTime: dayjs().add(1, "hour").format("HH:mm"),
|
||||
groupSizeMin: 20,
|
||||
groupSizeMax: 50,
|
||||
maxGroupsPerDay: 20,
|
||||
timeRange: { start: "09:00", end: "21:00" },
|
||||
groupSize: { min: 20, max: 50 },
|
||||
targetTags: ["VIP客户", "高价值"],
|
||||
groupNameTemplate: "VIP客户交流群{序号}",
|
||||
groupDescription: "VIP客户专属交流群,提供优质服务",
|
||||
status: 1,
|
||||
});
|
||||
}
|
||||
}, [isEdit, id]);
|
||||
@@ -65,7 +56,7 @@ const AutoGroupForm: React.FC = () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
if (isEdit) {
|
||||
await updateAutoGroup(id as string, form);
|
||||
await updateAutoGroup(form);
|
||||
Toast.show({ content: "编辑成功" });
|
||||
} else {
|
||||
await createAutoGroup(form);
|
||||
@@ -79,25 +70,24 @@ const AutoGroupForm: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const setTaskName = (val: string) => {
|
||||
setForm((f: any) => ({ ...f, name: val }));
|
||||
};
|
||||
const setDeviceGroups = (val: DeviceSelectionItem[]) => {
|
||||
console.log(val);
|
||||
setForm((f: any) => ({
|
||||
...f,
|
||||
deveiceGroups: val.map(item => item.id),
|
||||
deveiceGroupsOptions: val,
|
||||
}));
|
||||
};
|
||||
return (
|
||||
<Layout
|
||||
header={
|
||||
<NavBar
|
||||
back={null}
|
||||
style={{ background: "#fff" }}
|
||||
left={
|
||||
<div className="nav-title">
|
||||
<ArrowLeftOutlined
|
||||
twoToneColor="#1677ff"
|
||||
onClick={() => navigate(-1)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<span className="nav-title">
|
||||
{isEdit ? "编辑建群任务" : "新建建群任务"}
|
||||
</span>
|
||||
</NavBar>
|
||||
<NavCommon
|
||||
title={isEdit ? "编辑建群任务" : "新建建群任务"}
|
||||
backFn={() => navigate(-1)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className={style.autoGroupForm}>
|
||||
@@ -106,7 +96,7 @@ const AutoGroupForm: React.FC = () => {
|
||||
footer={
|
||||
<Button
|
||||
block
|
||||
color="primary"
|
||||
type="primary"
|
||||
loading={loading}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
@@ -117,115 +107,155 @@ const AutoGroupForm: React.FC = () => {
|
||||
<Form.Item label="任务名称" name="name" required>
|
||||
<Input
|
||||
value={form.name}
|
||||
onChange={val => setForm((f: any) => ({ ...f, name: val }))}
|
||||
onChange={val => setTaskName(val.target.value)}
|
||||
placeholder="请输入任务名称"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="执行设备数量" name="deviceCount" required>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.deviceCount}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({ ...f, deviceCount: Number(val) }))
|
||||
}
|
||||
placeholder="请输入设备数量"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="目标好友数" name="targetFriends" required>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.targetFriends}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({ ...f, targetFriends: Number(val) }))
|
||||
}
|
||||
placeholder="请输入目标好友数"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="建群间隔(秒)" name="createInterval" required>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.createInterval}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({ ...f, createInterval: Number(val) }))
|
||||
}
|
||||
placeholder="请输入建群间隔"
|
||||
<Form.Item label="设备组" name="deveiceGroups" required>
|
||||
<DeviceSelection
|
||||
selectedOptions={form.deveiceGroupsOptions}
|
||||
onSelect={setDeviceGroups}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="每日最大建群数" name="maxGroupsPerDay" required>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<Button
|
||||
htmlType="button"
|
||||
onClick={() => {
|
||||
const newValue = Math.max(1, (form.maxGroupsPerDay || 1) - 1);
|
||||
setForm((f: any) => ({ ...f, maxGroupsPerDay: newValue }));
|
||||
}}
|
||||
>
|
||||
-
|
||||
</Button>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={100}
|
||||
value={form.maxGroupsPerDay}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({ ...f, maxGroupsPerDay: val || 1 }))
|
||||
}
|
||||
placeholder="请输入最大建群数"
|
||||
step={1}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
htmlType="button"
|
||||
onClick={() => {
|
||||
const newValue = Math.min(
|
||||
100,
|
||||
(form.maxGroupsPerDay || 1) + 1,
|
||||
);
|
||||
setForm((f: any) => ({ ...f, maxGroupsPerDay: newValue }));
|
||||
}}
|
||||
>
|
||||
+
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item label="开始时间" name="startTime" required>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.maxGroupsPerDay}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({ ...f, maxGroupsPerDay: Number(val) }))
|
||||
}
|
||||
placeholder="请输入最大建群数"
|
||||
type="time"
|
||||
style={{ width: 120 }}
|
||||
value={form.startTime || ""}
|
||||
onChange={e => {
|
||||
setForm((f: any) => ({ ...f, startTime: e.target.value }));
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="执行时间段" name="timeRange" required>
|
||||
<div className={style.timeRangeRow}>
|
||||
<Input
|
||||
value={form.timeRange.start}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({
|
||||
...f,
|
||||
timeRange: { ...f.timeRange, start: val },
|
||||
}))
|
||||
}
|
||||
placeholder="开始时间"
|
||||
/>
|
||||
<span style={{ margin: "0 8px" }}>-</span>
|
||||
<Input
|
||||
value={form.timeRange.end}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({
|
||||
...f,
|
||||
timeRange: { ...f.timeRange, end: val },
|
||||
}))
|
||||
}
|
||||
placeholder="结束时间"
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item label="群组规模" name="groupSize" required>
|
||||
<div className={style.groupSizeRow}>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.groupSize.min}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({
|
||||
...f,
|
||||
groupSize: { ...f.groupSize, min: Number(val) },
|
||||
}))
|
||||
}
|
||||
placeholder="最小人数"
|
||||
/>
|
||||
<span style={{ margin: "0 8px" }}>-</span>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.groupSize.max}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({
|
||||
...f,
|
||||
groupSize: { ...f.groupSize, max: Number(val) },
|
||||
}))
|
||||
}
|
||||
placeholder="最大人数"
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item label="目标标签" name="targetTags">
|
||||
<Selector
|
||||
options={tagOptions}
|
||||
multiple
|
||||
value={form.targetTags}
|
||||
onChange={val => setForm((f: any) => ({ ...f, targetTags: val }))}
|
||||
<Form.Item label="结束时间" name="endTime" required>
|
||||
<Input
|
||||
type="time"
|
||||
style={{ width: 120 }}
|
||||
value={form.endTime || ""}
|
||||
onChange={e => {
|
||||
setForm((f: any) => ({ ...f, endTime: e.target.value }));
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="群组最小人数" name="groupSizeMin" required>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<Button
|
||||
htmlType="button"
|
||||
onClick={() => {
|
||||
const newValue = Math.max(1, (form.groupSizeMin || 1) - 1);
|
||||
setForm((f: any) => ({ ...f, groupSizeMin: newValue }));
|
||||
}}
|
||||
>
|
||||
-
|
||||
</Button>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={500}
|
||||
value={form.groupSizeMin}
|
||||
onChange={val => {
|
||||
const newValue = val || 1;
|
||||
setForm((f: any) => ({
|
||||
...f,
|
||||
groupSizeMin: Math.min(newValue, f.groupSizeMax),
|
||||
}));
|
||||
}}
|
||||
placeholder="请输入最小人数"
|
||||
step={1}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
htmlType="button"
|
||||
onClick={() => {
|
||||
const newValue = Math.min(500, (form.groupSizeMin || 1) + 1);
|
||||
setForm((f: any) => ({ ...f, groupSizeMin: newValue }));
|
||||
}}
|
||||
>
|
||||
+
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item label="群组最大人数" name="groupSizeMax" required>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<Button
|
||||
htmlType="button"
|
||||
onClick={() => {
|
||||
const newValue = Math.max(1, (form.groupSizeMax || 1) - 1);
|
||||
setForm((f: any) => ({ ...f, groupSizeMax: newValue }));
|
||||
}}
|
||||
>
|
||||
-
|
||||
</Button>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={500}
|
||||
value={form.groupSizeMax}
|
||||
onChange={val => {
|
||||
const newValue = val || 1;
|
||||
setForm((f: any) => ({
|
||||
...f,
|
||||
groupSizeMax: Math.max(newValue, f.groupSizeMin),
|
||||
}));
|
||||
}}
|
||||
placeholder="请输入最大人数"
|
||||
step={1}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
htmlType="button"
|
||||
onClick={() => {
|
||||
const newValue = Math.min(500, (form.groupSizeMax || 1) + 1);
|
||||
setForm((f: any) => ({ ...f, groupSizeMax: newValue }));
|
||||
}}
|
||||
>
|
||||
+
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="群名称模板" name="groupNameTemplate" required>
|
||||
<Input
|
||||
value={form.groupNameTemplate}
|
||||
onChange={val =>
|
||||
setForm((f: any) => ({ ...f, groupNameTemplate: val }))
|
||||
setForm((f: any) => ({
|
||||
...f,
|
||||
groupNameTemplate: val.target.value,
|
||||
}))
|
||||
}
|
||||
placeholder="请输入群名称模板"
|
||||
/>
|
||||
@@ -242,6 +272,23 @@ const AutoGroupForm: React.FC = () => {
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="是否开启" name="status">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<span>状态</span>
|
||||
<Switch
|
||||
checked={form.status === 1}
|
||||
onChange={checked =>
|
||||
setForm((f: any) => ({ ...f, status: checked ? 1 : 0 }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
53
Cunkebao/src/pages/mobile/workspace/auto-group/form/types.ts
Normal file
53
Cunkebao/src/pages/mobile/workspace/auto-group/form/types.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
|
||||
// 自动建群表单数据类型定义
|
||||
export interface AutoGroupFormData {
|
||||
id?: string; // 任务ID
|
||||
type: number; // 任务类型
|
||||
name: string; // 任务名称
|
||||
deveiceGroups: string[]; // 设备组
|
||||
deveiceGroupsOptions: DeviceSelectionItem[]; // 设备组选项
|
||||
startTime: string; // 开始时间 (YYYY-MM-DD HH:mm:ss)
|
||||
endTime: string; // 结束时间 (YYYY-MM-DD HH:mm:ss)
|
||||
groupSizeMin: number; // 群组最小人数
|
||||
groupSizeMax: number; // 群组最大人数
|
||||
maxGroupsPerDay: number; // 每日最大建群数
|
||||
groupNameTemplate: string; // 群名称模板
|
||||
groupDescription: string; // 群描述
|
||||
status: number; // 是否启用 (1: 启用, 0: 禁用)
|
||||
}
|
||||
|
||||
// 表单验证规则
|
||||
export const formValidationRules = {
|
||||
name: [
|
||||
{ required: true, message: "请输入任务名称" },
|
||||
{ min: 2, max: 50, message: "任务名称长度应在2-50个字符之间" },
|
||||
],
|
||||
deveiceGroups: [
|
||||
{ required: true, message: "请选择设备组" },
|
||||
{ type: "array", min: 1, message: "至少选择一个设备组" },
|
||||
],
|
||||
startTime: [{ required: true, message: "请选择开始时间" }],
|
||||
endTime: [{ required: true, message: "请选择结束时间" }],
|
||||
groupSizeMin: [
|
||||
{ required: true, message: "请输入群组最小人数" },
|
||||
{ type: "number", min: 1, max: 500, message: "群组最小人数应在1-500之间" },
|
||||
],
|
||||
groupSizeMax: [
|
||||
{ required: true, message: "请输入群组最大人数" },
|
||||
{ type: "number", min: 1, max: 500, message: "群组最大人数应在1-500之间" },
|
||||
],
|
||||
maxGroupsPerDay: [
|
||||
{ required: true, message: "请输入每日最大建群数" },
|
||||
{
|
||||
type: "number",
|
||||
min: 1,
|
||||
max: 100,
|
||||
message: "每日最大建群数应在1-100之间",
|
||||
},
|
||||
],
|
||||
groupNameTemplate: [
|
||||
{ required: true, message: "请输入群名称模板" },
|
||||
{ min: 2, max: 100, message: "群名称模板长度应在2-100个字符之间" },
|
||||
],
|
||||
groupDescription: [{ max: 200, message: "群描述不能超过200个字符" }],
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Button, Card, Popover, Toast } from "antd-mobile";
|
||||
import { Input, Switch } from "antd";
|
||||
import { Input, Switch, Pagination } from "antd";
|
||||
import {
|
||||
MoreOutline,
|
||||
AddCircleOutline,
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
} from "@ant-design/icons";
|
||||
|
||||
import { getAutoGroupList } from "../form/api";
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
import style from "./index.module.scss";
|
||||
import NavCommon from "@/components/NavCommon";
|
||||
@@ -22,91 +22,92 @@ import NavCommon from "@/components/NavCommon";
|
||||
interface GroupTask {
|
||||
id: string;
|
||||
name: string;
|
||||
status: "running" | "paused" | "completed";
|
||||
deviceCount: number;
|
||||
targetFriends: number;
|
||||
createdGroups: number;
|
||||
lastCreateTime: string;
|
||||
createTime: string;
|
||||
creator: string;
|
||||
createInterval: number;
|
||||
maxGroupsPerDay: number;
|
||||
timeRange: { start: string; end: string };
|
||||
groupSize: { min: number; max: number };
|
||||
targetTags: string[];
|
||||
groupNameTemplate: string;
|
||||
groupDescription: string;
|
||||
status: number; // 1 开启, 0 关闭
|
||||
deviceCount?: number;
|
||||
targetFriends?: number;
|
||||
createdGroups?: number;
|
||||
lastCreateTime?: string;
|
||||
createTime?: string;
|
||||
creator?: string;
|
||||
createInterval?: number;
|
||||
maxGroupsPerDay?: number;
|
||||
timeRange?: { start: string; end: string };
|
||||
groupSize?: { min: number; max: number };
|
||||
targetTags?: string[];
|
||||
groupNameTemplate?: string;
|
||||
groupDescription?: string;
|
||||
}
|
||||
|
||||
const mockTasks: GroupTask[] = [
|
||||
{
|
||||
id: "1",
|
||||
name: "VIP客户建群",
|
||||
deviceCount: 2,
|
||||
targetFriends: 156,
|
||||
createdGroups: 12,
|
||||
lastCreateTime: "2025-02-06 13:12:35",
|
||||
createTime: "2024-11-20 19:04:14",
|
||||
creator: "admin",
|
||||
status: "running",
|
||||
createInterval: 300,
|
||||
maxGroupsPerDay: 20,
|
||||
timeRange: { start: "09:00", end: "21:00" },
|
||||
groupSize: { min: 20, max: 50 },
|
||||
targetTags: ["VIP客户", "高价值"],
|
||||
groupNameTemplate: "VIP客户交流群{序号}",
|
||||
groupDescription: "VIP客户专属交流群,提供优质服务",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "产品推广建群",
|
||||
deviceCount: 1,
|
||||
targetFriends: 89,
|
||||
createdGroups: 8,
|
||||
lastCreateTime: "2024-03-04 14:09:35",
|
||||
createTime: "2024-03-04 14:29:04",
|
||||
creator: "manager",
|
||||
status: "paused",
|
||||
createInterval: 600,
|
||||
maxGroupsPerDay: 10,
|
||||
timeRange: { start: "10:00", end: "20:00" },
|
||||
groupSize: { min: 15, max: 30 },
|
||||
targetTags: ["潜在客户", "中意向"],
|
||||
groupNameTemplate: "产品推广群{序号}",
|
||||
groupDescription: "产品推广交流群,了解最新产品信息",
|
||||
},
|
||||
];
|
||||
// 初始空列表;真实数据由接口返回
|
||||
const mockTasks: GroupTask[] = [];
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
const getStatusColor = (status: number) => {
|
||||
switch (status) {
|
||||
case "running":
|
||||
case 1:
|
||||
return style.statusRunning;
|
||||
case "paused":
|
||||
case 0:
|
||||
return style.statusPaused;
|
||||
case "completed":
|
||||
return style.statusCompleted;
|
||||
default:
|
||||
return style.statusPaused;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
const getStatusText = (status: number) => {
|
||||
switch (status) {
|
||||
case "running":
|
||||
return "进行中";
|
||||
case "paused":
|
||||
return "已暂停";
|
||||
case "completed":
|
||||
return "已完成";
|
||||
case 1:
|
||||
return "开启";
|
||||
case 0:
|
||||
return "关闭";
|
||||
default:
|
||||
return "未知";
|
||||
return "关闭";
|
||||
}
|
||||
};
|
||||
|
||||
const AutoGroupList: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [tasks, setTasks] = useState<GroupTask[]>(mockTasks);
|
||||
const [tasks, setTasks] = useState<GroupTask[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [total, setTotal] = useState(0);
|
||||
|
||||
const refreshTasks = async (p = page, ps = pageSize) => {
|
||||
try {
|
||||
const res: any = await getAutoGroupList({ type: 4, page: p, limit: ps });
|
||||
// 兼容不同返回结构
|
||||
const list = res?.list || res?.records || res?.data || [];
|
||||
const totalCount = res?.total || res?.totalCount || list.length;
|
||||
const normalized: GroupTask[] = (list as any[]).map(item => ({
|
||||
id: String(item.id),
|
||||
name: item.name,
|
||||
status: Number(item.status) === 1 ? 1 : 0,
|
||||
deviceCount: Array.isArray(item.config?.devices)
|
||||
? item.config.devices.length
|
||||
: 0,
|
||||
maxGroupsPerDay: item.config?.maxGroupsPerDay ?? 0,
|
||||
timeRange: {
|
||||
start: item.config?.startTime ?? "-",
|
||||
end: item.config?.endTime ?? "-",
|
||||
},
|
||||
groupSize: {
|
||||
min: item.config?.groupSizeMin ?? 0,
|
||||
max: item.config?.groupSizeMax ?? 0,
|
||||
},
|
||||
creator: item.creatorName ?? "",
|
||||
createTime: item.createTime ?? "",
|
||||
lastCreateTime: item.updateTime ?? "",
|
||||
}));
|
||||
setTasks(normalized);
|
||||
setTotal(totalCount);
|
||||
} catch (e) {
|
||||
Toast.show({ content: "获取列表失败" });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
refreshTasks(1, pageSize);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleDelete = (taskId: string) => {
|
||||
const taskToDelete = tasks.find(task => task.id === taskId);
|
||||
@@ -144,7 +145,7 @@ const AutoGroupList: React.FC = () => {
|
||||
task.id === taskId
|
||||
? {
|
||||
...task,
|
||||
status: task.status === "running" ? "paused" : "running",
|
||||
status: task.status === 1 ? 0 : 1,
|
||||
}
|
||||
: task,
|
||||
),
|
||||
@@ -187,7 +188,7 @@ const AutoGroupList: React.FC = () => {
|
||||
</div>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => {}}
|
||||
onClick={() => refreshTasks()}
|
||||
loading={false}
|
||||
className="refresh-btn"
|
||||
>
|
||||
@@ -217,9 +218,9 @@ const AutoGroupList: React.FC = () => {
|
||||
{getStatusText(task.status)}
|
||||
</span>
|
||||
<Switch
|
||||
checked={task.status === "running"}
|
||||
checked={task.status === 1}
|
||||
onChange={() => toggleTaskStatus(task.id)}
|
||||
disabled={task.status === "completed"}
|
||||
disabled={false}
|
||||
style={{ marginLeft: 8 }}
|
||||
/>
|
||||
<Popover
|
||||
@@ -259,23 +260,26 @@ const AutoGroupList: React.FC = () => {
|
||||
<div className={style.taskInfoGrid}>
|
||||
<div>
|
||||
<div className={style.infoLabel}>执行设备</div>
|
||||
<div className={style.infoValue}>{task.deviceCount} 个</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={style.infoLabel}>目标好友</div>
|
||||
<div className={style.infoValue}>
|
||||
{task.targetFriends} 个
|
||||
{task.deviceCount ?? 0} 个
|
||||
</div>
|
||||
</div>
|
||||
{/* 该字段暂无,预留位 */}
|
||||
<div>
|
||||
<div className={style.infoLabel}>时间段</div>
|
||||
<div className={style.infoValue}>
|
||||
{task.timeRange?.start} - {task.timeRange?.end}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={style.infoLabel}>已建群</div>
|
||||
<div className={style.infoLabel}>单日上限</div>
|
||||
<div className={style.infoValue}>
|
||||
{task.createdGroups} 个
|
||||
{task.maxGroupsPerDay ?? 0} 个
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={style.infoLabel}>创建人</div>
|
||||
<div className={style.infoValue}>{task.creator}</div>
|
||||
<div className={style.infoValue}>{task.creator ?? ""}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.taskFooter}>
|
||||
@@ -291,6 +295,21 @@ const AutoGroupList: React.FC = () => {
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
{/* 分页 */}
|
||||
<div style={{ padding: 12, display: "flex", justifyContent: "center" }}>
|
||||
<Pagination
|
||||
current={page}
|
||||
pageSize={pageSize}
|
||||
total={total}
|
||||
onChange={(p, ps) => {
|
||||
setPage(p);
|
||||
setPageSize(ps);
|
||||
refreshTasks(p, ps);
|
||||
}}
|
||||
showSizeChanger
|
||||
showTotal={t => `共 ${t} 条`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user