feat:路由配置、数据配置加表单校验

This commit is contained in:
dongzp 2026-09-08 11:27:53 +08:00
parent 07a6af6fd9
commit b8aee98468

View File

@ -392,7 +392,12 @@
:visible.sync="routeDialog.visible"
width="760px"
>
<el-form :model="routeDialog.form" label-width="180px">
<el-form
ref="routeForm"
:model="routeDialog.form"
:rules="routeFormRules"
label-width="180px"
>
<el-form-item label="预置接口">
<el-select
v-model="routeDialog.form.selectedApiRoute"
@ -410,21 +415,21 @@
></el-option>
</el-select>
</el-form-item>
<el-form-item label="接口名称">
<el-form-item label="接口名称" prop="apiName">
<el-input
v-model="routeDialog.form.apiName"
placeholder="例如:获取用户信息"
:disabled="isApiListLocked()"
></el-input>
</el-form-item>
<el-form-item label="请求路径">
<el-form-item label="请求路径" prop="route">
<el-input
v-model="routeDialog.form.route"
placeholder="/api/new/mock"
:disabled="isApiListLocked()"
></el-input>
</el-form-item>
<el-form-item label="Mock 文件">
<el-form-item label="Mock 文件" prop="filePath">
<div style="display:flex;gap:8px;">
<el-select
v-model="routeDialog.form.groupFilter"
@ -457,7 +462,7 @@
</el-select>
</div>
</el-form-item>
<el-form-item label="返回状态码">
<el-form-item label="返回状态码" prop="statusCode">
<el-select
v-model="routeDialog.form.statusCode"
filterable
@ -475,7 +480,7 @@
></el-option>
</el-select>
</el-form-item>
<el-form-item label="是否启用 Mock">
<el-form-item label="是否启用 Mock" prop="enabled" required>
<el-switch v-model="routeDialog.form.enabled"></el-switch>
</el-form-item>
</el-form>
@ -494,20 +499,25 @@
:fullscreen="mockFileDialog.fullscreen"
:top="mockFileDialog.fullscreen ? '0' : '15vh'"
>
<el-form :model="mockFileDialog.form" label-width="180px">
<el-form-item label="别名">
<el-form
ref="mockFileForm"
:model="mockFileDialog.form"
:rules="mockFileFormRules"
label-width="180px"
>
<el-form-item label="别名" prop="alias">
<el-input
v-model="mockFileDialog.form.alias"
placeholder="可选:用于展示,支持任意文本"
placeholder="必填:用于展示,支持任意文本"
></el-input>
</el-form-item>
<el-form-item label="分组">
<el-select v-model="mockFileDialog.form.groupId" clearable placeholder="选择分组" style="width: 100%;">
<el-form-item label="分组" prop="groupId" required>
<el-select v-model="mockFileDialog.form.groupId" placeholder="请选择分组" style="width: 100%;">
<el-option label="无分组" :value="null"></el-option>
<el-option v-for="g in mockGroups" :key="g.id" :label="g.name" :value="g.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="Mock 文件路径">
<el-form-item label="Mock 文件路径" prop="fileName">
<el-input
v-model="mockFileDialog.form.fileName"
:placeholder="mockFileDialog.mode === 'edit' ? '' : '根据别名自动生成:别名_随机串.json'"
@ -516,7 +526,7 @@
<template slot="prepend">mock/</template>
</el-input>
</el-form-item>
<el-form-item label="文件内容">
<el-form-item label="文件内容" prop="content">
<div style="display:flex;justify-content:flex-end;margin-bottom:6px;">
<el-button size="mini" type="text" @click="mockFileDialog.fullscreen = !mockFileDialog.fullscreen">
<i :class="mockFileDialog.fullscreen ? 'el-icon-copy-document' : 'el-icon-full-screen'"></i>
@ -525,6 +535,7 @@
</div>
<el-input
type="textarea"
class="mock-content-textarea"
:autosize="mockFileDialog.fullscreen ? { minRows: 28, maxRows: 28 } : { minRows: 12, maxRows: 12 }"
v-model="mockFileDialog.form.content"
placeholder='{"code":0,"message":"ok"}'
@ -864,6 +875,77 @@
content: "",
},
},
routeFormRules: {
apiName: [
{ required: true, message: "请输入接口名称", trigger: "blur" },
],
route: [
{ required: true, message: "请输入请求路径", trigger: "blur" },
{
validator: function (rule, value, callback) {
if (value && String(value).charAt(0) !== "/") {
callback(new Error("请求路径必须以 / 开头"));
} else {
callback();
}
},
trigger: "blur",
},
],
filePath: [
{ required: true, message: "请选择 Mock 文件", trigger: "change" },
],
statusCode: [
{
required: true,
validator: function (rule, value, callback) {
if (value === null || value === undefined || value === "") {
callback(new Error("请选择或输入返回状态码"));
} else {
callback();
}
},
trigger: "change",
},
],
enabled: [
{
required: true,
validator: function (rule, value, callback) {
if (typeof value !== "boolean") {
callback(new Error("请设置是否启用 Mock"));
} else {
callback();
}
},
trigger: "change",
},
],
},
mockFileFormRules: {
alias: [
{ required: true, message: "请输入别名", trigger: "blur" },
],
groupId: [
{
validator: function (rule, value, callback) {
// null 表示「无分组」,视为已选择;仅未赋值时失败
if (value === undefined || value === "") {
callback(new Error("请选择分组"));
} else {
callback();
}
},
trigger: "change",
},
],
fileName: [
{ required: true, message: "文件路径不能为空", trigger: "change" },
],
content: [
{ required: true, message: "请输入文件内容", trigger: "blur" },
],
},
};
},
watch: {
@ -1266,6 +1348,9 @@
this.routeDialog.editingIndex = -1;
this.routeDialog.form = this.getDefaultRouteForm();
this.routeDialog.visible = true;
this.$nextTick(function () {
if (this.$refs.routeForm) this.$refs.routeForm.clearValidate();
});
},
openRouteDialogForEdit: function (index) {
var actualIndex =
@ -1289,6 +1374,9 @@
groupFilter: groupFilter,
};
this.routeDialog.visible = true;
this.$nextTick(function () {
if (this.$refs.routeForm) this.$refs.routeForm.clearValidate();
});
},
sanitizeMockFileAlias: function (alias) {
var text = String(alias || "").trim();
@ -1314,6 +1402,9 @@
this.mockFileDialog.fullscreen = false;
this.mockFileDialog.form = this.getDefaultMockFileForm();
this.mockFileDialog.visible = true;
this.$nextTick(function () {
if (this.$refs.mockFileForm) this.$refs.mockFileForm.clearValidate();
});
},
openMockFileDialogForEdit: function (item) {
var full = item.filePath || "";
@ -1327,6 +1418,9 @@
groupId: item.groupId || null,
};
this.mockFileDialog.visible = true;
this.$nextTick(function () {
if (this.$refs.mockFileForm) this.$refs.mockFileForm.clearValidate();
});
},
openMockFileFromRoute: function (routeRow) {
var filePath = routeRow.filePath || "";
@ -1356,6 +1450,9 @@
groupId: item.groupId || null,
};
this.mockFileDialog.visible = true;
this.$nextTick(function () {
if (this.$refs.mockFileForm) this.$refs.mockFileForm.clearValidate();
});
},
loadMockFiles: async function () {
try {
@ -1371,12 +1468,20 @@
this.$message.error("加载 mock 文件失败: " + err.message);
}
},
submitMockFileDialog: async function () {
var fileName = (this.mockFileDialog.form.fileName || "").trim();
submitMockFileDialog: function () {
var self = this;
if (this.mockFileDialog.mode === "create" || this.mockFileDialog.mode === "copy") {
fileName = this.generateMockFileName(this.mockFileDialog.form.alias);
this.mockFileDialog.form.fileName = fileName;
this.mockFileDialog.form.fileName = this.generateMockFileName(
this.mockFileDialog.form.alias,
);
}
this.$refs.mockFileForm.validate(function (valid) {
if (!valid) return;
self.doSubmitMockFileDialog();
});
},
doSubmitMockFileDialog: async function () {
var fileName = (this.mockFileDialog.form.fileName || "").trim();
if (!fileName) {
this.$message.error("文件名不能为空");
return;
@ -1502,15 +1607,14 @@
this.$message.error("保存配置失败: " + err.message);
}
},
submitRouteDialog: async function () {
if (!this.routeDialog.form.route) {
this.$message.error("请求路径不能为空");
return;
}
if (!this.routeDialog.form.filePath) {
this.$message.error("请选择 Mock 文件");
return;
}
submitRouteDialog: function () {
var self = this;
this.$refs.routeForm.validate(function (valid) {
if (!valid) return;
self.doSubmitRouteDialog();
});
},
doSubmitRouteDialog: async function () {
var payload = Object.assign({}, this.routeDialog.form, {
useExistingFile: true,
statusCode: this.normalizeStatusCode(this.routeDialog.form.statusCode),