|
@@ -0,0 +1,372 @@
|
|
|
+<template>
|
|
|
+ <div class="parameter-form">
|
|
|
+ <!-- 新增参数组按钮 -->
|
|
|
+ <div class="body-toolbar">
|
|
|
+ <el-button
|
|
|
+ circle
|
|
|
+ icon="el-icon-plus"
|
|
|
+ @click="addParamGroup"
|
|
|
+ class="add-btn"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 动态参数组折叠面板 -->
|
|
|
+ <el-collapse :gutter="20">
|
|
|
+ <el-collapse-item
|
|
|
+ v-for="(group, groupIndex) in paramGroups"
|
|
|
+ :key="groupIndex"
|
|
|
+ :title="'参数组 ' + (groupIndex + 1)"
|
|
|
+ :name="groupIndex"
|
|
|
+ >
|
|
|
+ <el-form
|
|
|
+ :model="group.formData"
|
|
|
+ :rules="group.rules"
|
|
|
+ ref="groupFormRef"
|
|
|
+ label-width="120px"
|
|
|
+ >
|
|
|
+ <!-- 循环生成参数输入项 -->
|
|
|
+ <el-form-item
|
|
|
+ v-for="param in group.configData"
|
|
|
+ :key="param.paramId"
|
|
|
+ :label="param.paramChineseName || param.paramName"
|
|
|
+ :prop="param.paramName"
|
|
|
+ >
|
|
|
+ <!-- 文件类型处理 -->
|
|
|
+ <!-- 文件类型处理 -->
|
|
|
+ <template v-if="param.paramType === 'file'">
|
|
|
+ <file-upload v-model="group.formData[param.paramName]" :limit="1"></file-upload>
|
|
|
+<!-- <input-->
|
|
|
+<!-- type="file"-->
|
|
|
+<!-- :id="`file-input-${param.paramName}-${groupIndex}`"-->
|
|
|
+<!-- style="display: none"-->
|
|
|
+<!-- @change="handleFileChange($event, param.paramName, groupIndex)"-->
|
|
|
+<!-- />-->
|
|
|
+<!-- <div class="file-container">-->
|
|
|
+<!-- <el-button-->
|
|
|
+<!-- size="small"-->
|
|
|
+<!-- type="primary"-->
|
|
|
+<!-- @click="triggerFileSelect(`file-input-${param.paramName}-${groupIndex}`)"-->
|
|
|
+<!-- >-->
|
|
|
+<!-- {{ group.formData[param.paramName] && group.formData[param.paramName].name || '选择文件' }}-->
|
|
|
+<!-- </el-button>-->
|
|
|
+<!-- <!– 文件列表 –>-->
|
|
|
+<!-- <div class="file-list" v-if="group.formData[param.paramName]">-->
|
|
|
+<!-- <div class="file-item">-->
|
|
|
+<!-- <span>{{ group.formData[param.paramName].name }}</span>-->
|
|
|
+<!-- <el-button-->
|
|
|
+<!-- size="mini"-->
|
|
|
+<!-- type="danger"-->
|
|
|
+<!-- @click="removeFile(param.paramName, groupIndex)"-->
|
|
|
+<!-- >-->
|
|
|
+<!-- 移除-->
|
|
|
+<!-- </el-button>-->
|
|
|
+<!-- </div>-->
|
|
|
+<!-- </div>-->
|
|
|
+<!-- </div>-->
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 字符串类型 -->
|
|
|
+ <template v-else-if="param.paramType === 'string' || !param.paramType">
|
|
|
+ <el-input
|
|
|
+ size="small"
|
|
|
+ v-model="group.formData[param.paramName]"
|
|
|
+ :placeholder="param.paramDescription"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 数字类型 -->
|
|
|
+ <template v-else-if="param.paramType === 'number'">
|
|
|
+ <el-input-number
|
|
|
+ :precision="param.precision || 2"
|
|
|
+ size="small"
|
|
|
+ v-model="group.formData[param.paramName]"
|
|
|
+ :min="param.min || 0"
|
|
|
+ :max="param.max || Infinity"
|
|
|
+ :placeholder="param.paramDescription"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 布尔类型 -->
|
|
|
+ <template v-else-if="param.paramType === 'boolean'">
|
|
|
+ <el-switch
|
|
|
+ v-model="group.formData[param.paramName]"
|
|
|
+ active-text="开启"
|
|
|
+ inactive-text="关闭"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 下拉选择类型 -->
|
|
|
+ <template v-else-if="param.paramType === 'select'">
|
|
|
+ <el-select
|
|
|
+ size="small"
|
|
|
+ v-model="group.formData[param.paramName]"
|
|
|
+ :placeholder="param.paramDescription"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="option in parseOptions(param.paramOptions)"
|
|
|
+ :key="option.value"
|
|
|
+ :label="option.label"
|
|
|
+ :value="option.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 日期类型 -->
|
|
|
+ <template v-else-if="param.paramType === 'date'">
|
|
|
+ <el-date-picker
|
|
|
+ size="small"
|
|
|
+ v-model="group.formData[param.paramName]"
|
|
|
+ type="date"
|
|
|
+ placeholder="选择日期"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 其他类型默认处理 -->
|
|
|
+ <template v-else>
|
|
|
+ <el-input
|
|
|
+ size="small"
|
|
|
+ v-model="group.formData[param.paramName]"
|
|
|
+ :placeholder="param.paramDescription"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-collapse-item>
|
|
|
+ </el-collapse>
|
|
|
+
|
|
|
+ <!-- 操作按钮 -->
|
|
|
+ <el-form-item class="button-group">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="submitAllForms"
|
|
|
+ :loading="isSubmitting"
|
|
|
+ >
|
|
|
+ 提交参数
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ @click="resetAllForms"
|
|
|
+ style="margin-left: 10px"
|
|
|
+ >
|
|
|
+ 重置表单
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ name: 'DynamicParamForm',
|
|
|
+ props: {
|
|
|
+ // 基础参数配置(必填)
|
|
|
+ configData: {
|
|
|
+ type: Array,
|
|
|
+ required: true,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
+ // 初始参数组数据(可选)
|
|
|
+ initialGroups: {
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ paramGroups: [], // 存储所有参数组
|
|
|
+ isSubmitting: false // 提交加载状态
|
|
|
+ };
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.initParamGroups();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 初始化参数组
|
|
|
+ initParamGroups() {
|
|
|
+ this.paramGroups = this.initialGroups.map(initialGroup => ({
|
|
|
+ configData: this.configData,
|
|
|
+ formData: {
|
|
|
+ ...initialGroup.formData,
|
|
|
+ // 初始化文件类型参数为空数组
|
|
|
+ ...this.configData.reduce((acc, param) => {
|
|
|
+ if (param.paramType === 'file') acc[param.paramName] = [];
|
|
|
+ return acc;
|
|
|
+ }, {})
|
|
|
+ },
|
|
|
+ rules: this.generateRules(this.configData)
|
|
|
+ }));
|
|
|
+ },
|
|
|
+
|
|
|
+ // 生成校验规则
|
|
|
+ generateRules(params) {
|
|
|
+ const rules = {};
|
|
|
+ params.forEach(param => {
|
|
|
+ if (param.isRequired) {
|
|
|
+ rules[param.paramName] = [{
|
|
|
+ required: true,
|
|
|
+ message: `请填写${param.paramChineseName || param.paramName}`,
|
|
|
+ trigger: 'blur'
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return rules;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 新增参数组
|
|
|
+ addParamGroup() {
|
|
|
+ this.paramGroups.push({
|
|
|
+ configData: this.configData,
|
|
|
+ formData: this.configData.reduce((acc, param) => {
|
|
|
+ // 优先使用 paramDefaultValue,若不存在则用空值(根据类型处理)
|
|
|
+ if (param.paramType === 'file') {
|
|
|
+ acc[param.paramName] = []; // 文件类型仍保持空数组(非文件类型走下面逻辑)
|
|
|
+ } else {
|
|
|
+ // 非文件类型:使用 paramDefaultValue 或默认空值(字符串/数字等)
|
|
|
+ acc[param.paramName] = param.paramDefaultValue !== undefined
|
|
|
+ ? param.paramDefaultValue // 存在 paramDefaultValue 时使用
|
|
|
+ : param.defaultValue || ''; // 不存在时使用原逻辑(defaultValue 或空字符串)
|
|
|
+ }
|
|
|
+ return acc;
|
|
|
+ }, {}),
|
|
|
+ rules: this.generateRules(this.configData)
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 触发文件选择
|
|
|
+ triggerFileSelect(inputId) {
|
|
|
+ document.getElementById(inputId).click();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 处理文件选择
|
|
|
+ handleFileChange(event, paramName,groupIndex) {
|
|
|
+ const file = event.target.files[0];
|
|
|
+ this.paramGroups[groupIndex].formData[paramName] = file;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 移除文件
|
|
|
+ removeFile(paramName, groupIndex, fileIndex) {
|
|
|
+ this.paramGroups[groupIndex].formData[paramName].splice(fileIndex, 1);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 解析下拉选项
|
|
|
+ parseOptions(optionsStr) {
|
|
|
+ if (!optionsStr) return [];
|
|
|
+ try {
|
|
|
+ return JSON.parse(optionsStr);
|
|
|
+ } catch (e) {
|
|
|
+ return optionsStr.split(',').map(item => {
|
|
|
+ const [value, label] = item.split(':');
|
|
|
+ return { value: value.trim(), label: label?.trim() || value.trim() };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 提交所有表单
|
|
|
+ submitAllForms() {
|
|
|
+ this.isSubmitting = true;
|
|
|
+ const allValid = this.paramGroups.every((group, groupIndex) => {
|
|
|
+ const formRef = this.$refs.groupFormRef[group.name];
|
|
|
+ return formRef ? formRef.validate() : true;
|
|
|
+ });
|
|
|
+
|
|
|
+ if (allValid) {
|
|
|
+ // 整理数据格式:[{paramName: value}, {paramName: value}]
|
|
|
+ const result = this.paramGroups.map(group => {
|
|
|
+ return this.configData.reduce((acc, param) => {
|
|
|
+ acc[param.paramName] = group.formData[param.paramName];
|
|
|
+ return acc;
|
|
|
+ }, {});
|
|
|
+ });
|
|
|
+
|
|
|
+ this.$emit('submit', result);
|
|
|
+ this.isSubmitting = false;
|
|
|
+ } else {
|
|
|
+ this.isSubmitting = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 重置表单
|
|
|
+ resetAllForms() {
|
|
|
+ this.paramGroups = this.initialGroups.length
|
|
|
+ ? this.initialGroups.map(group => ({
|
|
|
+ ...group,
|
|
|
+ formData: { ...group.formData },
|
|
|
+ rules: this.generateRules(this.configData)
|
|
|
+ }))
|
|
|
+ : [this.getDefaultGroup()];
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取默认参数组
|
|
|
+ getDefaultGroup() {
|
|
|
+ return {
|
|
|
+ configData: this.configData,
|
|
|
+ formData: this.configData.reduce((acc, param) => {
|
|
|
+ if (param.paramType === 'file') acc[param.paramName] = [];
|
|
|
+ else acc[param.paramName] = param.defaultValue || '';
|
|
|
+ return acc;
|
|
|
+ }, {}),
|
|
|
+ rules: this.generateRules(this.configData)
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.el-collapse,
|
|
|
+.el-collapse /deep/ .el-collapse-item__header,
|
|
|
+.el-collapse /deep/ .el-collapse-item__content {
|
|
|
+ background-color: #0b333f;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+.parameter-form {
|
|
|
+ padding: 20px;
|
|
|
+ background-color: #0b333f;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.body-toolbar {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.add-btn {
|
|
|
+ background-color: #0b333f;
|
|
|
+ color: white;
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.el-collapse-item {
|
|
|
+ margin-bottom: 15px;
|
|
|
+ background-color: #0b333f;
|
|
|
+ color: white;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
|
|
|
+}
|
|
|
+
|
|
|
+.file-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.file-list {
|
|
|
+ margin-left: 15px;
|
|
|
+ flex-grow: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.file-item {
|
|
|
+ display: inline-block;
|
|
|
+ margin-right: 10px;
|
|
|
+ margin-top: 5px;
|
|
|
+ padding: 4px 8px;
|
|
|
+ border-radius: 4px;
|
|
|
+ background-color: #0b333f;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.button-group {
|
|
|
+ margin-top: 30px;
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+</style>
|