更新管理端用户详情弹窗,新增 VIP 手动设置功能,支持到期日、展示名、项目、联系方式和简介的编辑。优化 VIP 相关接口,确保用户状态和资料更新功能正常,增强用户体验。调整文档,明确 VIP 设置的必填项和格式要求。
This commit is contained in:
@@ -10,7 +10,6 @@ import { Badge } from '@/components/ui/badge'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import {
|
||||
User,
|
||||
Phone,
|
||||
@@ -25,7 +24,6 @@ import {
|
||||
Save,
|
||||
X,
|
||||
Tag,
|
||||
Crown,
|
||||
} from 'lucide-react'
|
||||
import { get, put, post } from '@/api/client'
|
||||
|
||||
@@ -91,12 +89,6 @@ export function UserDetailModal({
|
||||
const [editNickname, setEditNickname] = useState('')
|
||||
const [editTags, setEditTags] = useState<string[]>([])
|
||||
const [newTag, setNewTag] = useState('')
|
||||
const [editIsVip, setEditIsVip] = useState(false)
|
||||
const [editVipExpireDate, setEditVipExpireDate] = useState('')
|
||||
const [editVipName, setEditVipName] = useState('')
|
||||
const [editVipProject, setEditVipProject] = useState('')
|
||||
const [editVipContact, setEditVipContact] = useState('')
|
||||
const [editVipBio, setEditVipBio] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (open && userId) loadUserDetail()
|
||||
@@ -115,12 +107,6 @@ export function UserDetailModal({
|
||||
setEditPhone(u.phone || '')
|
||||
setEditNickname(u.nickname || '')
|
||||
setEditTags(typeof u.tags === 'string' ? (JSON.parse(u.tags || '[]') as string[]) : [])
|
||||
setEditIsVip(!!u.isVip)
|
||||
setEditVipExpireDate(u.vipExpireDate ? String(u.vipExpireDate).slice(0, 10) : '')
|
||||
setEditVipName(u.vipName || '')
|
||||
setEditVipProject(u.vipProject || '')
|
||||
setEditVipContact(u.vipContact || '')
|
||||
setEditVipBio(u.vipBio || '')
|
||||
}
|
||||
try {
|
||||
const trackData = await get<{ success?: boolean; tracks?: UserTrack[] }>(
|
||||
@@ -173,17 +159,6 @@ export function UserDetailModal({
|
||||
|
||||
async function handleSave() {
|
||||
if (!user) return
|
||||
if (editIsVip && !editVipExpireDate.trim()) {
|
||||
alert('开启 VIP 时请填写有效到期日')
|
||||
return
|
||||
}
|
||||
if (editIsVip && editVipExpireDate.trim()) {
|
||||
const d = new Date(editVipExpireDate)
|
||||
if (isNaN(d.getTime())) {
|
||||
alert('到期日格式无效,请使用 YYYY-MM-DD')
|
||||
return
|
||||
}
|
||||
}
|
||||
setSaving(true)
|
||||
try {
|
||||
const payload: Record<string, unknown> = {
|
||||
@@ -191,12 +166,6 @@ export function UserDetailModal({
|
||||
phone: editPhone || undefined,
|
||||
nickname: editNickname || undefined,
|
||||
tags: JSON.stringify(editTags),
|
||||
isVip: editIsVip,
|
||||
vipExpireDate: editVipExpireDate || undefined,
|
||||
vipName: editVipName || undefined,
|
||||
vipProject: editVipProject || undefined,
|
||||
vipContact: editVipContact || undefined,
|
||||
vipBio: editVipBio || undefined,
|
||||
}
|
||||
const data = await put<{ success?: boolean; error?: string }>('/api/db/users', payload)
|
||||
if (data?.success) {
|
||||
@@ -331,70 +300,6 @@ export function UserDetailModal({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 bg-[#0a1628] rounded-lg border border-amber-500/20">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Crown className="w-4 h-4 text-amber-400" />
|
||||
<span className="text-white font-medium">VIP 手动设置</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-gray-300">VIP 会员</Label>
|
||||
<Switch
|
||||
checked={editIsVip}
|
||||
onCheckedChange={setEditIsVip}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-300">
|
||||
到期日 (YYYY-MM-DD)
|
||||
{editIsVip && <span className="text-amber-400 ml-1">*必填</span>}
|
||||
</Label>
|
||||
<Input
|
||||
type="date"
|
||||
className="bg-[#162840] border-gray-700 text-white"
|
||||
value={editVipExpireDate}
|
||||
onChange={(e) => setEditVipExpireDate(e.target.value)}
|
||||
required={editIsVip}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2 col-span-2">
|
||||
<Label className="text-gray-300">VIP 展示名</Label>
|
||||
<Input
|
||||
className="bg-[#162840] border-gray-700 text-white"
|
||||
placeholder="创业老板排行展示名"
|
||||
value={editVipName}
|
||||
onChange={(e) => setEditVipName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2 col-span-2">
|
||||
<Label className="text-gray-300">项目/公司</Label>
|
||||
<Input
|
||||
className="bg-[#162840] border-gray-700 text-white"
|
||||
placeholder="项目名称"
|
||||
value={editVipProject}
|
||||
onChange={(e) => setEditVipProject(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2 col-span-2">
|
||||
<Label className="text-gray-300">联系方式</Label>
|
||||
<Input
|
||||
className="bg-[#162840] border-gray-700 text-white"
|
||||
placeholder="微信号或手机"
|
||||
value={editVipContact}
|
||||
onChange={(e) => setEditVipContact(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2 col-span-2">
|
||||
<Label className="text-gray-300">一句话简介</Label>
|
||||
<Input
|
||||
className="bg-[#162840] border-gray-700 text-white"
|
||||
placeholder="简要描述业务"
|
||||
value={editVipBio}
|
||||
onChange={(e) => setEditVipBio(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="p-4 bg-[#0a1628] rounded-lg">
|
||||
<p className="text-gray-400 text-sm">推荐人数</p>
|
||||
|
||||
Reference in New Issue
Block a user