跳到主要内容

表单页:新增与修改数据

列表能看、详情能改,接下来自然就是新建了。这篇文章用 SDKcreateupdate API 实现表单功能,包括表单状态管理、前端校验、提交和错误反馈。

本节你将学到
  • 使用 create API 创建新数据
  • 使用 update API 修改数据
  • 表单状态管理和校验
  • 错误处理和用户反馈

需求

实现一个客户新建表单:输入姓名、电话、公司、状态,前端校验通过后提交,成功返回列表。

最终效果:

新建数据页面


前置条件

信息
  1. 已完成 客户列表页面
  2. 已配置 React Router

实现步骤

步骤 1:创建表单组件

// src/pages/data-form.tsx

/**
* Title: 新建数据
*/
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { lovrabetClient } from "../api/client";

interface FormData {
name: string;
phone: string;
company: string;
status: "potential" | "dealed" | "lost";
}

const initialForm: FormData = {
name: "",
phone: "",
company: "",
status: "potential",
};

export default function DataForm() {
const navigate = useNavigate();
const [form, setForm] = useState<FormData>(initialForm);
const [errors, setErrors] = useState<Partial<Record<keyof FormData, string>>>(
{}
);
const [submitting, setSubmitting] = useState(false);

const MODEL_NAME = "dataset_customers"; // 标准方式,推荐
// const MODEL_NAME = "customers"; // 别名方式

// 表单校验
const validate = (): boolean => {
const newErrors: Partial<Record<keyof FormData, string>> = {};

if (!form.name.trim()) {
newErrors.name = "姓名不能为空";
}

if (!form.phone.trim()) {
newErrors.phone = "电话不能为空";
} else if (!/^1[3-9]\d{9}$/.test(form.phone)) {
newErrors.phone = "请输入正确的手机号";
}

if (!form.status) {
newErrors.status = "请选择状态";
}

setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};

// 提交表单
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();

if (!validate()) {
return;
}

setSubmitting(true);
try {
const result = await lovrabetClient.models[MODEL_NAME].create({
name: form.name.trim(),
phone: form.phone.trim(),
company: form.company.trim(),
status: form.status,
});

console.log("创建成功:", result);
alert("创建成功");
navigate("/data"); // 返回列表页
} catch (error) {
console.error("创建失败:", error);
alert("创建失败,请重试");
} finally {
setSubmitting(false);
}
};

// 取消
const handleCancel = () => {
if (form.name || form.phone || form.company) {
if (!confirm("确定要取消吗?已填写的内容将会丢失。")) {
return;
}
}
navigate("/data");
};

return (
<div className="data-form">
<div className="header">
<h1>新建数据</h1>
</div>

<form onSubmit={handleSubmit}>
<div className="form-group">
<label>
姓名 <span className="required">*</span>
</label>
<input
type="text"
value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })}
placeholder="请输入姓名"
disabled={submitting}
/>
{errors.name && <span className="error">{errors.name}</span>}
</div>

<div className="form-group">
<label>
电话 <span className="required">*</span>
</label>
<input
type="text"
value={form.phone}
onChange={(e) => setForm({ ...form, phone: e.target.value })}
placeholder="请输入手机号"
disabled={submitting}
/>
{errors.phone && <span className="error">{errors.phone}</span>}
</div>

<div className="form-group">
<label>公司</label>
<input
type="text"
value={form.company}
onChange={(e) => setForm({ ...form, company: e.target.value })}
placeholder="请输入公司名称"
disabled={submitting}
/>
</div>

<div className="form-group">
<label>
状态 <span className="required">*</span>
</label>
<select
value={form.status}
onChange={(e) =>
setForm({ ...form, status: e.target.value as any })
}
disabled={submitting}
>
<option value="potential">潜在</option>
<option value="dealed">成交</option>
<option value="lost">流失</option>
</select>
{errors.status && <span className="error">{errors.status}</span>}
</div>

<div className="actions">
<button type="button" onClick={handleCancel} disabled={submitting}>
取消
</button>
<button type="submit" disabled={submitting}>
{submitting ? "提交中..." : "保存"}
</button>
</div>
</form>
</div>
);
}

步骤 2:添加路由配置

// src/App.tsx
import { BrowserRouter, Routes, Route } from "react-router-dom";
import DataList from "./pages/data-list";
import DataDetail from "./pages/data-detail";
import DataForm from "./pages/data-form";

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/data" element={<DataList />} />
<Route path="/data/new" element={<DataForm />} />
<Route path="/data/:id" element={<DataDetail />} />
</Routes>
</BrowserRouter>
);
}

步骤 3:列表页添加新建按钮

// src/pages/data-list.tsx
import { useNavigate } from "react-router-dom";

export default function DataList() {
const navigate = useNavigate();

return (
<div className="data-list">
<div className="header">
<h1>数据列表</h1>
<button onClick={() => navigate("/data/new")}>+ 新建</button>
</div>
{/* ... 其他内容 */}
</div>
);
}

知识点整理

create API

创建新记录:

// 基础用法
const result = await lovrabetClient.models.dataset_xxx.create({
name: "张三",
phone: "13800138000",
status: "potential",
});

