Files
cunkebao_v3/Cunkebao/app/workspace/traffic-distribution/new/components/basic-info-step.tsx
2025-06-19 15:14:41 +08:00

280 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState, useEffect } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { Slider } from "@/components/ui/slider"
import { format } from "date-fns"
import { Search, Users } from "lucide-react"
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"
import { api } from "@/lib/api"
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://yishi.com'
interface BasicInfoStepProps {
onNext: (data: any) => void
initialData?: {
name?: string
distributeType?: string | number
maxPerDay?: number
timeType?: string | number
startTime?: string
endTime?: string
accounts?: string[]
account?: string[]
}
}
export default function BasicInfoStep({ onNext, initialData = {} }: BasicInfoStepProps) {
const [formData, setFormData] = useState({
name: initialData.name ?? `流量分发 ${format(new Date(), "yyyyMMdd HHmm")}`,
distributeType: String(initialData.distributeType ?? "1"),
maxPerDay: initialData.maxPerDay ?? 200,
timeType: String(initialData.timeType ?? "2"),
startTime: initialData.startTime ?? "09:00",
endTime: initialData.endTime ?? "18:00",
})
// 账号选择相关状态
const [accountDialogOpen, setAccountDialogOpen] = useState(false)
const [accountList, setAccountList] = useState<any[]>([])
const [selectedAccountIds, setSelectedAccountIds] = useState<string[]>(
(initialData.account || initialData.accounts || []).map(String)
)
const [accountPage, setAccountPage] = useState(1)
const [accountTotal, setAccountTotal] = useState(0)
const [accountLoading, setAccountLoading] = useState(false)
// API配置弹窗状态
const [apiDialogOpen, setApiDialogOpen] = useState(false)
const [apiKey] = useState("naxf1-82h2f-vdwcm-rrhpm-q9hd1") // 这里可以从后端获取或生成
const [apiUrl] = useState(`${API_BASE_URL}/v1/plan/api/scenariosz`)
// 拉取账号列表
useEffect(() => {
setAccountLoading(true)
api.get(`/v1/workbench/account-list?page=${accountPage}&size=10`).then((res: any) => {
setAccountList(res.data?.list || [])
setAccountTotal(res.data?.total || 0)
}).finally(() => setAccountLoading(false))
}, [accountPage])
const handleChange = (field: string, value: any) => {
setFormData((prev) => ({ ...prev, [field]: value }))
}
const handleSubmit = () => {
onNext({
name: formData.name,
distributeType: Number(formData.distributeType),
maxPerDay: formData.maxPerDay,
timeType: Number(formData.timeType),
startTime: formData.timeType == "2" ? formData.startTime : "09:00",
endTime: formData.timeType == "2" ? formData.endTime : "21:00",
account: selectedAccountIds,
accounts: selectedAccountIds,
})
}
// 账号弹窗确认
const handleAccountDialogConfirm = () => {
setAccountDialogOpen(false)
}
return (
<div className="bg-white rounded-lg p-6 shadow-sm">
<h2 className="text-xl font-bold mb-6"></h2>
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="name" className="flex items-center">
<span className="text-red-500 ml-1">*</span>
</Label>
<Input
id="name"
value={formData.name}
onChange={(e) => handleChange("name", e.target.value)}
placeholder="请输入计划名称"
required
/>
</div>
<div className="space-y-2">
<Label></Label>
<RadioGroup
value={String(formData.distributeType)}
onValueChange={(value) => handleChange("distributeType", value)}
className="space-y-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="1" id="equal" />
<Label htmlFor="equal" className="cursor-pointer">
<span className="text-gray-500 text-sm">()</span>
</Label>
</div>
</RadioGroup>
</div>
<div className="space-y-4">
<Label></Label>
<div className="space-y-2">
<div className="flex justify-between items-center">
<span></span>
<span className="font-medium">{formData.maxPerDay} /</span>
</div>
<Slider
value={[formData.maxPerDay]}
min={1}
max={1000}
step={1}
onValueChange={(value) => handleChange("maxPerDay", value[0])}
className="py-4"
/>
<p className="text-sm text-gray-500">1-1000</p>
</div>
<div className="space-y-4 pt-4">
<Label></Label>
<RadioGroup
value={String(formData.timeType)}
onValueChange={(value) => handleChange("timeType", value)}
className="space-y-4"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="1" id="allDay" />
<Label htmlFor="allDay" className="cursor-pointer">
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="2" id="custom" />
<Label htmlFor="custom" className="cursor-pointer">
</Label>
</div>
</RadioGroup>
{formData.timeType == "2" && (
<div className="grid grid-cols-2 gap-4 pt-2">
<div>
<Label htmlFor="startTime" className="mb-2 block">
</Label>
<div className="relative">
<Input
id="startTime"
type="time"
value={formData.startTime}
onChange={(e) => handleChange("startTime", e.target.value)}
/>
</div>
</div>
<div>
<Label htmlFor="endTime" className="mb-2 block">
</Label>
<div className="relative">
<Input
id="endTime"
type="time"
value={formData.endTime}
onChange={(e) => handleChange("endTime", e.target.value)}
/>
</div>
</div>
</div>
)}
</div>
</div>
{/* 账号选择 */}
<div className="space-y-2">
<Label> <span className="text-red-500 ml-1">*</span></Label>
<div className="relative w-full">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
<Input
placeholder="选择账号"
value={selectedAccountIds.length > 0 ? `已选择${selectedAccountIds.length}个账号` : ''}
readOnly
className="pl-10 cursor-pointer"
onClick={() => setAccountDialogOpen(true)}
/>
</div>
<div className="flex items-center gap-2 text-sm text-gray-500">
<Users className="w-4 h-4 text-blue-500" />
<span></span>
{selectedAccountIds.length === 0 ? (
<span className="text-gray-400"></span>
) : (
<span className="bg-blue-50 text-blue-600 rounded px-2 py-0.5 font-semibold">{selectedAccountIds.length} </span>
)}
</div>
</div>
</div>
<div className="mt-8 flex justify-end">
<Button onClick={handleSubmit} disabled={selectedAccountIds.length === 0} className="px-8">
</Button>
</div>
{/* 账号选择弹窗 */}
<Dialog open={accountDialogOpen} onOpenChange={setAccountDialogOpen}>
<DialogContent className="max-w-xl w-full p-0 rounded-2xl shadow-2xl max-h-[80vh]">
<DialogTitle className="text-lg font-bold text-center py-3 border-b"></DialogTitle>
<div className="p-6 pt-4">
{/* 账号列表 */}
<div className="max-h-[500px] overflow-y-auto space-y-2">
{accountLoading ? (
<div className="text-center text-gray-400 py-8">...</div>
) : accountList.length === 0 ? (
<div className="text-center text-gray-400 py-8"></div>
) : (
accountList.map(account => (
<label
key={account.id}
className={`
flex items-center gap-3 p-4 rounded-xl border
${selectedAccountIds.includes(String(account.id)) ? "border-blue-500 bg-blue-50" : "border-gray-200 bg-white"}
hover:border-blue-400 transition-colors cursor-pointer
`}
>
<input
type="checkbox"
className="accent-blue-500 scale-110"
checked={selectedAccountIds.includes(String(account.id))}
onChange={() => {
setSelectedAccountIds(prev =>
prev.includes(String(account.id))
? prev.filter(id => id !== String(account.id))
: [...prev, String(account.id)]
)
}}
/>
<div className="flex-1">
<div className="font-semibold text-base">{account.realName || account.nickname}</div>
<div className="text-xs text-gray-500">: {account.userName || '--'}</div>
</div>
</label>
))
)}
</div>
{/* 确认按钮 */}
<div className="flex justify-center mt-8">
<Button
className="w-4/5 py-3 rounded-full text-base font-bold shadow-md"
onClick={handleAccountDialogConfirm}
>
</Button>
</div>
</div>
</DialogContent>
</Dialog>
</div>
)
}