From 3830c272bf30c783dd229d759e19c80e5352659d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 24 Jul 2025 17:49:17 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=96=B0=E5=BB=BA=E5=86=85=E5=AE=B9=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/content/form/index.module.scss | 180 +++--- nkebao/src/pages/content/form/index.tsx | 546 ++++++++---------- .../src/pages/content/list/index.module.scss | 35 +- nkebao/src/pages/content/list/index.tsx | 117 ++-- 4 files changed, 385 insertions(+), 493 deletions(-) diff --git a/nkebao/src/pages/content/form/index.module.scss b/nkebao/src/pages/content/form/index.module.scss index c988b351..0ee6be9d 100644 --- a/nkebao/src/pages/content/form/index.module.scss +++ b/nkebao/src/pages/content/form/index.module.scss @@ -1,116 +1,138 @@ .form-page { + background: #f7f8fa; padding: 16px; - background: #f5f5f5; - min-height: 100vh; } -.loading { - display: flex; - justify-content: center; - align-items: center; - padding: 40px 0; +.form-main { + max-width: 420px; + margin: 0 auto; + padding: 16px 0 0 0; } -.form { - display: flex; - flex-direction: column; - gap: 16px; +.form-section { + margin-bottom: 18px; } .form-card { - background: white; - border-radius: 8px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - border: none; - padding: 16px; + border-radius: 16px; + box-shadow: 0 4px 24px rgba(0,0,0,0.06); + padding: 24px 18px 18px 18px; + background: #fff; } -.card-title { - font-size: 16px; +.form-label { font-weight: 600; - color: #333; - margin-bottom: 16px; - padding-bottom: 8px; + font-size: 16px; + color: #222; + display: block; + margin-bottom: 6px; +} + +.section-title { + font-size: 16px; + font-weight: 700; + color: #222; + margin-top: 28px; + margin-bottom: 12px; + letter-spacing: 0.5px; +} + +.section-block { + padding: 12px 0 8px 0; border-bottom: 1px solid #f0f0f0; + margin-bottom: 8px; } -.textarea { - border-radius: 6px; - border: 1px solid #d9d9d9; - - &:focus { - border-color: #1677ff; - box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.1); +.tabs-bar { + .adm-tabs-header { + background: #f7f8fa; + border-radius: 8px; + margin-bottom: 8px; + } + .adm-tabs-tab { + font-size: 15px; + font-weight: 500; + padding: 8px 0; } } -.time-settings { +.collapse { + margin-top: 12px; + .adm-collapse-panel-content { + padding-bottom: 8px; + background: #f8fafc; + border-radius: 10px; + padding: 18px 14px 10px 14px; + margin-top: 2px; + box-shadow: 0 2px 8px rgba(0,0,0,0.03); + } + .form-section { + margin-bottom: 22px; + } + .form-label { + font-size: 15px; + font-weight: 500; + margin-bottom: 4px; + color: #333; + } + .adm-input { + min-height: 42px; + font-size: 15px; + border-radius: 7px; + margin-bottom: 2px; + } +} + +.ai-row, .section-block { display: flex; - gap: 16px; - margin-top: 16px; - padding-top: 16px; - border-top: 1px solid #f0f0f0; + align-items: center; + gap: 12px; } -.time-picker { - width: 100%; - border-radius: 6px; - - &:focus { - border-color: #1677ff; - box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.1); - } +.ai-desc { + color: #888; + font-size: 13px; + flex: 1; } -.form-actions { +.date-row, .section-block { display: flex; gap: 12px; - padding: 16px; - background: white; - border-radius: 8px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - margin-top: 16px; + align-items: center; } -.back-btn { - flex: 1; - border-radius: 6px; - border: 1px solid #d9d9d9; - - &:hover { - border-color: #1677ff; - color: #1677ff; - } +.adm-input { + min-height: 44px; + font-size: 15px; + border-radius: 8px; } .submit-btn { - flex: 1; - border-radius: 6px; + margin-top: 32px; + height: 48px !important; + border-radius: 10px !important; + font-size: 17px; + font-weight: 600; + letter-spacing: 1px; } -// 覆盖 antd-mobile 的默认样式 -:global { - .adm-form-item { - margin-bottom: 16px; +@media (max-width: 600px) { + .form-main { + max-width: 100vw; + padding: 0; } - - .adm-form-item-label { - font-size: 14px; - color: #333; - font-weight: 500; + .form-card { + border-radius: 0; + box-shadow: none; + padding: 16px 6px 12px 6px; } - - .adm-input { - border-radius: 6px; - border: 1px solid #d9d9d9; - - &:focus { - border-color: #1677ff; - box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.1); - } + .section-title { + font-size: 15px; + margin-top: 22px; + margin-bottom: 8px; } - - .adm-switch { - --checked-color: #1677ff; + .submit-btn { + height: 44px !important; + font-size: 15px; } } diff --git a/nkebao/src/pages/content/form/index.tsx b/nkebao/src/pages/content/form/index.tsx index d60f1819..49ea7a06 100644 --- a/nkebao/src/pages/content/form/index.tsx +++ b/nkebao/src/pages/content/form/index.tsx @@ -1,331 +1,257 @@ -import React, { useState, useEffect } from "react"; -import { useNavigate, useParams } from "react-router-dom"; -import { - Button, - Toast, - SpinLoading, - Form, - Input, - Switch, - Card, - Space, -} from "antd-mobile"; -import { Input as AntdInput, Select, TimePicker } from "antd"; -import { - ArrowLeftOutlined, - SaveOutlined, - UserOutlined, - TeamOutlined, -} from "@ant-design/icons"; -import Layout from "@/components/Layout/Layout"; +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { Input as AntdInput, Switch } from "antd"; +import { Button, Collapse, Toast, DatePicker, Tabs } from "antd-mobile"; import NavCommon from "@/components/NavCommon"; -import { - getContentLibraryDetail, - createContentLibrary, - updateContentLibrary, -} from "./api"; -import { ContentLibrary, CreateContentLibraryParams } from "./data"; +import FriendSelection from "@/components/FriendSelection"; +import GroupSelection from "@/components/GroupSelection"; +import Layout from "@/components/Layout/Layout"; import style from "./index.module.scss"; +import request from "@/api/request"; -const { Option } = Select; const { TextArea } = AntdInput; -const ContentLibraryForm: React.FC = () => { +function formatDate(date: Date | null) { + if (!date) return ""; + // 格式化为 YYYY-MM-DD + const y = date.getFullYear(); + const m = (date.getMonth() + 1).toString().padStart(2, "0"); + const d = date.getDate().toString().padStart(2, "0"); + return `${y}-${m}-${d}`; +} + +export default function ContentForm() { const navigate = useNavigate(); - const { id } = useParams<{ id: string }>(); - const [form] = Form.useForm(); - const [loading, setLoading] = useState(false); - const [saving, setSaving] = useState(false); - const [library, setLibrary] = useState(null); - const [sourceType, setSourceType] = useState(1); + const [sourceType, setSourceType] = useState<"friends" | "groups">("friends"); + const [name, setName] = useState(""); + const [selectedFriends, setSelectedFriends] = useState([]); + const [selectedGroups, setSelectedGroups] = useState([]); + const [useAI, setUseAI] = useState(false); + const [aiPrompt, setAIPrompt] = useState(""); + const [enabled, setEnabled] = useState(true); + const [dateRange, setDateRange] = useState<[Date | null, Date | null]>([ + null, + null, + ]); + const [showStartPicker, setShowStartPicker] = useState(false); + const [showEndPicker, setShowEndPicker] = useState(false); + const [keywordsInclude, setKeywordsInclude] = useState(""); + const [keywordsExclude, setKeywordsExclude] = useState(""); + const [submitting, setSubmitting] = useState(false); - const isEdit = !!id; - - // 获取内容库详情 - useEffect(() => { - if (isEdit && id) { - fetchLibraryDetail(); + const handleSubmit = async (e?: React.FormEvent) => { + if (e) e.preventDefault(); + if (!name.trim()) { + Toast.show({ content: "请输入内容库名称", position: "top" }); + return; } - }, [isEdit, id]); - - const fetchLibraryDetail = async () => { - setLoading(true); + setSubmitting(true); try { - const response = await getContentLibraryDetail(id!); - if (response.code === 200 && response.data) { - setLibrary(response.data); - setSourceType(response.data.sourceType); - - // 填充表单数据 - form.setFieldsValue({ - name: response.data.name, - sourceType: response.data.sourceType, - keywordInclude: response.data.keywordInclude?.join(",") || "", - keywordExclude: response.data.keywordExclude?.join(",") || "", - aiPrompt: response.data.aiPrompt || "", - timeEnabled: response.data.timeEnabled === 1, - timeStart: response.data.timeStart || "09:00", - timeEnd: response.data.timeEnd || "18:00", - }); - } else { - Toast.show({ - content: response.msg || "获取内容库详情失败", - position: "top", - }); - } - } catch (error: any) { - console.error("获取内容库详情失败:", error); - Toast.show({ - content: error?.message || "请检查网络连接", - position: "top", - }); - } finally { - setLoading(false); - } - }; - - const handleSubmit = async (values: any) => { - setSaving(true); - try { - const params: CreateContentLibraryParams = { - name: values.name, - sourceType: values.sourceType, - keywordInclude: values.keywordInclude - ? values.keywordInclude - .split(",") - .map((k: string) => k.trim()) - .filter(Boolean) - : [], - keywordExclude: values.keywordExclude - ? values.keywordExclude - .split(",") - .map((k: string) => k.trim()) - .filter(Boolean) - : [], - aiPrompt: values.aiPrompt || "", - timeEnabled: values.timeEnabled ? 1 : 0, - timeStart: values.timeStart || "09:00", - timeEnd: values.timeEnd || "18:00", + const payload = { + name, + sourceType: sourceType === "friends" ? 1 : 2, + friends: selectedFriends, + groups: selectedGroups, + groupMembers: {}, + keywordInclude: keywordsInclude + .split(/,|,|\n|\s+/) + .map((s) => s.trim()) + .filter(Boolean), + keywordExclude: keywordsExclude + .split(/,|,|\n|\s+/) + .map((s) => s.trim()) + .filter(Boolean), + aiPrompt, + timeEnabled: dateRange[0] || dateRange[1] ? 1 : 0, + startTime: dateRange[0] ? formatDate(dateRange[0]) : "", + endTime: dateRange[1] ? formatDate(dateRange[1]) : "", + status: enabled ? 1 : 0, }; - - let response; - if (isEdit) { - response = await updateContentLibrary({ - id: id!, - ...params, - }); - } else { - response = await createContentLibrary(params); - } - - if (response.code === 200) { - Toast.show({ - content: isEdit ? "更新成功" : "创建成功", - position: "top", - }); - navigate("/content"); - } else { - Toast.show({ - content: response.msg || (isEdit ? "更新失败" : "创建失败"), - position: "top", - }); - } - } catch (error: any) { - console.error("保存内容库失败:", error); - Toast.show({ - content: error?.message || "请检查网络连接", - position: "top", - }); + await request("/v1/content/library/create", payload, "POST"); + Toast.show({ content: "创建成功", position: "top" }); + navigate("/content"); + } catch (e: any) { + Toast.show({ content: e?.message || "创建失败", position: "top" }); } finally { - setSaving(false); + setSubmitting(false); } }; - const handleBack = () => { - navigate("/content"); - }; - - if (loading) { - return ( - } - > -
- -
-
- ); - } - return ( - }> + } + footer={ +
+ +
+ } + >
-
e.preventDefault()} + autoComplete="off" > - {/* 基本信息 */} - -
基本信息
- - - - - - - - -
- - {/* 关键词设置 */} - -
关键词设置
- - -