|
|
@@ -2,21 +2,72 @@ import React, { useState, useEffect } from 'react';
|
|
|
import { useAuth } from '../hooks/AuthProvider';
|
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
import { silverUserProfileClient, userClient } from '@/client/api';
|
|
|
-import { Form, Input, Button, DatePicker, Select } from 'antd';
|
|
|
-import dayjs from 'dayjs';
|
|
|
-import { toast } from 'react-toastify';
|
|
|
-import { SimpleAvatarUpload } from '@/client/mobile/components/SimpleAvatarUpload';
|
|
|
+import { useForm } from 'react-hook-form';
|
|
|
+import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
+import { z } from 'zod';
|
|
|
+import { toast } from 'sonner';
|
|
|
+import { InferRequestType, InferResponseType } from 'hono/client';
|
|
|
|
|
|
-const { Option } = Select;
|
|
|
+// 导入 shadcn 组件
|
|
|
+import { Button } from '@/client/components/ui/button';
|
|
|
+import { Card, CardContent, CardHeader, CardTitle } from '@/client/components/ui/card';
|
|
|
+import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
|
|
|
+import { Input } from '@/client/components/ui/input';
|
|
|
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select';
|
|
|
+import { Textarea } from '@/client/components/ui/textarea';
|
|
|
+import { Skeleton } from '@/client/components/ui/skeleton';
|
|
|
+import { ChevronLeft, User, Mail, Phone, Building, FileText, Award, BookOpen } from 'lucide-react';
|
|
|
+
|
|
|
+// 保留现有的 AvatarUpload 组件
|
|
|
+import { AvatarUpload } from '@/client/mobile/components/AvatarUpload';
|
|
|
+
|
|
|
+// 类型定义
|
|
|
+type CreateProfileRequest = InferRequestType<typeof silverUserProfileClient.$post>['json'];
|
|
|
+type UpdateProfileRequest = InferRequestType<typeof silverUserProfileClient[':id']['$put']>['json'];
|
|
|
+type UpdateUserRequest = InferRequestType<typeof userClient[':id']['$put']>['json'];
|
|
|
+type ProfileResponse = InferResponseType<typeof silverUserProfileClient.$get, 200>['data'][0];
|
|
|
+
|
|
|
+// 表单 Schema
|
|
|
+const profileFormSchema = z.object({
|
|
|
+ username: z.string().min(2, '用户名至少2个字符').max(20, '用户名最多20个字符'),
|
|
|
+ email: z.string().email('请输入有效的邮箱地址'),
|
|
|
+ phone: z.string().min(11, '手机号至少11位').max(20, '手机号最多20位'),
|
|
|
+ realName: z.string().min(1, '请输入真实姓名').max(50, '真实姓名最多50个字符'),
|
|
|
+ age: z.coerce.number().min(1, '年龄必须大于0').max(120, '年龄不能超过120岁'),
|
|
|
+ gender: z.coerce.number().min(1).max(3),
|
|
|
+ organization: z.string().max(255, '所属机构最多255个字符').optional(),
|
|
|
+ personalIntro: z.string().max(500, '个人简介最多500个字符').optional(),
|
|
|
+ personalSkills: z.string().max(500, '个人技能最多500个字符').optional(),
|
|
|
+ personalExperience: z.string().max(500, '个人经历最多500个字符').optional(),
|
|
|
+});
|
|
|
+
|
|
|
+type ProfileFormData = z.infer<typeof profileFormSchema>;
|
|
|
|
|
|
const ProfileEditPage: React.FC = () => {
|
|
|
const { user } = useAuth();
|
|
|
const navigate = useNavigate();
|
|
|
- const [form] = Form.useForm();
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
- const [profile, setProfile] = useState<any>(null);
|
|
|
+ const [profile, setProfile] = useState<ProfileResponse | null>(null);
|
|
|
+ const [isLoading, setIsLoading] = useState(true);
|
|
|
const [avatarFileId, setAvatarFileId] = useState<number | null>(null);
|
|
|
|
|
|
+ // 初始化表单
|
|
|
+ const form = useForm<ProfileFormData>({
|
|
|
+ resolver: zodResolver(profileFormSchema),
|
|
|
+ defaultValues: {
|
|
|
+ username: '',
|
|
|
+ email: '',
|
|
|
+ phone: '',
|
|
|
+ realName: '',
|
|
|
+ age: 18,
|
|
|
+ gender: 1,
|
|
|
+ organization: '',
|
|
|
+ personalIntro: '',
|
|
|
+ personalSkills: '',
|
|
|
+ personalExperience: '',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
if (user) {
|
|
|
loadProfile();
|
|
|
@@ -24,44 +75,55 @@ const ProfileEditPage: React.FC = () => {
|
|
|
}, [user]);
|
|
|
|
|
|
const loadProfile = async () => {
|
|
|
+ if (!user) return;
|
|
|
+
|
|
|
try {
|
|
|
+ setIsLoading(true);
|
|
|
const response = await silverUserProfileClient.$get({
|
|
|
query: { filters: JSON.stringify({ userId: user.id }) }
|
|
|
});
|
|
|
-
|
|
|
+
|
|
|
if (response.status === 200) {
|
|
|
const data = await response.json();
|
|
|
if (data.data && data.data.length > 0) {
|
|
|
const profileData = data.data[0];
|
|
|
setProfile(profileData);
|
|
|
setAvatarFileId(profileData.avatarFileId || null);
|
|
|
-
|
|
|
- form.setFieldsValue({
|
|
|
+
|
|
|
+ // 填充表单数据
|
|
|
+ form.reset({
|
|
|
+ username: user.username || '',
|
|
|
+ email: user.email || '',
|
|
|
+ phone: profileData.phone || user.phone || '',
|
|
|
realName: profileData.realName || '',
|
|
|
- age: profileData.age || '',
|
|
|
+ age: profileData.age || 18,
|
|
|
gender: profileData.gender || 1,
|
|
|
- phone: profileData.phone || '',
|
|
|
- email: profileData.email || '',
|
|
|
organization: profileData.organization || '',
|
|
|
personalIntro: profileData.personalIntro || '',
|
|
|
personalSkills: profileData.personalSkills || '',
|
|
|
- personalExperience: profileData.personalExperience || ''
|
|
|
+ personalExperience: profileData.personalExperience || '',
|
|
|
});
|
|
|
} else {
|
|
|
- form.setFieldsValue({
|
|
|
- userId: user.id,
|
|
|
+ // 新用户,使用现有用户信息填充
|
|
|
+ form.reset({
|
|
|
+ username: user.username || '',
|
|
|
+ email: user.email || '',
|
|
|
phone: user.phone || '',
|
|
|
- email: user.email || ''
|
|
|
+ realName: '',
|
|
|
+ age: 18,
|
|
|
+ gender: 1,
|
|
|
+ organization: '',
|
|
|
+ personalIntro: '',
|
|
|
+ personalSkills: '',
|
|
|
+ personalExperience: '',
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
- form.setFieldsValue({
|
|
|
- username: user.username
|
|
|
- });
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('加载个人档案失败:', error);
|
|
|
toast.error('加载个人档案失败');
|
|
|
+ } finally {
|
|
|
+ setIsLoading(false);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -69,53 +131,59 @@ const ProfileEditPage: React.FC = () => {
|
|
|
setAvatarFileId(fileId);
|
|
|
};
|
|
|
|
|
|
- const handleSubmit = async (values: any) => {
|
|
|
+ const onSubmit = async (data: ProfileFormData) => {
|
|
|
if (!user) return;
|
|
|
-
|
|
|
+
|
|
|
setLoading(true);
|
|
|
try {
|
|
|
// 更新用户信息
|
|
|
+ const userUpdateData: UpdateUserRequest = {
|
|
|
+ username: data.username,
|
|
|
+ email: data.email,
|
|
|
+ phone: data.phone,
|
|
|
+ };
|
|
|
+
|
|
|
const userUpdateResponse = await userClient[':id'].$put({
|
|
|
param: { id: user.id },
|
|
|
- json: {
|
|
|
- username: values.username,
|
|
|
- email: values.email,
|
|
|
- phone: values.phone
|
|
|
- }
|
|
|
+ json: userUpdateData,
|
|
|
});
|
|
|
|
|
|
if (userUpdateResponse.status === 200) {
|
|
|
// 更新银龄档案
|
|
|
- const profileData = {
|
|
|
+ const profileData: CreateProfileRequest | UpdateProfileRequest = {
|
|
|
userId: user.id,
|
|
|
- realName: values.realName,
|
|
|
- age: values.age,
|
|
|
- gender: values.gender,
|
|
|
- phone: values.phone,
|
|
|
- email: values.email,
|
|
|
+ realName: data.realName,
|
|
|
+ age: data.age,
|
|
|
+ gender: data.gender as 1 | 2 | 3,
|
|
|
+ phone: data.phone,
|
|
|
+ email: data.email,
|
|
|
avatarFileId: avatarFileId || undefined,
|
|
|
- organization: values.organization,
|
|
|
- personalIntro: values.personalIntro,
|
|
|
- personalSkills: values.personalSkills,
|
|
|
- personalExperience: values.personalExperience
|
|
|
+ organization: data.organization,
|
|
|
+ personalIntro: data.personalIntro,
|
|
|
+ personalSkills: data.personalSkills,
|
|
|
+ personalExperience: data.personalExperience,
|
|
|
};
|
|
|
|
|
|
let response;
|
|
|
if (profile?.id) {
|
|
|
response = await silverUserProfileClient[':id'].$put({
|
|
|
param: { id: profile.id },
|
|
|
- json: profileData
|
|
|
+ json: profileData as UpdateProfileRequest,
|
|
|
});
|
|
|
} else {
|
|
|
response = await silverUserProfileClient.$post({
|
|
|
- json: profileData
|
|
|
+ json: profileData as CreateProfileRequest,
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- if (response.status === 200) {
|
|
|
+ if (response.status === 200 || response.status === 201) {
|
|
|
toast.success('个人信息更新成功');
|
|
|
navigate('/profile');
|
|
|
+ } else {
|
|
|
+ throw new Error('更新个人档案失败');
|
|
|
}
|
|
|
+ } else {
|
|
|
+ throw new Error('更新用户信息失败');
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('更新失败:', error);
|
|
|
@@ -127,12 +195,39 @@ const ProfileEditPage: React.FC = () => {
|
|
|
|
|
|
if (!user) {
|
|
|
return (
|
|
|
- <div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
|
|
- <div className="text-center">
|
|
|
- <p className="text-gray-600 mb-4">请先登录</p>
|
|
|
- <Button type="primary" onClick={() => navigate('/login')}>
|
|
|
- 去登录
|
|
|
- </Button>
|
|
|
+ <div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
|
|
|
+ <Card className="w-full max-w-sm">
|
|
|
+ <CardContent className="pt-6">
|
|
|
+ <div className="text-center">
|
|
|
+ <p className="text-gray-600 mb-4">请先登录</p>
|
|
|
+ <Button onClick={() => navigate('/login')} className="w-full">
|
|
|
+ 去登录
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isLoading) {
|
|
|
+ return (
|
|
|
+ <div className="min-h-screen bg-gray-50">
|
|
|
+ <div className="bg-white border-b">
|
|
|
+ <div className="flex items-center p-4">
|
|
|
+ <Button variant="ghost" size="sm" onClick={() => navigate('/profile')} className="p-0">
|
|
|
+ <ChevronLeft className="h-5 w-5" />
|
|
|
+ </Button>
|
|
|
+ <h1 className="flex-1 text-center text-lg font-semibold">编辑个人信息</h1>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="p-4 space-y-4">
|
|
|
+ <Skeleton className="h-10 w-full" />
|
|
|
+ <Skeleton className="h-10 w-full" />
|
|
|
+ <Skeleton className="h-10 w-full" />
|
|
|
+ <Skeleton className="h-24 w-full" />
|
|
|
+ <Skeleton className="h-10 w-full" />
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|
|
|
@@ -140,93 +235,260 @@ const ProfileEditPage: React.FC = () => {
|
|
|
|
|
|
return (
|
|
|
<div className="min-h-screen bg-gray-50">
|
|
|
- <div className="bg-white">
|
|
|
- <div className="flex items-center p-4 border-b">
|
|
|
- <Button type="text" onClick={() => navigate('/profile')}>
|
|
|
- 返回
|
|
|
+ {/* 头部导航 */}
|
|
|
+ <div className="bg-white border-b">
|
|
|
+ <div className="flex items-center p-4">
|
|
|
+ <Button
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => navigate('/profile')}
|
|
|
+ className="p-0 hover:bg-transparent"
|
|
|
+ >
|
|
|
+ <ChevronLeft className="h-5 w-5" />
|
|
|
</Button>
|
|
|
<h1 className="flex-1 text-center text-lg font-semibold">编辑个人信息</h1>
|
|
|
+ <div className="w-10" />
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div className="p-4">
|
|
|
- <Form form={form} layout="vertical" onFinish={handleSubmit}>
|
|
|
- {/* 头像上传 */}
|
|
|
- <Form.Item label="头像">
|
|
|
- <div className="flex items-center space-x-4">
|
|
|
- <SimpleAvatarUpload
|
|
|
- currentAvatar={avatarFileId ? profile?.avatarFile?.fullUrl : ''}
|
|
|
- size={80}
|
|
|
- onUploadSuccess={handleAvatarChange}
|
|
|
- fileId={avatarFileId}
|
|
|
- />
|
|
|
- <span className="text-sm text-gray-500">点击头像上传新照片</span>
|
|
|
- </div>
|
|
|
- </Form.Item>
|
|
|
+ <Form {...form}>
|
|
|
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
|
|
+ {/* 头像上传 */}
|
|
|
+ <Card>
|
|
|
+ <CardHeader className="pb-3">
|
|
|
+ <CardTitle className="text-base">头像设置</CardTitle>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent>
|
|
|
+ <div className="flex items-center space-x-4">
|
|
|
+ <AvatarUpload
|
|
|
+ currentAvatar={avatarFileId ? profile?.avatarFile?.fullUrl : ''}
|
|
|
+ size={80}
|
|
|
+ onUploadSuccess={handleAvatarChange}
|
|
|
+ />
|
|
|
+ <div>
|
|
|
+ <p className="text-sm text-gray-600">点击头像上传新照片</p>
|
|
|
+ <p className="text-xs text-gray-500 mt-1">支持 JPG、PNG 格式,最大 2MB</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
|
|
|
- {/* 基础信息 */}
|
|
|
- <Form.Item
|
|
|
- name="username"
|
|
|
- label="用户名"
|
|
|
- rules={[{ required: true, message: '请输入用户名' }]}
|
|
|
- >
|
|
|
- <Input placeholder="请输入用户名" />
|
|
|
- </Form.Item>
|
|
|
+ {/* 基础信息 */}
|
|
|
+ <Card>
|
|
|
+ <CardHeader className="pb-3">
|
|
|
+ <CardTitle className="text-base flex items-center gap-2">
|
|
|
+ <User className="h-4 w-4" />
|
|
|
+ 基础信息
|
|
|
+ </CardTitle>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent className="space-y-4">
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="username"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>用户名 *</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入用户名" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
|
|
|
- <Form.Item
|
|
|
- name="email"
|
|
|
- label="邮箱"
|
|
|
- rules={[{ required: true, type: 'email', message: '请输入有效邮箱' }]}
|
|
|
- >
|
|
|
- <Input placeholder="请输入邮箱" />
|
|
|
- </Form.Item>
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="email"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>邮箱 *</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input type="email" placeholder="请输入邮箱" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
|
|
|
- <Form.Item
|
|
|
- name="phone"
|
|
|
- label="手机号"
|
|
|
- rules={[{ required: true, message: '请输入手机号' }]}
|
|
|
- >
|
|
|
- <Input placeholder="请输入手机号" />
|
|
|
- </Form.Item>
|
|
|
-
|
|
|
- {/* 银龄档案信息 */}
|
|
|
- <Form.Item name="realName" label="真实姓名">
|
|
|
- <Input placeholder="请输入真实姓名" />
|
|
|
- </Form.Item>
|
|
|
-
|
|
|
- <Form.Item name="gender" label="性别">
|
|
|
- <Select placeholder="请选择性别">
|
|
|
- <Option value={1}>男</Option>
|
|
|
- <Option value={2}>女</Option>
|
|
|
- <Option value={3}>其他</Option>
|
|
|
- </Select>
|
|
|
- </Form.Item>
|
|
|
-
|
|
|
- <Form.Item name="age" label="年龄">
|
|
|
- <Input type="number" placeholder="请输入年龄" />
|
|
|
- </Form.Item>
|
|
|
-
|
|
|
- <Form.Item name="organization" label="所属机构">
|
|
|
- <Input placeholder="请输入所属机构" />
|
|
|
- </Form.Item>
|
|
|
-
|
|
|
- <Form.Item name="personalIntro" label="个人简介">
|
|
|
- <Input.TextArea rows={4} placeholder="请简单介绍自己" maxLength={500} />
|
|
|
- </Form.Item>
|
|
|
-
|
|
|
- <Form.Item name="personalSkills" label="个人技能">
|
|
|
- <Input.TextArea rows={4} placeholder="请输入个人技能" maxLength={500} />
|
|
|
- </Form.Item>
|
|
|
-
|
|
|
- <Form.Item name="personalExperience" label="个人经历">
|
|
|
- <Input.TextArea rows={4} placeholder="请输入个人经历" maxLength={500} />
|
|
|
- </Form.Item>
|
|
|
-
|
|
|
- <Form.Item>
|
|
|
- <Button type="primary" htmlType="submit" loading={loading} block>
|
|
|
- 保存修改
|
|
|
- </Button>
|
|
|
- </Form.Item>
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="phone"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>手机号 *</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入手机号" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 银龄档案信息 */}
|
|
|
+ <Card>
|
|
|
+ <CardHeader className="pb-3">
|
|
|
+ <CardTitle className="text-base flex items-center gap-2">
|
|
|
+ <Award className="h-4 w-4" />
|
|
|
+ 银龄档案信息
|
|
|
+ </CardTitle>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent className="space-y-4">
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="realName"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>真实姓名 *</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入真实姓名" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <div className="grid grid-cols-2 gap-4">
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="age"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>年龄 *</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input type="number" placeholder="请输入年龄" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="gender"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>性别 *</FormLabel>
|
|
|
+ <Select onValueChange={(value) => field.onChange(Number(value))} value={field.value.toString()}>
|
|
|
+ <FormControl>
|
|
|
+ <SelectTrigger>
|
|
|
+ <SelectValue placeholder="请选择性别" />
|
|
|
+ </SelectTrigger>
|
|
|
+ </FormControl>
|
|
|
+ <SelectContent>
|
|
|
+ <SelectItem value="1">男</SelectItem>
|
|
|
+ <SelectItem value="2">女</SelectItem>
|
|
|
+ <SelectItem value="3">其他</SelectItem>
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="organization"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>所属机构</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入所属机构" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>如:社区服务中心、学校等</FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 个人简介 */}
|
|
|
+ <Card>
|
|
|
+ <CardHeader className="pb-3">
|
|
|
+ <CardTitle className="text-base flex items-center gap-2">
|
|
|
+ <FileText className="h-4 w-4" />
|
|
|
+ 个人简介
|
|
|
+ </CardTitle>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent className="space-y-4">
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="personalIntro"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>个人简介</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Textarea
|
|
|
+ placeholder="请简单介绍自己,例如:退休教师,热爱教育事业..."
|
|
|
+ className="resize-none"
|
|
|
+ rows={3}
|
|
|
+ {...field}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>简要介绍您的背景和兴趣</FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="personalSkills"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>个人技能</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Textarea
|
|
|
+ placeholder="请输入个人技能,例如:书法、绘画、音乐教学..."
|
|
|
+ className="resize-none"
|
|
|
+ rows={3}
|
|
|
+ {...field}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>您擅长或可以分享的技能</FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="personalExperience"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>个人经历</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Textarea
|
|
|
+ placeholder="请输入个人经历,例如:从事教育工作40年,经验丰富..."
|
|
|
+ className="resize-none"
|
|
|
+ rows={3}
|
|
|
+ {...field}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>您的工作或生活经历</FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 保存按钮 */}
|
|
|
+ <div className="pt-4">
|
|
|
+ <Button
|
|
|
+ type="submit"
|
|
|
+ className="w-full"
|
|
|
+ size="lg"
|
|
|
+ disabled={loading}
|
|
|
+ >
|
|
|
+ {loading ? '保存中...' : '保存修改'}
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
</Form>
|
|
|
</div>
|
|
|
</div>
|