// 返回创建后的完整数据
console.log(result.id); // 新创建记录的 ID
console.log(result.create_time); // 创建时间
参数类型说明
dataRecord<string, any>要创建的数据对象

返回值:返回创建后的完整记录数据(包含自动生成的字段如 idcreate_time 等)。

必填字段不能省略,字段名要和数据集字段名一致。OpenAPI 模式和 WebAPI 模式都支持(v1.1.14+)。


表单校验模式

提交时校验

const validate = (): boolean => {
const newErrors: Record<string, string> = {};

if (!form.name.trim()) {
newErrors.name = "姓名不能为空";
}

if (!form.phone.trim()) {
newErrors.phone = "电话不能为空";
} else if (!/^1[3-9]\d{9}$/.test(form.phone)) {
newErrors.phone = "请输入正确的手机号";
}

setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!validate()) return;
// 提交逻辑...
};

实时校验(输入时即时反馈)

const [errors, setErrors] = useState<Record<string, string>>({});

const handleFieldChange = (field: string, value: string) => {
setForm({ ...form, [field]: value });

// 实时清除错误
if (errors[field]) {
setErrors({ ...errors, [field]: "" });
}
};

const handleFieldBlur = (field: string) => {
const newErrors = { ...errors };

if (field === "name" && !form.name.trim()) {
newErrors.name = "姓名不能为空";
}

if (field === "phone") {
if (!form.phone.trim()) {
newErrors.phone = "电话不能为空";
} else if (!/^1[3-9]\d{9}$/.test(form.phone)) {
newErrors.phone = "请输入正确的手机号";
}
}

setErrors(newErrors);
};

常见校验规则

必填校验

if (!value || !value.trim()) {
return "该字段不能为空";
}

长度校验

if (value.length < 2) {
return "至少需要2个字符";
}
if (value.length > 50) {
return "不能超过50个字符";
}

手机号校验

// 简单校验
if (!/^1\d{10}$/.test(value)) {
return "请输入正确的手机号";
}

// 严格校验(运营商号段)
if (!/^1[3-9]\d{9}$/.test(value)) {
return "请输入正确的手机号";
}

邮箱校验

if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
return "请输入正确的邮箱地址";
}

数字范围校验

const num = Number(value);
if (isNaN(num)) {
return "请输入有效的数字";
}
if (num < 0 || num > 100) {
return "请输入0-100之间的数字";
}

几个好的做法

使用受控组件

// 推荐:受控组件
<input
type="text"
value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })}
/>

// 不推荐:非受控组件
<input type="text" defaultValue={form.name} ref={nameRef} />

trim 处理输入

await lovrabetClient.models.dataset_xxx.create({
name: form.name.trim(), // 去除首尾空格
phone: form.phone.trim(), // 去除首尾空格
company: form.company.trim(), // 去除首尾空格
});

提交中禁用表单

<button type="submit" disabled={submitting}>
{submitting ? "提交中..." : "保存"}
</button>

<input disabled={submitting} />

取消时确认

const handleCancel = () => {
// 检查是否有已填写的内容
const hasContent = Object.values(form).some((v) => v && v.trim());

if (hasContent && !confirm("确定要取消吗?已填写的内容将会丢失。")) {
return;
}

navigate("/data");
};

常见问题

Q: 创建成功后如何获取新数据的 ID?

create 返回值包含创建后的完整数据:

const result = await lovrabetClient.models.dataset_xxx.create({
name: "张三",
phone: "13800138000",
});

console.log(result.id); // 新创建的 ID
console.log(result.create_time); // 创建时间

Q: 必填字段校验在哪里做?

建议两层校验:前端校验提升用户体验(立即反馈),后端通过数据集配置的"必填"设置确保数据完整性。

// 前端校验
if (!form.name.trim()) {
setErrors({ name: "姓名不能为空" });
return;
}

// 提交(后端也会校验)
await lovrabetClient.models.dataset_xxx.create(form);

Q: 如何处理枚举字段?

使用 select 下拉框:

<select
value={form.status}
onChange={(e) => setForm({ ...form, status: e.target.value as any })}
>
<option value="potential">潜在</option>
<option value="dealed">成交</option>
<option value="lost">流失</option>
</select>

枚举值需要与数据集配置中的枚举值完全一致。

Q: 如何实现多步骤表单?

用状态管理当前步骤:

const [step, setStep] = useState(1);
const [formData, setFormData] = useState({});

const nextStep = () => {
// 校验当前步骤
if (!validateStep(step)) return;
setStep(step + 1);
};

const prevStep = () => {
setStep(step - 1);
};

// 最后一步提交
const handleSubmit = async () => {
await lovrabetClient.models.dataset_xxx.create(formData);
navigate("/data");
};

本节小结

恭喜你完成了新增与编辑功能!你学会了:

知识点说明
create API创建新记录,返回包含 ID 的完整数据
update API更新记录,需要传入 ID
表单校验前端校验提升用户体验
错误处理try-catch 捕获异常,友好提示用户
最佳实践
  • 前端校验做基础格式检查(如手机号格式)
  • 业务规则校验放在后端(如唯一性检查)
  • 始终使用 try-catch 处理异常
  • 提交时禁用按钮防止重复提交

下一步

相关阅读

核心文档

进阶主题


难度等级:L1 | 预计耗时:30 分钟