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