setSearchTerm(e.target.value)}
+ onChange={e => setSearchTerm(e.target.value)}
prefix={
}
allowClear
size="large"
@@ -121,7 +121,7 @@ const WechatAccounts: React.FC = () => {
- {accounts.map((account) => {
+ {accounts.map(account => {
const percent =
account.times > 0
? Math.min((account.addedCount / account.times) * 100, 100)
diff --git a/nkebao/src/pages/mobile/scenarios/list/api.ts b/nkebao/src/pages/mobile/scenarios/list/api.ts
index 0fff1662..ff0fbbcd 100644
--- a/nkebao/src/pages/mobile/scenarios/list/api.ts
+++ b/nkebao/src/pages/mobile/scenarios/list/api.ts
@@ -1,26 +1,26 @@
-import request from '@/api/request';
-
-// 获取场景列表
-export function getScenarios(params: any) {
- return request('/v1/plan/scenes', params, 'GET');
-}
-
-// 获取场景详情
-export function getScenarioDetail(id: string) {
- return request(`/v1/scenarios/${id}`, {}, 'GET');
-}
-
-// 创建场景
-export function createScenario(data: any) {
- return request('/v1/scenarios', data, 'POST');
-}
-
-// 更新场景
-export function updateScenario(id: string, data: any) {
- return request(`/v1/scenarios/${id}`, data, 'PUT');
-}
-
-// 删除场景
-export function deleteScenario(id: string) {
- return request(`/v1/scenarios/${id}`, {}, 'DELETE');
-}
+import request from "@/api/request";
+
+// 获取场景列表
+export function getScenarios(params: any) {
+ return request("/v1/plan/scenes", params, "GET");
+}
+
+// 获取场景详情
+export function getScenarioDetail(id: string) {
+ return request(`/v1/scenarios/${id}`, {}, "GET");
+}
+
+// 创建场景
+export function createScenario(data: any) {
+ return request("/v1/scenarios", data, "POST");
+}
+
+// 更新场景
+export function updateScenario(id: string, data: any) {
+ return request(`/v1/scenarios/${id}`, data, "PUT");
+}
+
+// 删除场景
+export function deleteScenario(id: string) {
+ return request(`/v1/scenarios/${id}`, {}, "DELETE");
+}
diff --git a/nkebao/src/pages/mobile/scenarios/list/index.module.scss b/nkebao/src/pages/mobile/scenarios/list/index.module.scss
index e6a3ae82..60c5c10a 100644
--- a/nkebao/src/pages/mobile/scenarios/list/index.module.scss
+++ b/nkebao/src/pages/mobile/scenarios/list/index.module.scss
@@ -22,7 +22,7 @@
background: var(--primary-gradient);
border: none;
box-shadow: 0 2px 8px var(--primary-shadow);
-
+
&:active {
transform: translateY(1px);
box-shadow: 0 1px 4px var(--primary-shadow);
@@ -132,11 +132,11 @@
.scenario-item {
cursor: pointer;
transition: all 0.2s ease;
-
+
&:hover {
transform: translateY(-1px);
}
-
+
&:active {
transform: translateY(0);
}
@@ -152,12 +152,14 @@
.scenario-card {
background: #fff;
border-radius: 16px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.06);
- transition: box-shadow 0.2s, transform 0.2s;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ transition:
+ box-shadow 0.2s,
+ transform 0.2s;
cursor: pointer;
overflow: hidden;
&:hover {
- box-shadow: 0 6px 16px rgba(0,0,0,0.12);
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
transform: translateY(-2px) scale(1.02);
}
}
@@ -258,34 +260,33 @@
// 响应式设计
@media (max-width: 480px) {
-
.scenario-card {
padding: 14px 16px;
min-height: 70px;
}
-
+
.scenario-icon {
width: 46px;
height: 46px;
}
-
+
.scenario-image {
width: 28px;
height: 28px;
}
-
+
.scenario-name {
font-size: 15px;
}
-
+
.stat-text {
font-size: 12px;
}
-
+
.scenario-growth {
font-size: 15px;
}
-
+
.growth-icon {
font-size: 13px;
}
diff --git a/nkebao/src/pages/mobile/scenarios/list/index.tsx b/nkebao/src/pages/mobile/scenarios/list/index.tsx
index 24ddffe8..eb21d3e2 100644
--- a/nkebao/src/pages/mobile/scenarios/list/index.tsx
+++ b/nkebao/src/pages/mobile/scenarios/list/index.tsx
@@ -123,7 +123,7 @@ const Scene: React.FC = () => {
>
- {scenarios.map((scenario) => (
+ {scenarios.map(scenario => (
{
src={scenario.image}
alt={scenario.name}
className={style["card-img"]}
- onError={(e) => {
+ onError={e => {
e.currentTarget.src =
"https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png";
}}
diff --git a/nkebao/src/pages/mobile/scenarios/plan/list/api.ts b/nkebao/src/pages/mobile/scenarios/plan/list/api.ts
index 4b5f6e4b..8562ee44 100644
--- a/nkebao/src/pages/mobile/scenarios/plan/list/api.ts
+++ b/nkebao/src/pages/mobile/scenarios/plan/list/api.ts
@@ -1,32 +1,32 @@
-import request from "@/api/request";
-import { PlanDetail, PlanListResponse, ApiResponse } from "./data";
-
-// ==================== 计划相关接口 ====================
-// 获取计划列表
-export function getPlanList(params: {
- sceneId: string;
- page: number;
- pageSize: number;
-}): Promise
{
- return request(`/v1/plan/list`, params, "GET");
-}
-
-// 获取计划详情
-export function getPlanDetail(planId: string): Promise {
- return request(`/v1/plan/detail`, { planId }, "GET");
-}
-
-// 复制计划
-export function copyPlan(planId: string): Promise> {
- return request(`/v1/plan/copy`, { planId }, "GET");
-}
-
-// 删除计划
-export function deletePlan(planId: string): Promise> {
- return request(`/v1/plan/delete`, { planId }, "DELETE");
-}
-
-// 获取小程序二维码
-export function getWxMinAppCode(planId: string): Promise> {
- return request(`/v1/plan/getWxMinAppCode`, { taskId: planId }, "GET");
-}
+import request from "@/api/request";
+import { PlanDetail, PlanListResponse, ApiResponse } from "./data";
+
+// ==================== 计划相关接口 ====================
+// 获取计划列表
+export function getPlanList(params: {
+ sceneId: string;
+ page: number;
+ pageSize: number;
+}): Promise {
+ return request(`/v1/plan/list`, params, "GET");
+}
+
+// 获取计划详情
+export function getPlanDetail(planId: string): Promise {
+ return request(`/v1/plan/detail`, { planId }, "GET");
+}
+
+// 复制计划
+export function copyPlan(planId: string): Promise> {
+ return request(`/v1/plan/copy`, { planId }, "GET");
+}
+
+// 删除计划
+export function deletePlan(planId: string): Promise> {
+ return request(`/v1/plan/delete`, { planId }, "DELETE");
+}
+
+// 获取小程序二维码
+export function getWxMinAppCode(planId: string): Promise> {
+ return request(`/v1/plan/getWxMinAppCode`, { taskId: planId }, "GET");
+}
diff --git a/nkebao/src/pages/mobile/scenarios/plan/list/data.ts b/nkebao/src/pages/mobile/scenarios/plan/list/data.ts
index e757f433..9c1397f8 100644
--- a/nkebao/src/pages/mobile/scenarios/plan/list/data.ts
+++ b/nkebao/src/pages/mobile/scenarios/plan/list/data.ts
@@ -1,59 +1,59 @@
-export interface Task {
- id: string;
- name: string;
- status: number;
- created_at: string;
- updated_at: string;
- enabled: boolean;
- total_customers?: number;
- today_customers?: number;
- lastUpdated?: string;
- stats?: {
- devices?: number;
- acquired?: number;
- added?: number;
- };
- reqConf?: {
- device?: string[];
- selectedDevices?: string[];
- };
- acquiredCount?: number;
- addedCount?: number;
- passRate?: number;
-}
-
-export interface ApiSettings {
- apiKey: string;
- webhookUrl: string;
- taskId: string;
-}
-
-// API响应相关类型
-export interface TextUrl {
- apiKey: string;
- originalString?: string;
- sign?: string;
- fullUrl: string;
-}
-
-export interface PlanDetail {
- id: number;
- name: string;
- scenario: number;
- enabled: boolean;
- status: number;
- apiKey: string;
- textUrl: TextUrl;
- [key: string]: any;
-}
-
-export interface ApiResponse {
- code: number;
- msg?: string;
- data: T;
-}
-
-export interface PlanListResponse {
- list: Task[];
- total: number;
-}
+export interface Task {
+ id: string;
+ name: string;
+ status: number;
+ created_at: string;
+ updated_at: string;
+ enabled: boolean;
+ total_customers?: number;
+ today_customers?: number;
+ lastUpdated?: string;
+ stats?: {
+ devices?: number;
+ acquired?: number;
+ added?: number;
+ };
+ reqConf?: {
+ device?: string[];
+ selectedDevices?: string[];
+ };
+ acquiredCount?: number;
+ addedCount?: number;
+ passRate?: number;
+}
+
+export interface ApiSettings {
+ apiKey: string;
+ webhookUrl: string;
+ taskId: string;
+}
+
+// API响应相关类型
+export interface TextUrl {
+ apiKey: string;
+ originalString?: string;
+ sign?: string;
+ fullUrl: string;
+}
+
+export interface PlanDetail {
+ id: number;
+ name: string;
+ scenario: number;
+ enabled: boolean;
+ status: number;
+ apiKey: string;
+ textUrl: TextUrl;
+ [key: string]: any;
+}
+
+export interface ApiResponse {
+ code: number;
+ msg?: string;
+ data: T;
+}
+
+export interface PlanListResponse {
+ list: Task[];
+ total: number;
+}
diff --git a/nkebao/src/pages/mobile/scenarios/plan/list/index.module.scss b/nkebao/src/pages/mobile/scenarios/plan/list/index.module.scss
index cfd64be7..ee58c81c 100644
--- a/nkebao/src/pages/mobile/scenarios/plan/list/index.module.scss
+++ b/nkebao/src/pages/mobile/scenarios/plan/list/index.module.scss
@@ -1,383 +1,382 @@
-.scenario-list-page {
- padding:0 16px;
-}
-
-.loading {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 60vh;
- gap: 16px;
-}
-
-.loading-text {
- color: #666;
- font-size: 14px;
-}
-
-.search-bar {
- display: flex;
- gap: 12px;
- align-items: center;
- padding: 16px;
-}
-
-.search-input-wrapper {
- position: relative;
- flex: 1;
-
- .ant-input {
- border-radius: 8px;
- height: 40px;
- }
-}
-
-
-.plan-list {
- display: flex;
- flex-direction: column;
- gap: 12px;
-}
-
-.plan-item {
- background: white;
- border-radius: 12px;
- padding: 16px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- transition: all 0.2s ease;
-
- &:hover {
- transform: translateY(-2px);
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
- }
-}
-
-.plan-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16px;
-}
-
-.plan-name {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- flex: 1;
- margin-right: 12px;
-}
-
-.plan-header-right {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.more-btn {
- padding: 4px;
- min-width: auto;
- height: 28px;
- width: 28px;
- border-radius: 4px;
-
- &:hover {
- background-color: #f5f5f5;
- }
-}
-
-.stats-grid {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 12px;
- margin-bottom: 16px;
-}
-
-.stat-item {
- background: #f8f9fa;
- border-radius: 8px;
- padding: 12px;
- text-align: center;
- border: 1px solid #e9ecef;
-}
-
-.stat-label {
- font-size: 12px;
- color: #666;
- margin-bottom: 4px;
- font-weight: 500;
-}
-
-.stat-value {
- font-size: 18px;
- font-weight: 600;
- color: #333;
- line-height: 1.2;
-}
-
-.plan-footer {
- border-top: 1px solid #f0f0f0;
- padding-top: 12px;
-}
-
-.last-execution {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 12px;
- color: #999;
-
- svg {
- font-size: 14px;
- color: #999;
- }
-}
-
-.empty-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 60px 20px;
- text-align: center;
-}
-
-.empty-text {
- color: #999;
- font-size: 14px;
- margin-bottom: 20px;
-}
-
-.create-first-btn {
- height: 40px;
- padding: 0 24px;
- border-radius: 20px;
-}
-
-// 加载更多按钮样式
-.load-more-container {
- display: flex;
- justify-content: center;
- padding: 20px 0;
-}
-
-.load-more-btn {
- height: 44px;
- padding: 0 32px;
- border-radius: 22px;
- font-size: 16px;
- font-weight: 500;
- display: flex;
- align-items: center;
- gap: 8px;
- transition: all 0.2s ease;
-
- &:hover {
- transform: translateY(-1px);
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
- }
-
- &:active {
- transform: translateY(0);
- }
-}
-
-// 没有更多数据提示样式
-.no-more-data {
- display: flex;
- justify-content: center;
- align-items: center;
- padding: 20px 0;
- color: #999;
- font-size: 14px;
-
- span {
- position: relative;
- padding: 0 20px;
-
- &::before,
- &::after {
- content: '';
- position: absolute;
- top: 50%;
- width: 40px;
- height: 1px;
- background-color: #e0e0e0;
- }
-
- &::before {
- left: -50px;
- }
-
- &::after {
- right: -50px;
- }
- }
-}
-
-.action-menu-dialog {
- background: white;
- border-radius: 16px 16px 0 0;
- padding: 20px;
- max-height: 60vh;
- display: flex;
- flex-direction: column;
-}
-
-.action-menu-item {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 16px;
- border-radius: 8px;
- cursor: pointer;
- transition: background-color 0.2s ease;
-
- &:hover {
- background-color: #f5f5f5;
- }
-
- &.danger {
- color: #ff4d4f;
-
- &:hover {
- background-color: #fff2f0;
- }
- }
-}
-
-.action-icon {
- font-size: 16px;
- width: 20px;
- text-align: center;
-}
-
-.action-text {
- font-size: 16px;
- font-weight: 500;
-}
-
-.dialog-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 20px;
- padding-bottom: 16px;
- border-bottom: 1px solid #f0f0f0;
-
- h3 {
- margin: 0;
- font-size: 18px;
- font-weight: 600;
- color: #333;
- }
-}
-
-.dialog-content {
- flex: 1;
- text-align: center;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-}
-
-.qr-dialog {
- background: white;
- border-radius: 16px;
- padding: 20px;
- width: 100%;
-}
-
-.qr-loading {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 40px 20px;
- gap: 16px;
- color: #666;
- font-size: 14px;
-}
-
-.qr-image {
- width: 100%;
- max-width: 200px;
- height: auto;
- border-radius: 8px;
-}
-
-.qr-error {
- text-align: center;
- color: #ff4d4f;
- font-size: 14px;
- padding: 40px 20px;
-}
-
-.qr-link-section {
- margin-top: 20px;
- width: 100%;
- padding: 0 10px;
-}
-
-.link-label {
- font-size: 14px;
- font-weight: 500;
- color: #333;
- margin-bottom: 8px;
- text-align: left;
-}
-
-.link-input-wrapper {
- display: flex;
- gap: 8px;
- align-items: center;
- width: 100%;
-
- @media (max-width: 480px) {
- flex-direction: column;
- gap: 12px;
- }
-}
-
-.link-input {
- flex: 1;
-
- .ant-input {
- border-radius: 8px;
- font-size: 12px;
- color: #666;
- background-color: #f8f9fa;
- border: 1px solid #e9ecef;
-
- &:focus {
- border-color: #1890ff;
- box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
- }
- }
-
- @media (max-width: 480px) {
- width: 100%;
- }
-}
-
-.copy-button {
- height: 32px;
- padding: 0 12px;
- border-radius: 8px;
- font-size: 12px;
- display: flex;
- align-items: center;
- gap: 4px;
- white-space: nowrap;
- flex-shrink: 0;
-
- .anticon {
- font-size: 12px;
- }
-
- @media (max-width: 480px) {
- width: 100%;
- justify-content: center;
- }
-}
\ No newline at end of file
+.scenario-list-page {
+ padding: 0 16px;
+}
+
+.loading {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 60vh;
+ gap: 16px;
+}
+
+.loading-text {
+ color: #666;
+ font-size: 14px;
+}
+
+.search-bar {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ padding: 16px;
+}
+
+.search-input-wrapper {
+ position: relative;
+ flex: 1;
+
+ .ant-input {
+ border-radius: 8px;
+ height: 40px;
+ }
+}
+
+.plan-list {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.plan-item {
+ background: white;
+ border-radius: 12px;
+ padding: 16px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ transition: all 0.2s ease;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ }
+}
+
+.plan-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+}
+
+.plan-name {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ flex: 1;
+ margin-right: 12px;
+}
+
+.plan-header-right {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.more-btn {
+ padding: 4px;
+ min-width: auto;
+ height: 28px;
+ width: 28px;
+ border-radius: 4px;
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+}
+
+.stats-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 12px;
+ margin-bottom: 16px;
+}
+
+.stat-item {
+ background: #f8f9fa;
+ border-radius: 8px;
+ padding: 12px;
+ text-align: center;
+ border: 1px solid #e9ecef;
+}
+
+.stat-label {
+ font-size: 12px;
+ color: #666;
+ margin-bottom: 4px;
+ font-weight: 500;
+}
+
+.stat-value {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ line-height: 1.2;
+}
+
+.plan-footer {
+ border-top: 1px solid #f0f0f0;
+ padding-top: 12px;
+}
+
+.last-execution {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 12px;
+ color: #999;
+
+ svg {
+ font-size: 14px;
+ color: #999;
+ }
+}
+
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 60px 20px;
+ text-align: center;
+}
+
+.empty-text {
+ color: #999;
+ font-size: 14px;
+ margin-bottom: 20px;
+}
+
+.create-first-btn {
+ height: 40px;
+ padding: 0 24px;
+ border-radius: 20px;
+}
+
+// 加载更多按钮样式
+.load-more-container {
+ display: flex;
+ justify-content: center;
+ padding: 20px 0;
+}
+
+.load-more-btn {
+ height: 44px;
+ padding: 0 32px;
+ border-radius: 22px;
+ font-size: 16px;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ transition: all 0.2s ease;
+
+ &:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+}
+
+// 没有更多数据提示样式
+.no-more-data {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 20px 0;
+ color: #999;
+ font-size: 14px;
+
+ span {
+ position: relative;
+ padding: 0 20px;
+
+ &::before,
+ &::after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ width: 40px;
+ height: 1px;
+ background-color: #e0e0e0;
+ }
+
+ &::before {
+ left: -50px;
+ }
+
+ &::after {
+ right: -50px;
+ }
+ }
+}
+
+.action-menu-dialog {
+ background: white;
+ border-radius: 16px 16px 0 0;
+ padding: 20px;
+ max-height: 60vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.action-menu-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 16px;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+
+ &.danger {
+ color: #ff4d4f;
+
+ &:hover {
+ background-color: #fff2f0;
+ }
+ }
+}
+
+.action-icon {
+ font-size: 16px;
+ width: 20px;
+ text-align: center;
+}
+
+.action-text {
+ font-size: 16px;
+ font-weight: 500;
+}
+
+.dialog-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ padding-bottom: 16px;
+ border-bottom: 1px solid #f0f0f0;
+
+ h3 {
+ margin: 0;
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+}
+
+.dialog-content {
+ flex: 1;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.qr-dialog {
+ background: white;
+ border-radius: 16px;
+ padding: 20px;
+ width: 100%;
+}
+
+.qr-loading {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 40px 20px;
+ gap: 16px;
+ color: #666;
+ font-size: 14px;
+}
+
+.qr-image {
+ width: 100%;
+ max-width: 200px;
+ height: auto;
+ border-radius: 8px;
+}
+
+.qr-error {
+ text-align: center;
+ color: #ff4d4f;
+ font-size: 14px;
+ padding: 40px 20px;
+}
+
+.qr-link-section {
+ margin-top: 20px;
+ width: 100%;
+ padding: 0 10px;
+}
+
+.link-label {
+ font-size: 14px;
+ font-weight: 500;
+ color: #333;
+ margin-bottom: 8px;
+ text-align: left;
+}
+
+.link-input-wrapper {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ width: 100%;
+
+ @media (max-width: 480px) {
+ flex-direction: column;
+ gap: 12px;
+ }
+}
+
+.link-input {
+ flex: 1;
+
+ .ant-input {
+ border-radius: 8px;
+ font-size: 12px;
+ color: #666;
+ background-color: #f8f9fa;
+ border: 1px solid #e9ecef;
+
+ &:focus {
+ border-color: #1890ff;
+ box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
+ }
+ }
+
+ @media (max-width: 480px) {
+ width: 100%;
+ }
+}
+
+.copy-button {
+ height: 32px;
+ padding: 0 12px;
+ border-radius: 8px;
+ font-size: 12px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ white-space: nowrap;
+ flex-shrink: 0;
+
+ .anticon {
+ font-size: 12px;
+ }
+
+ @media (max-width: 480px) {
+ width: 100%;
+ justify-content: center;
+ }
+}
diff --git a/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx b/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx
index 552f7347..68db5ee5 100644
--- a/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx
+++ b/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx
@@ -104,7 +104,7 @@ const ScenarioList: React.FC = () => {
if (response && response.list) {
if (isLoadMore) {
// 加载更多时,追加数据
- setTasks((prev) => [...prev, ...response.list]);
+ setTasks(prev => [...prev, ...response.list]);
} else {
// 首次加载或刷新时,替换数据
setTasks(response.list);
@@ -158,7 +158,7 @@ const ScenarioList: React.FC = () => {
};
const handleCopyPlan = async (taskId: string) => {
- const taskToCopy = tasks.find((task) => task.id === taskId);
+ const taskToCopy = tasks.find(task => task.id === taskId);
if (!taskToCopy) return;
try {
@@ -178,7 +178,7 @@ const ScenarioList: React.FC = () => {
};
const handleDeletePlan = async (taskId: string) => {
- const taskToDelete = tasks.find((task) => task.id === taskId);
+ const taskToDelete = tasks.find(task => task.id === taskId);
if (!taskToDelete) return;
const result = await Dialog.confirm({
@@ -285,7 +285,7 @@ const ScenarioList: React.FC = () => {
await fetchPlanList(1, false);
};
- const filteredTasks = tasks.filter((task) =>
+ const filteredTasks = tasks.filter(task =>
task.name.toLowerCase().includes(searchTerm.toLowerCase())
);
@@ -371,7 +371,7 @@ const ScenarioList: React.FC = () => {
setSearchTerm(e.target.value)}
+ onChange={e => setSearchTerm(e.target.value)}
prefix={}
allowClear
size="large"
@@ -408,7 +408,7 @@ const ScenarioList: React.FC = () => {
) : (
<>
- {filteredTasks.map((task) => (
+ {filteredTasks.map(task => (
{/* 头部:标题、状态和操作菜单 */}
@@ -527,8 +527,8 @@ const ScenarioList: React.FC = () => {
{showActionMenu &&
- getActionMenu(tasks.find((t) => t.id === showActionMenu)!).map(
- (item) => (
+ getActionMenu(tasks.find(t => t.id === showActionMenu)!).map(
+ item => (
setShowApiDialog(false)}
- * apiKey={apiSettings.apiKey}
- * webhookUrl={apiSettings.webhookUrl}
- * taskId={apiSettings.taskId}
- * />
- * ```
- *
- * 特性:
- * - 移动端使用 Popup,PC端使用 Modal
- * - 支持四个标签页:接口配置、快速测试、开发文档、代码示例
- * - 支持多种编程语言的代码示例
- * - 响应式设计,自适应不同屏幕尺寸
- * - 支持暗色主题
- * - 自动拼接API地址前缀
- */
-
-interface PlanApiProps {
- visible: boolean;
- onClose: () => void;
- apiKey: string;
- webhookUrl: string;
- taskId: string;
-}
-
-interface ApiSettings {
- apiKey: string;
- webhookUrl: string;
- taskId: string;
-}
-
-const PlanApi: React.FC
= ({
- visible,
- onClose,
- apiKey,
- webhookUrl,
- taskId,
-}) => {
- const [activeTab, setActiveTab] = useState("config");
- const [activeLanguage, setActiveLanguage] = useState("javascript");
-
- // 处理webhook URL,确保包含完整的API地址
- const fullWebhookUrl = useMemo(() => {
- return buildApiUrl(webhookUrl);
- }, [webhookUrl]);
-
- // 生成测试URL
- const testUrl = useMemo(() => {
- if (!fullWebhookUrl) return "";
- return `${fullWebhookUrl}?name=测试客户&phone=13800138000&source=API测试`;
- }, [fullWebhookUrl]);
-
- // 检测是否为移动端
- const isMobile = window.innerWidth <= 768;
-
- const handleCopy = (text: string, type: string) => {
- navigator.clipboard.writeText(text);
- Toast.show({
- content: `${type}已复制到剪贴板`,
- position: "top",
- });
- };
-
- const handleTestInBrowser = () => {
- window.open(testUrl, "_blank");
- };
-
- const renderConfigTab = () => (
-
- {/* API密钥配置 */}
-
-
-
-
-
-
-
- 安全提示:
- 请妥善保管API密钥,不要在客户端代码中暴露。建议在服务器端使用该密钥。
-
-
-
- {/* 接口地址配置 */}
-
-
-
-
-
-
-
- {/* 参数说明 */}
-
-
-
必要参数
-
-
- name - 客户姓名
-
-
- phone - 手机号码
-
-
-
-
-
可选参数
-
-
- source - 来源标识
-
-
- remark - 备注信息
-
-
- tags - 客户标签
-
-
-
-
-
-
- );
-
- const renderQuickTestTab = () => (
-
-
-
快速测试URL
-
-
-
-
-
-
-
-
-
- );
-
- const renderDocsTab = () => (
-
-
-
-
-
-
- 完整API文档
- 详细的接口说明和参数文档
-
-
-
-
-
- 集成指南
- 第三方平台集成教程
-
-
-
- );
-
- const renderCodeTab = () => {
- const codeExamples = {
- javascript: `fetch('${fullWebhookUrl}', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': 'Bearer ${apiKey}'
- },
- body: JSON.stringify({
- name: '张三',
- phone: '13800138000',
- source: '官网表单',
- })
-})`,
- python: `import requests
-
-url = '${fullWebhookUrl}'
-headers = {
- 'Content-Type': 'application/json',
- 'Authorization': 'Bearer ${apiKey}'
-}
-data = {
- 'name': '张三',
- 'phone': '13800138000',
- 'source': '官网表单'
-}
-
-response = requests.post(url, json=data, headers=headers)`,
- php: ` '张三',
- 'phone' => '13800138000',
- 'source' => '官网表单'
-);
-
-$options = array(
- 'http' => array(
- 'header' => "Content-type: application/json\\r\\nAuthorization: Bearer ${apiKey}\\r\\n",
- 'method' => 'POST',
- 'content' => json_encode($data)
- )
-);
-
-$context = stream_context_create($options);
-$result = file_get_contents($url, false, $context);`,
- java: `import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.net.URI;
-
-HttpClient client = HttpClient.newHttpClient();
-String json = "{\\"name\\":\\"张三\\",\\"phone\\":\\"13800138000\\",\\"source\\":\\"官网表单\\"}";
-
-HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create("${fullWebhookUrl}"))
- .header("Content-Type", "application/json")
- .header("Authorization", "Bearer ${apiKey}")
- .POST(HttpRequest.BodyPublishers.ofString(json))
- .build();
-
-HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());`,
- };
-
- return (
-
-
- {Object.keys(codeExamples).map((lang) => (
-
- ))}
-
-
-
-
- {codeExamples[activeLanguage as keyof typeof codeExamples]}
-
-
-
-
-
- );
- };
-
- const renderContent = () => (
-
- {/* 头部 */}
-
-
-
-
-
计划接口配置
-
- 通过API接口直接导入客资到该获客计划,支持多种编程语言和第三方平台集成
-
-
-
-
-
-
- {/* 导航标签 */}
-
-
-
-
-
-
-
- {/* 内容区域 */}
-
- {activeTab === "config" && renderConfigTab()}
- {activeTab === "test" && renderQuickTestTab()}
- {activeTab === "docs" && renderDocsTab()}
- {activeTab === "code" && renderCodeTab()}
-
-
- {/* 底部 */}
-
-
-
- 所有数据传输均采用HTTPS加密
-
-
-
-
- );
-
- // 移动端使用Popup
- if (isMobile) {
- return (
-
- {renderContent()}
-
- );
- }
-
- // PC端使用Modal
- return (
-
- {renderContent()}
-
- );
-};
-
-export default PlanApi;
+import React, { useState, useMemo } from "react";
+import { Popup, Button, Toast, SpinLoading } from "antd-mobile";
+import { Modal, Input, Tabs, Card, Tag, Space } from "antd";
+import {
+ CopyOutlined,
+ CodeOutlined,
+ BookOutlined,
+ ThunderboltOutlined,
+ SettingOutlined,
+ LinkOutlined,
+ SafetyOutlined,
+ CheckCircleOutlined,
+} from "@ant-design/icons";
+import style from "./planApi.module.scss";
+import { buildApiUrl } from "@/utils/apiUrl";
+
+/**
+ * 计划接口配置弹窗组件
+ *
+ * 使用示例:
+ * ```tsx
+ * const [showApiDialog, setShowApiDialog] = useState(false);
+ * const [apiSettings, setApiSettings] = useState({
+ * apiKey: "your-api-key",
+ * webhookUrl: "https://api.example.com/webhook",
+ * taskId: "task-123"
+ * });
+ *
+ * setShowApiDialog(false)}
+ * apiKey={apiSettings.apiKey}
+ * webhookUrl={apiSettings.webhookUrl}
+ * taskId={apiSettings.taskId}
+ * />
+ * ```
+ *
+ * 特性:
+ * - 移动端使用 Popup,PC端使用 Modal
+ * - 支持四个标签页:接口配置、快速测试、开发文档、代码示例
+ * - 支持多种编程语言的代码示例
+ * - 响应式设计,自适应不同屏幕尺寸
+ * - 支持暗色主题
+ * - 自动拼接API地址前缀
+ */
+
+interface PlanApiProps {
+ visible: boolean;
+ onClose: () => void;
+ apiKey: string;
+ webhookUrl: string;
+ taskId: string;
+}
+
+interface ApiSettings {
+ apiKey: string;
+ webhookUrl: string;
+ taskId: string;
+}
+
+const PlanApi: React.FC = ({
+ visible,
+ onClose,
+ apiKey,
+ webhookUrl,
+ taskId,
+}) => {
+ const [activeTab, setActiveTab] = useState("config");
+ const [activeLanguage, setActiveLanguage] = useState("javascript");
+
+ // 处理webhook URL,确保包含完整的API地址
+ const fullWebhookUrl = useMemo(() => {
+ return buildApiUrl(webhookUrl);
+ }, [webhookUrl]);
+
+ // 生成测试URL
+ const testUrl = useMemo(() => {
+ if (!fullWebhookUrl) return "";
+ return `${fullWebhookUrl}?name=测试客户&phone=13800138000&source=API测试`;
+ }, [fullWebhookUrl]);
+
+ // 检测是否为移动端
+ const isMobile = window.innerWidth <= 768;
+
+ const handleCopy = (text: string, type: string) => {
+ navigator.clipboard.writeText(text);
+ Toast.show({
+ content: `${type}已复制到剪贴板`,
+ position: "top",
+ });
+ };
+
+ const handleTestInBrowser = () => {
+ window.open(testUrl, "_blank");
+ };
+
+ const renderConfigTab = () => (
+
+ {/* API密钥配置 */}
+
+
+
+
+
+
+
+ 安全提示:
+ 请妥善保管API密钥,不要在客户端代码中暴露。建议在服务器端使用该密钥。
+
+
+
+ {/* 接口地址配置 */}
+
+
+
+
+
+
+
+ {/* 参数说明 */}
+
+
+
必要参数
+
+
+ name - 客户姓名
+
+
+ phone - 手机号码
+
+
+
+
+
可选参数
+
+
+ source - 来源标识
+
+
+ remark - 备注信息
+
+
+ tags - 客户标签
+
+
+
+
+
+
+ );
+
+ const renderQuickTestTab = () => (
+
+
+
快速测试URL
+
+
+
+
+
+
+
+
+
+ );
+
+ const renderDocsTab = () => (
+
+
+
+
+
+
+ 完整API文档
+ 详细的接口说明和参数文档
+
+
+
+
+
+ 集成指南
+ 第三方平台集成教程
+
+
+
+ );
+
+ const renderCodeTab = () => {
+ const codeExamples = {
+ javascript: `fetch('${fullWebhookUrl}', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer ${apiKey}'
+ },
+ body: JSON.stringify({
+ name: '张三',
+ phone: '13800138000',
+ source: '官网表单',
+ })
+})`,
+ python: `import requests
+
+url = '${fullWebhookUrl}'
+headers = {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer ${apiKey}'
+}
+data = {
+ 'name': '张三',
+ 'phone': '13800138000',
+ 'source': '官网表单'
+}
+
+response = requests.post(url, json=data, headers=headers)`,
+ php: ` '张三',
+ 'phone' => '13800138000',
+ 'source' => '官网表单'
+);
+
+$options = array(
+ 'http' => array(
+ 'header' => "Content-type: application/json\\r\\nAuthorization: Bearer ${apiKey}\\r\\n",
+ 'method' => 'POST',
+ 'content' => json_encode($data)
+ )
+);
+
+$context = stream_context_create($options);
+$result = file_get_contents($url, false, $context);`,
+ java: `import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.URI;
+
+HttpClient client = HttpClient.newHttpClient();
+String json = "{\\"name\\":\\"张三\\",\\"phone\\":\\"13800138000\\",\\"source\\":\\"官网表单\\"}";
+
+HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("${fullWebhookUrl}"))
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer ${apiKey}")
+ .POST(HttpRequest.BodyPublishers.ofString(json))
+ .build();
+
+HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());`,
+ };
+
+ return (
+
+
+ {Object.keys(codeExamples).map(lang => (
+
+ ))}
+
+
+
+
+ {codeExamples[activeLanguage as keyof typeof codeExamples]}
+
+
+
+
+
+ );
+ };
+
+ const renderContent = () => (
+
+ {/* 头部 */}
+
+
+
+
+
计划接口配置
+
+ 通过API接口直接导入客资到该获客计划,支持多种编程语言和第三方平台集成
+
+
+
+
+
+
+ {/* 导航标签 */}
+
+
+
+
+
+
+
+ {/* 内容区域 */}
+
+ {activeTab === "config" && renderConfigTab()}
+ {activeTab === "test" && renderQuickTestTab()}
+ {activeTab === "docs" && renderDocsTab()}
+ {activeTab === "code" && renderCodeTab()}
+
+
+ {/* 底部 */}
+
+
+
+ 所有数据传输均采用HTTPS加密
+
+
+
+
+ );
+
+ // 移动端使用Popup
+ if (isMobile) {
+ return (
+
+ {renderContent()}
+
+ );
+ }
+
+ // PC端使用Modal
+ return (
+
+ {renderContent()}
+
+ );
+};
+
+export default PlanApi;
diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/index.api.ts b/nkebao/src/pages/mobile/scenarios/plan/new/index.api.ts
index 0eb6c231..2a79e0e8 100644
--- a/nkebao/src/pages/mobile/scenarios/plan/new/index.api.ts
+++ b/nkebao/src/pages/mobile/scenarios/plan/new/index.api.ts
@@ -1,53 +1,53 @@
-import request from "@/api/request";
-// 获取场景类型列表
-export function getScenarioTypes() {
- return request("/v1/plan/scenes", undefined, "GET");
-}
-
-// 创建计划
-export function createPlan(data: any) {
- return request("/v1/plan/create", data, "POST");
-}
-
-// 更新计划
-export function updatePlan(planId: string, data: any) {
- return request(`/v1/scenarios/plans/${planId}`, data, "PUT");
-}
-
-// 获取计划详情
-export function getPlanDetail(planId: string) {
- return request(`/v1/scenarios/plans/${planId}`, undefined, "GET");
-}
-
-// PlanDetail 类型定义(可根据实际接口返回结构补充字段)
-export interface PlanDetail {
- name: string;
- scenario: number;
- posters: any[];
- device: string[];
- remarkType: string;
- greeting: string;
- addInterval: number;
- startTime: string;
- endTime: string;
- enabled: boolean;
- sceneId: string | number;
- remarkFormat: string;
- addFriendInterval: number;
- // 其它字段可扩展
- [key: string]: any;
-}
-
-// 兼容旧代码的接口命名
-export function getPlanScenes() {
- return getScenarioTypes();
-}
-export function createScenarioPlan(data: any) {
- return createPlan(data);
-}
-export function fetchPlanDetail(planId: string) {
- return getPlanDetail(planId);
-}
-export function updateScenarioPlan(planId: string, data: any) {
- return updatePlan(planId, data);
-}
+import request from "@/api/request";
+// 获取场景类型列表
+export function getScenarioTypes() {
+ return request("/v1/plan/scenes", undefined, "GET");
+}
+
+// 创建计划
+export function createPlan(data: any) {
+ return request("/v1/plan/create", data, "POST");
+}
+
+// 更新计划
+export function updatePlan(planId: string, data: any) {
+ return request(`/v1/scenarios/plans/${planId}`, data, "PUT");
+}
+
+// 获取计划详情
+export function getPlanDetail(planId: string) {
+ return request(`/v1/scenarios/plans/${planId}`, undefined, "GET");
+}
+
+// PlanDetail 类型定义(可根据实际接口返回结构补充字段)
+export interface PlanDetail {
+ name: string;
+ scenario: number;
+ posters: any[];
+ device: string[];
+ remarkType: string;
+ greeting: string;
+ addInterval: number;
+ startTime: string;
+ endTime: string;
+ enabled: boolean;
+ sceneId: string | number;
+ remarkFormat: string;
+ addFriendInterval: number;
+ // 其它字段可扩展
+ [key: string]: any;
+}
+
+// 兼容旧代码的接口命名
+export function getPlanScenes() {
+ return getScenarioTypes();
+}
+export function createScenarioPlan(data: any) {
+ return createPlan(data);
+}
+export function fetchPlanDetail(planId: string) {
+ return getPlanDetail(planId);
+}
+export function updateScenarioPlan(planId: string, data: any) {
+ return updatePlan(planId, data);
+}
diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx
index dc1362d0..013b0218 100644
--- a/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx
+++ b/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx
@@ -71,10 +71,10 @@ export default function NewPlan() {
setSceneLoading(true);
//获取场景类型
getScenarioTypes()
- .then((data) => {
+ .then(data => {
setSceneList(data || []);
})
- .catch((err) => {
+ .catch(err => {
message.error(err.message || "获取场景类型失败");
})
.finally(() => setSceneLoading(false));
@@ -83,7 +83,7 @@ export default function NewPlan() {
//获取计划详情
const detail = await getPlanDetail(planId);
- setFormData((prev) => ({
+ setFormData(prev => ({
...prev,
name: detail.name ?? "",
scenario: Number(detail.scenario) || 1,
@@ -102,7 +102,7 @@ export default function NewPlan() {
}));
} else {
if (scenarioId) {
- setFormData((prev) => ({
+ setFormData(prev => ({
...prev,
...{ scenario: Number(scenarioId) || 1 },
}));
@@ -112,7 +112,7 @@ export default function NewPlan() {
// 更新表单数据
const onChange = (data: any) => {
- setFormData((prev) => ({ ...prev, ...data }));
+ setFormData(prev => ({ ...prev, ...data }));
};
// 处理保存
@@ -136,7 +136,7 @@ export default function NewPlan() {
result = await createPlan(formData);
}
message.success(isEdit ? "计划已更新" : "获客计划已创建");
- const sceneItem = sceneList.find((v) => formData.scenario === v.id);
+ const sceneItem = sceneList.find(v => formData.scenario === v.id);
router(`/scenarios/list/${formData.scenario}/${sceneItem.name}`);
} catch (error) {
message.error(
@@ -156,13 +156,13 @@ export default function NewPlan() {
if (currentStep === steps.length) {
handleSave();
} else {
- setCurrentStep((prev) => prev + 1);
+ setCurrentStep(prev => prev + 1);
}
};
// 上一步
const handlePrev = () => {
- setCurrentStep((prev) => Math.max(prev - 1, 1));
+ setCurrentStep(prev => Math.max(prev - 1, 1));
};
// 渲染当前步骤内容
diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx
index e2d0ba35..40431a5e 100644
--- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx
+++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx
@@ -82,7 +82,7 @@ const generateRandomAccounts = (count: number): Account[] => {
};
const generatePosterMaterials = (): Material[] => {
- return posterTemplates.map((template) => ({
+ return posterTemplates.map(template => ({
id: template.id,
name: template.name,
type: "poster",
@@ -190,7 +190,7 @@ const BasicSettings: React.FC = ({
useEffect(() => {
const today = new Date().toLocaleDateString("zh-CN").replace(/\//g, "");
- const sceneItem = sceneList.find((v) => formData.scenario === v.id);
+ const sceneItem = sceneList.find(v => formData.scenario === v.id);
onChange({ ...formData, name: `${sceneItem?.name || "海报"}${today}` });
}, [isEdit]);
@@ -251,15 +251,15 @@ const BasicSettings: React.FC = ({
type: "poster",
preview: urls[0],
};
- setCustomPosters((prev) => [...prev, newPoster]);
+ setCustomPosters(prev => [...prev, newPoster]);
}
};
// 新增:删除自定义海报
const handleRemoveCustomPoster = (id: string) => {
- setCustomPosters((prev) => prev.filter((p) => p.id !== id));
+ setCustomPosters(prev => prev.filter(p => p.id !== id));
// 如果选中则取消选中
- if (selectedMaterials.some((m) => m.id === id)) {
+ if (selectedMaterials.some(m => m.id === id)) {
setSelectedMaterials([]);
onChange({ ...formData, materials: [] });
}
@@ -267,7 +267,7 @@ const BasicSettings: React.FC = ({
// 修改:选中/取消选中海报
const handleMaterialSelect = (material: Material) => {
- const isSelected = selectedMaterials.some((m) => m.id === material.id);
+ const isSelected = selectedMaterials.some(m => m.id === material.id);
if (isSelected) {
setSelectedMaterials([]);
onChange({ ...formData, materials: [] });
@@ -318,11 +318,11 @@ const BasicSettings: React.FC = ({
const file = event.target.files?.[0];
if (file) {
const reader = new FileReader();
- reader.onload = (e) => {
+ reader.onload = e => {
try {
const content = e.target?.result as string;
- const rows = content.split("\n").filter((row) => row.trim());
- const tags = rows.slice(1).map((row) => {
+ const rows = content.split("\n").filter(row => row.trim());
+ const tags = rows.slice(1).map(row => {
const [phone, wechat, source, orderAmount, orderDate] =
row.split(",");
return {
@@ -405,7 +405,7 @@ const BasicSettings: React.FC = ({
};
// 当前选中的场景对象
- const currentScene = sceneList.find((s) => s.id === formData.scenario);
+ const currentScene = sceneList.find(s => s.id === formData.scenario);
//打开订单
const openOrder =
formData.scenario !== 2 ? { display: "none" } : { display: "block" };
@@ -430,7 +430,7 @@ const BasicSettings: React.FC = ({
) : (
- {sceneList.map((scene) => {
+ {sceneList.map(scene => {
const selected = formData.scenario === scene.id;
return (
+ }
+ footer={
}
+ >
+
+
+
+
+
基本信息
+
任务名称:{taskDetail.name}
+
+ 创建时间:{taskDetail.createTime}
+
+
创建人:{taskDetail.creator}
+
+ 执行设备:{taskDetail.deviceCount} 个
+
+
+
+
建群配置
+
+ 群组规模:{taskDetail.groupSize.min}-{taskDetail.groupSize.max}{" "}
+ 人
+
+
+ 执行时间:{taskDetail.timeRange.start} -{" "}
+ {taskDetail.timeRange.end}
+
+
+ 目标标签:{taskDetail.targetTags.join(", ")}
+
+
+ 群名称模板:{taskDetail.groupNameTemplate}
+
+
+
+
+
+
+
+ );
+};
+
+export default AutoGroupDetail;
diff --git a/nkebao/src/pages/mobile/workspace/auto-group/form/api.ts b/nkebao/src/pages/mobile/workspace/auto-group/form/api.ts
index 4ab01f32..811d1e56 100644
--- a/nkebao/src/pages/mobile/workspace/auto-group/form/api.ts
+++ b/nkebao/src/pages/mobile/workspace/auto-group/form/api.ts
@@ -1,11 +1,11 @@
-import request from "@/api/request";
-
-// 新建自动建群任务
-export function createAutoGroup(data: any) {
- return request("/api/auto-group/create", data, "POST");
-}
-
-// 编辑自动建群任务
-export function updateAutoGroup(id: string, data: any) {
- return request(`/api/auto-group/update/${id}`, data, "POST");
-}
+import request from "@/api/request";
+
+// 新建自动建群任务
+export function createAutoGroup(data: any) {
+ return request("/api/auto-group/create", data, "POST");
+}
+
+// 编辑自动建群任务
+export function updateAutoGroup(id: string, data: any) {
+ return request(`/api/auto-group/update/${id}`, data, "POST");
+}
diff --git a/nkebao/src/pages/mobile/workspace/auto-group/form/index.module.scss b/nkebao/src/pages/mobile/workspace/auto-group/form/index.module.scss
index 20bf7f92..6a7bd15a 100644
--- a/nkebao/src/pages/mobile/workspace/auto-group/form/index.module.scss
+++ b/nkebao/src/pages/mobile/workspace/auto-group/form/index.module.scss
@@ -1,34 +1,34 @@
-.autoGroupForm {
- padding: 10px;
- background: #f7f8fa;
-}
-
-.headerBar {
- display: flex;
- align-items: center;
- height: 48px;
- background: #fff;
- border-bottom: 1px solid #f0f0f0;
- font-size: 18px;
- font-weight: 600;
- padding: 0 16px;
-}
-
-.title {
- font-size: 18px;
- font-weight: 600;
- color: #222;
- flex: 1;
- text-align: center;
-}
-
-.timeRangeRow {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-.groupSizeRow {
- display: flex;
- align-items: center;
- gap: 8px;
-}
\ No newline at end of file
+.autoGroupForm {
+ padding: 10px;
+ background: #f7f8fa;
+}
+
+.headerBar {
+ display: flex;
+ align-items: center;
+ height: 48px;
+ background: #fff;
+ border-bottom: 1px solid #f0f0f0;
+ font-size: 18px;
+ font-weight: 600;
+ padding: 0 16px;
+}
+
+.title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #222;
+ flex: 1;
+ text-align: center;
+}
+
+.timeRangeRow {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.groupSizeRow {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
diff --git a/nkebao/src/pages/mobile/workspace/auto-group/form/index.tsx b/nkebao/src/pages/mobile/workspace/auto-group/form/index.tsx
index fd00746f..1e7dc78d 100644
--- a/nkebao/src/pages/mobile/workspace/auto-group/form/index.tsx
+++ b/nkebao/src/pages/mobile/workspace/auto-group/form/index.tsx
@@ -1,253 +1,251 @@
-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 Layout from "@/components/Layout/Layout";
-import style from "./index.module.scss";
-import { createAutoGroup, updateAutoGroup } from "./api";
-
-const defaultForm = {
- 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: "",
-};
-
-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
(defaultForm);
- const [loading, setLoading] = useState(false);
-
- useEffect(() => {
- if (isEdit) {
- // 这里应请求详情接口,回填表单,演示用mock
- setForm({
- ...defaultForm,
- name: "VIP客户建群",
- deviceCount: 2,
- targetFriends: 156,
- createInterval: 300,
- maxGroupsPerDay: 20,
- timeRange: { start: "09:00", end: "21:00" },
- groupSize: { min: 20, max: 50 },
- targetTags: ["VIP客户", "高价值"],
- groupNameTemplate: "VIP客户交流群{序号}",
- groupDescription: "VIP客户专属交流群,提供优质服务",
- });
- }
- }, [isEdit, id]);
-
- const handleSubmit = async () => {
- setLoading(true);
- try {
- if (isEdit) {
- await updateAutoGroup(id as string, form);
- Toast.show({ content: "编辑成功" });
- } else {
- await createAutoGroup(form);
- Toast.show({ content: "创建成功" });
- }
- navigate("/workspace/auto-group");
- } catch (e) {
- Toast.show({ content: "提交失败" });
- } finally {
- setLoading(false);
- }
- };
-
- return (
-
- navigate(-1)}
- />
-
- }
- >
-
- {isEdit ? "编辑建群任务" : "新建建群任务"}
-
-
- }
- >
-
-
- setForm((f: any) => ({ ...f, name: val }))}
- placeholder="请输入任务名称"
- />
-
-
-
- setForm((f: any) => ({ ...f, deviceCount: Number(val) }))
- }
- placeholder="请输入设备数量"
- />
-
-
-
- setForm((f: any) => ({ ...f, targetFriends: Number(val) }))
- }
- placeholder="请输入目标好友数"
- />
-
-
-
- setForm((f: any) => ({ ...f, createInterval: Number(val) }))
- }
- placeholder="请输入建群间隔"
- />
-
-
-
- setForm((f: any) => ({ ...f, maxGroupsPerDay: Number(val) }))
- }
- placeholder="请输入最大建群数"
- />
-
-
-
-
- setForm((f: any) => ({
- ...f,
- timeRange: { ...f.timeRange, start: val },
- }))
- }
- placeholder="开始时间"
- />
- -
-
- setForm((f: any) => ({
- ...f,
- timeRange: { ...f.timeRange, end: val },
- }))
- }
- placeholder="结束时间"
- />
-
-
-
-
-
- setForm((f: any) => ({
- ...f,
- groupSize: { ...f.groupSize, min: Number(val) },
- }))
- }
- placeholder="最小人数"
- />
- -
-
- setForm((f: any) => ({
- ...f,
- groupSize: { ...f.groupSize, max: Number(val) },
- }))
- }
- placeholder="最大人数"
- />
-
-
-
-
- setForm((f: any) => ({ ...f, targetTags: val }))
- }
- />
-
-
-
- setForm((f: any) => ({ ...f, groupNameTemplate: val }))
- }
- placeholder="请输入群名称模板"
- />
-
-
-
-
-
-
- );
-};
-
-export default AutoGroupForm;
+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 Layout from "@/components/Layout/Layout";
+import style from "./index.module.scss";
+import { createAutoGroup, updateAutoGroup } from "./api";
+
+const defaultForm = {
+ 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: "",
+};
+
+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(defaultForm);
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ if (isEdit) {
+ // 这里应请求详情接口,回填表单,演示用mock
+ setForm({
+ ...defaultForm,
+ name: "VIP客户建群",
+ deviceCount: 2,
+ targetFriends: 156,
+ createInterval: 300,
+ maxGroupsPerDay: 20,
+ timeRange: { start: "09:00", end: "21:00" },
+ groupSize: { min: 20, max: 50 },
+ targetTags: ["VIP客户", "高价值"],
+ groupNameTemplate: "VIP客户交流群{序号}",
+ groupDescription: "VIP客户专属交流群,提供优质服务",
+ });
+ }
+ }, [isEdit, id]);
+
+ const handleSubmit = async () => {
+ setLoading(true);
+ try {
+ if (isEdit) {
+ await updateAutoGroup(id as string, form);
+ Toast.show({ content: "编辑成功" });
+ } else {
+ await createAutoGroup(form);
+ Toast.show({ content: "创建成功" });
+ }
+ navigate("/workspace/auto-group");
+ } catch (e) {
+ Toast.show({ content: "提交失败" });
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+ navigate(-1)}
+ />
+
+ }
+ >
+
+ {isEdit ? "编辑建群任务" : "新建建群任务"}
+
+
+ }
+ >
+
+
+ setForm((f: any) => ({ ...f, name: val }))}
+ placeholder="请输入任务名称"
+ />
+
+
+
+ setForm((f: any) => ({ ...f, deviceCount: Number(val) }))
+ }
+ placeholder="请输入设备数量"
+ />
+
+
+
+ setForm((f: any) => ({ ...f, targetFriends: Number(val) }))
+ }
+ placeholder="请输入目标好友数"
+ />
+
+
+
+ setForm((f: any) => ({ ...f, createInterval: Number(val) }))
+ }
+ placeholder="请输入建群间隔"
+ />
+
+
+
+ setForm((f: any) => ({ ...f, maxGroupsPerDay: Number(val) }))
+ }
+ placeholder="请输入最大建群数"
+ />
+
+
+
+
+ setForm((f: any) => ({
+ ...f,
+ timeRange: { ...f.timeRange, start: val },
+ }))
+ }
+ placeholder="开始时间"
+ />
+ -
+
+ setForm((f: any) => ({
+ ...f,
+ timeRange: { ...f.timeRange, end: val },
+ }))
+ }
+ placeholder="结束时间"
+ />
+
+
+
+
+
+ setForm((f: any) => ({
+ ...f,
+ groupSize: { ...f.groupSize, min: Number(val) },
+ }))
+ }
+ placeholder="最小人数"
+ />
+ -
+
+ setForm((f: any) => ({
+ ...f,
+ groupSize: { ...f.groupSize, max: Number(val) },
+ }))
+ }
+ placeholder="最大人数"
+ />
+
+
+
+ setForm((f: any) => ({ ...f, targetTags: val }))}
+ />
+
+
+
+ setForm((f: any) => ({ ...f, groupNameTemplate: val }))
+ }
+ placeholder="请输入群名称模板"
+ />
+
+
+
+
+
+
+ );
+};
+
+export default AutoGroupForm;
diff --git a/nkebao/src/pages/mobile/workspace/auto-group/list/api.ts b/nkebao/src/pages/mobile/workspace/auto-group/list/api.ts
index 073a6242..e0cd0a9f 100644
--- a/nkebao/src/pages/mobile/workspace/auto-group/list/api.ts
+++ b/nkebao/src/pages/mobile/workspace/auto-group/list/api.ts
@@ -1,8 +1,8 @@
-import request from "@/api/request";
-
-// 获取自动建群任务列表
-export function getAutoGroupList(params?: any) {
- return request("/api/auto-group/list", params, "GET");
-}
-
-// 其他相关API可按需添加
+import request from "@/api/request";
+
+// 获取自动建群任务列表
+export function getAutoGroupList(params?: any) {
+ return request("/api/auto-group/list", params, "GET");
+}
+
+// 其他相关API可按需添加
diff --git a/nkebao/src/pages/mobile/workspace/auto-group/list/index.module.scss b/nkebao/src/pages/mobile/workspace/auto-group/list/index.module.scss
index d4da9d4b..8d7267ec 100644
--- a/nkebao/src/pages/mobile/workspace/auto-group/list/index.module.scss
+++ b/nkebao/src/pages/mobile/workspace/auto-group/list/index.module.scss
@@ -1,173 +1,173 @@
-.autoGroupList {
- padding: 0 12px;
-}
-
-.taskList {
-}
-
-.taskCard {
- margin-bottom: 16px;
- border-radius: 12px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.03);
- border: none;
- background: #fff;
- padding: 16px;
-}
-
-.taskHeader {
- display: flex;
- align-items: center;
- gap: 8px;
- font-size: 16px;
- font-weight: 500;
- margin-bottom: 8px;
-}
-
-.taskTitle {
- flex: 1;
- font-size: 16px;
- font-weight: 500;
- color: #222;
-}
-
-.statusRunning {
- background: #e6f7e6;
- color: #389e0d;
- border-radius: 8px;
- padding: 2px 8px;
- font-size: 12px;
- margin-left: 8px;
-}
-.statusPaused {
- background: #f5f5f5;
- color: #888;
- border-radius: 8px;
- padding: 2px 8px;
- font-size: 12px;
- margin-left: 8px;
-}
-.statusCompleted {
- background: #e6f4ff;
- color: #1677ff;
- border-radius: 8px;
- padding: 2px 8px;
- font-size: 12px;
- margin-left: 8px;
-}
-
-.taskInfoGrid {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 8px 16px;
- margin-bottom: 8px;
- font-size: 13px;
-}
-.infoLabel {
- color: #888;
- font-size: 12px;
-}
-.infoValue {
- color: #222;
- font-weight: 500;
- font-size: 14px;
-}
-
-.taskFooter {
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-size: 12px;
- color: #888;
- border-top: 1px solid #f0f0f0;
- padding-top: 8px;
- margin-top: 8px;
-}
-.footerLeft {
- display: flex;
- align-items: center;
-}
-.footerRight {
- display: flex;
- align-items: center;
-}
-
-.expandPanel {
- margin-top: 16px;
- padding-top: 12px;
- border-top: 1px dashed #e0e0e0;
-}
-.expandGrid {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 16px;
-}
-.expandTitle {
- font-size: 14px;
- font-weight: 500;
- margin-bottom: 4px;
- display: flex;
- align-items: center;
- color: #1677ff;
-}
-.expandInfo {
- font-size: 13px;
- color: #444;
- margin-bottom: 2px;
-}
-.expandTags {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
-}
-.tag {
- background: #f0f5ff;
- color: #1677ff;
- border-radius: 8px;
- padding: 2px 8px;
- font-size: 12px;
-}
-
-.menuItem {
- padding: 8px 12px;
- font-size: 14px;
- color: #222;
- cursor: pointer;
- border-radius: 6px;
- transition: background 0.2s;
-}
-.menuItem:hover {
- background: #f5f5f5;
-}
-.menuItemDanger {
- padding: 8px 12px;
- font-size: 14px;
- color: #e53e3e;
- cursor: pointer;
- border-radius: 6px;
- transition: background 0.2s;
-}
-.menuItemDanger:hover {
- background: #fff1f0;
-}
-
-.emptyCard {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 40px 0;
- background: #fff;
- border-radius: 12px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.03);
- margin-top: 32px;
-}
-.emptyTitle {
- font-size: 16px;
- color: #888;
- margin: 12px 0 4px 0;
-}
-.emptyDesc {
- font-size: 13px;
- color: #bbb;
- margin-bottom: 16px;
-}
\ No newline at end of file
+.autoGroupList {
+ padding: 0 12px;
+}
+
+.taskList {
+}
+
+.taskCard {
+ margin-bottom: 16px;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
+ border: none;
+ background: #fff;
+ padding: 16px;
+}
+
+.taskHeader {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 16px;
+ font-weight: 500;
+ margin-bottom: 8px;
+}
+
+.taskTitle {
+ flex: 1;
+ font-size: 16px;
+ font-weight: 500;
+ color: #222;
+}
+
+.statusRunning {
+ background: #e6f7e6;
+ color: #389e0d;
+ border-radius: 8px;
+ padding: 2px 8px;
+ font-size: 12px;
+ margin-left: 8px;
+}
+.statusPaused {
+ background: #f5f5f5;
+ color: #888;
+ border-radius: 8px;
+ padding: 2px 8px;
+ font-size: 12px;
+ margin-left: 8px;
+}
+.statusCompleted {
+ background: #e6f4ff;
+ color: #1677ff;
+ border-radius: 8px;
+ padding: 2px 8px;
+ font-size: 12px;
+ margin-left: 8px;
+}
+
+.taskInfoGrid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 8px 16px;
+ margin-bottom: 8px;
+ font-size: 13px;
+}
+.infoLabel {
+ color: #888;
+ font-size: 12px;
+}
+.infoValue {
+ color: #222;
+ font-weight: 500;
+ font-size: 14px;
+}
+
+.taskFooter {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 12px;
+ color: #888;
+ border-top: 1px solid #f0f0f0;
+ padding-top: 8px;
+ margin-top: 8px;
+}
+.footerLeft {
+ display: flex;
+ align-items: center;
+}
+.footerRight {
+ display: flex;
+ align-items: center;
+}
+
+.expandPanel {
+ margin-top: 16px;
+ padding-top: 12px;
+ border-top: 1px dashed #e0e0e0;
+}
+.expandGrid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 16px;
+}
+.expandTitle {
+ font-size: 14px;
+ font-weight: 500;
+ margin-bottom: 4px;
+ display: flex;
+ align-items: center;
+ color: #1677ff;
+}
+.expandInfo {
+ font-size: 13px;
+ color: #444;
+ margin-bottom: 2px;
+}
+.expandTags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+.tag {
+ background: #f0f5ff;
+ color: #1677ff;
+ border-radius: 8px;
+ padding: 2px 8px;
+ font-size: 12px;
+}
+
+.menuItem {
+ padding: 8px 12px;
+ font-size: 14px;
+ color: #222;
+ cursor: pointer;
+ border-radius: 6px;
+ transition: background 0.2s;
+}
+.menuItem:hover {
+ background: #f5f5f5;
+}
+.menuItemDanger {
+ padding: 8px 12px;
+ font-size: 14px;
+ color: #e53e3e;
+ cursor: pointer;
+ border-radius: 6px;
+ transition: background 0.2s;
+}
+.menuItemDanger:hover {
+ background: #fff1f0;
+}
+
+.emptyCard {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 40px 0;
+ background: #fff;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
+ margin-top: 32px;
+}
+.emptyTitle {
+ font-size: 16px;
+ color: #888;
+ margin: 12px 0 4px 0;
+}
+.emptyDesc {
+ font-size: 13px;
+ color: #bbb;
+ margin-bottom: 16px;
+}
diff --git a/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx b/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx
index dac222d8..0ef82861 100644
--- a/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx
+++ b/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx
@@ -1,312 +1,312 @@
-import React, { useState } from "react";
-import { useNavigate } from "react-router-dom";
-import { Button, Card, ProgressBar, Popover, Toast, NavBar } from "antd-mobile";
-import { Input, Switch } from "antd";
-import {
- MoreOutline,
- AddCircleOutline,
- UserAddOutline,
- ClockCircleOutline,
- TeamOutline,
- CalendarOutline,
-} from "antd-mobile-icons";
-
-import {
- ReloadOutlined,
- SettingOutlined,
- PlusOutlined,
- ArrowLeftOutlined,
- SearchOutlined,
-} from "@ant-design/icons";
-
-import Layout from "@/components/Layout/Layout";
-import style from "./index.module.scss";
-
-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;
-}
-
-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 getStatusColor = (status: string) => {
- switch (status) {
- case "running":
- return style.statusRunning;
- case "paused":
- return style.statusPaused;
- case "completed":
- return style.statusCompleted;
- default:
- return style.statusPaused;
- }
-};
-
-const getStatusText = (status: string) => {
- switch (status) {
- case "running":
- return "进行中";
- case "paused":
- return "已暂停";
- case "completed":
- return "已完成";
- default:
- return "未知";
- }
-};
-
-const AutoGroupList: React.FC = () => {
- const navigate = useNavigate();
- const [searchTerm, setSearchTerm] = useState("");
- const [tasks, setTasks] = useState
(mockTasks);
-
- const handleDelete = (taskId: string) => {
- const taskToDelete = tasks.find((task) => task.id === taskId);
- if (!taskToDelete) return;
- window.confirm(`确定要删除"${taskToDelete.name}"吗?`) &&
- setTasks(tasks.filter((task) => task.id !== taskId));
- Toast.show({ content: "删除成功" });
- };
-
- const handleEdit = (taskId: string) => {
- navigate(`/workspace/auto-group/${taskId}/edit`);
- };
-
- const handleView = (taskId: string) => {
- navigate(`/workspace/auto-group/${taskId}`);
- };
-
- const handleCopy = (taskId: string) => {
- const taskToCopy = tasks.find((task) => task.id === taskId);
- if (taskToCopy) {
- const newTask = {
- ...taskToCopy,
- id: `${Date.now()}`,
- name: `${taskToCopy.name} (复制)`,
- createTime: new Date().toISOString().replace("T", " ").substring(0, 19),
- };
- setTasks([...tasks, newTask]);
- Toast.show({ content: "复制成功" });
- }
- };
-
- const toggleTaskStatus = (taskId: string) => {
- setTasks((prev) =>
- prev.map((task) =>
- task.id === taskId
- ? {
- ...task,
- status: task.status === "running" ? "paused" : "running",
- }
- : task
- )
- );
- Toast.show({ content: "状态已切换" });
- };
-
- const handleCreateNew = () => {
- navigate("/workspace/auto-group/new");
- };
-
- const filteredTasks = tasks.filter((task) =>
- task.name.toLowerCase().includes(searchTerm.toLowerCase())
- );
-
- return (
-
-
- navigate(-1)}
- />
-
- }
- right={
-
- 新建任务
-
- }
- >
-
自动建群
-
- {/* 搜索栏 */}
-
-
- setSearchTerm(e.target.value)}
- prefix={}
- allowClear
- size="large"
- />
-
-
{}}
- loading={false}
- className="refresh-btn"
- >
-
-
-
- >
- }
- >
-
-
- {filteredTasks.length === 0 ? (
-
-
- 暂无建群任务
- 创建您的第一个自动建群任务
-
- 创建第一个任务
-
-
- ) : (
- filteredTasks.map((task) => (
-
-
-
{task.name}
-
- {getStatusText(task.status)}
-
-
toggleTaskStatus(task.id)}
- disabled={task.status === "completed"}
- style={{ marginLeft: 8 }}
- />
-
- handleView(task.id)}
- >
- 查看
-
- handleEdit(task.id)}
- >
- 编辑
-
- handleCopy(task.id)}
- >
- 复制
-
- handleDelete(task.id)}
- >
- 删除
-
-
- }
- trigger="click"
- >
-
-
-
-
-
-
执行设备
-
{task.deviceCount} 个
-
-
-
目标好友
-
- {task.targetFriends} 个
-
-
-
-
已建群
-
- {task.createdGroups} 个
-
-
-
-
-
-
-
- 更新时间:{task.lastCreateTime}
-
-
- 创建时间:{task.createTime}
-
-
-
- ))
- )}
-
-
-
- );
-};
-
-export default AutoGroupList;
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { Button, Card, ProgressBar, Popover, Toast, NavBar } from "antd-mobile";
+import { Input, Switch } from "antd";
+import {
+ MoreOutline,
+ AddCircleOutline,
+ UserAddOutline,
+ ClockCircleOutline,
+ TeamOutline,
+ CalendarOutline,
+} from "antd-mobile-icons";
+
+import {
+ ReloadOutlined,
+ SettingOutlined,
+ PlusOutlined,
+ ArrowLeftOutlined,
+ SearchOutlined,
+} from "@ant-design/icons";
+
+import Layout from "@/components/Layout/Layout";
+import style from "./index.module.scss";
+
+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;
+}
+
+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 getStatusColor = (status: string) => {
+ switch (status) {
+ case "running":
+ return style.statusRunning;
+ case "paused":
+ return style.statusPaused;
+ case "completed":
+ return style.statusCompleted;
+ default:
+ return style.statusPaused;
+ }
+};
+
+const getStatusText = (status: string) => {
+ switch (status) {
+ case "running":
+ return "进行中";
+ case "paused":
+ return "已暂停";
+ case "completed":
+ return "已完成";
+ default:
+ return "未知";
+ }
+};
+
+const AutoGroupList: React.FC = () => {
+ const navigate = useNavigate();
+ const [searchTerm, setSearchTerm] = useState("");
+ const [tasks, setTasks] = useState