需求:根据type类型,动态检验abstract是否为必填项

52 阅读3分钟

需求:根据type类型,动态检验abstract是否为必填项

效果图1 image.png

效果图2

579134f6-dd14-427d-85ff-e57bd0381388.png

效果图3

image.png

表单代码如下:

 <el-form-item
                label="Type:"
                prop="articleType"
                :rules="[
                  {
                    required: true,
                    message: 'Please Select Type',
                    trigger: 'change',
                  },
                ]"
              >
                <el-select
                  v-model="formData.articleType"
                  class="m-2"
                  placeholder="Select"
                  filterable
                  style="width: 1000px"
                  @change="handleArticleTypeChange"
                >
                  <el-option
                    v-for="item in typeList"
                    :key="item"
                    :label="item"
                    :value="item"
                  />
                </el-select>
              </el-form-item>
               <el-form-item
                label="Abstract:"
                prop="abstracts"
              >
                <template #label>
                  <span 
                    v-if="isAbstractRequired" 
                    style="color: red; margin-right: 4px;"
                  >*</span>
                  Abstract
                </template>
                <el-input
                  v-model="formData.abstracts"
                  :rows="9"
                  type="textarea"
                  style="width: 1000px"
                />
              </el-form-item>
             

js代码如下:

const handleArticleTypeChange = (value) =>{
  nextTick(() => {
    if (formRef.value) {
      formRef.value.validateField('abstracts');
    }
  });
}

const formDataRules = reactive({
  abstracts: [ { 
      // required: false, 
      validator: checkAbstracts, 
      trigger: ["blur", "change"] 
    }]
})

const checkAbstracts = (rule, value, callback) => {
  const currentType = formData.value.articleType;
  
  // 如果没有选择文章类型,不进行校验
  if (!currentType) {
    callback();
    return;
  }
  
  // 检查当前类型是否为可选摘要类型
  const isOptionalType = optionalTypes.some(type => 
    currentType.toLowerCase().includes(type)
  );
  
  // 如果是可选类型,直接通过校验
  if (isOptionalType) {
    callback();
    return;
  }
  
  // 对于必填类型,检查摘要是否填写
  if (!value || !value.trim()) {
    callback(new Error("Please Input Abstract"));
  } else {
    callback();
  }
};


const optionalTypes = ["editorial", "correspondence", "announcement", "commentary"];

// 判断是否必填
const isAbstractRequired = computed(() => {
  const currentType = formData.value.articleType;
  if (!currentType) return true;
  
  return !optionalTypes.some(type => 
    currentType.toLowerCase().includes(type)
  );
});



补充另一个示例======================================

根据选择的reason来动态控制是否要设置为必填

image.png

image.png

<template>
  <div class="distribute-rewards-modal">
    <el-dialog
      v-model="innerVisible"
      title="Create New Rewards Distribution"
      width="800px"
      :close-on-click-modal="false"
      @close="resetForm"
    >
      <el-form
        :model="form"
        :rules="rules"
        ref="formRef"
        label-width="180px"
      >
        <el-form-item label="Email" prop="email">
          <el-input
            v-model="form.email"
            @blur="autoFillByEmail"
            autocomplete="off"
          />
        </el-form-item>
        <el-form-item label="Name">
          <el-input v-model="form.name" />
        </el-form-item>
        <el-form-item label="Reason" prop="reason">
          <el-select v-model="form.reason" @change="handleReasonChange" style="width: 100%">
            <el-option label="Behavior of sharing" value="Behavior" />
            <el-option v-if="role === 'admin'" label="Other" value="Other" />
          </el-select>
        </el-form-item>
        <el-form-item label="Sharing Channels" prop="sharingChannels">
          <el-select v-model="form.sharingChannels" placeholder="Select" style="width: 100%">
            <el-option
              v-for="item in sharingChannelsList"
              :key="item"
              :label="item"
              :value="item"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="Sharing Type" prop="sharingType">
          <el-select v-model="form.sharingType" placeholder="Select" style="width: 100%">
            <el-option
              v-for="item in sharingTypeList"
              :key="item"
              :label="item"
              :value="item"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="Journal" prop="journal">
          <el-select v-model="form.journal" placeholder="Select" style="width: 100%">
            <el-option
              v-for="item in journalList"
              :key="item"
              :label="item"
              :value="item"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="Paper Title/SI Title" prop="paperTitle">
          <el-input v-model="form.paperTitle" />
        </el-form-item>
        <el-form-item label="Paper /SI ID" prop="paperId">
          <el-input v-model="form.paperId" />
        </el-form-item>
        <el-form-item label="Screenshot" prop="screenshot">
          <PasteUpload v-model="form.screenshot" style="width: 100%" />
        </el-form-item>
        <el-form-item label="Remark" prop="remark">
          <el-input
            v-model="form.remark"
            type="textarea"
            :rows="3"
            maxlength="200"
            show-word-limit
            autocomplete="off"
            placeholder="富文本框,支持粘贴图片"
          />
        </el-form-item>
        <template v-if="form.reason === 'Other'">
          <el-form-item label="Points Distribution" prop="points">
            <el-input-number
              v-model="form.points"
              :min="0"
              :max="100"
              style="width: 180px"
            />
          </el-form-item>
          <el-form-item label="Voucher Distribution($)" prop="voucher">
            <el-input-number
              v-model="form.voucher"
              :min="0"
              :max="100"
              style="width: 180px"
            />
          </el-form-item>
        </template>
        <template v-else>
          <el-form-item label="Rewards Distribution">
            <el-select
              v-model="form.rewardSelect"
              placeholder="Select distribtuion"
              style="width: 100%"
            >
              <el-option
                v-for="item in rewardOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
        </template>
        <div style="display: flex;justify-content: center;">
          <el-button @click="emit('update:visible', false)">Cancel</el-button>
          <el-button type="primary" @click="handleConfirm">Confirm</el-button>
        </div>
      </el-form>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, watch, computed } from "vue";
import PasteUpload from "@/views/promotePlus/components/PasteUpload/index.vue";
import { ElForm, ElMessage } from "element-plus";

const props = defineProps<{ visible: boolean; role: string }>();
const emit = defineEmits<{
  (event: "update:visible", value: boolean): void;
  (
    event: "success",
    payload: { email: string; points: number; voucher: number }
  ): void;
}>();

const innerVisible = ref(props.visible);
watch(
  () => props.visible,
  (v) => (innerVisible.value = v)
);
watch(innerVisible, (v) => emit("update:visible", v));

const sharingChannelsList = ["WeChat", "QQ", "Email", "Twitter", "Facebook"];
const sharingTypeList = ["Featured Papers", "Notification", "News", "Activity"];
const journalList = ["CMC", "BMC", "AMM"];
const rewardOptions = [
  { label: "20 Points + 20 Voucher", value: "20|20" },
  { label: "40 Points + 40 Voucher", value: "40|40" },
];

const formRef = ref<typeof ElForm | null>(null);
const form = reactive({
  email: "",
  name: "",
  reason: "Behavior", // 默认选中Behavior of sharing
  sharingChannels: "",
  sharingType: "",
  journal: "",
  paperTitle: "",
  paperId: "",
  screenshot: "",
  remark: "",
  points: 0,
  voucher: 0,
  rewardSelect: "",
});

function resetForm() {
  Object.assign(form, {
    email: "",
    name: "",
    reason: "Behavior", // reset时默认Behavior
    sharingChannels: "",
    sharingType: "",
    journal: "",
    paperTitle: "",
    paperId: "",
    screenshot: "",
    remark: "",
    points: 0,
    voucher: 0,
    rewardSelect: "",
  });
  formRef.value?.clearValidate();
}

function handleReasonChange(val: string) {
  // 清空与奖励相关内容和所有校验提示
  form.points = 0;
  form.voucher = 0;
  form.rewardSelect = "";
  formRef.value?.clearValidate();  // 切换 reason 时会自动清除所有表单的校验红框和错误提示
}

function autoFillByEmail() {
  // 假数据逻辑(邮箱结尾相同的名字)
  if (!form.email) return;
  const map = {
    "123@qq.com": {
      name: "张三",
      paperTitle: "深度学习方法研究",
      paperId: "10001",
    },
    "test@qq.com": { name: "李四", paperTitle: "AI影响分析", paperId: "10002" },
    "heweny@gmail.com": {
      name: "Weney He",
      paperTitle: "Bridging 2D and 3D Object Detection",
      paperId: "56982",
    },
  };
  const v = map[form.email];
  if (v) {
    form.name = v.name;
    form.paperTitle = v.paperTitle;
    form.paperId = v.paperId;
  } else {
    form.name = "";
    form.paperTitle = "";
    form.paperId = "";
  }
}

const rules = computed(() => {
  return {
    email: [
      { required: true, message: "请输入邮箱", trigger: "blur" },
      { type: "email", message: "邮箱格式不正确", trigger: "blur" },
    ],
    reason: [{ required: true, message: "请选择原因", trigger: "change" }],
    sharingChannels:
      form.reason === "Behavior"
        ? [{ required: true, message: "请选择渠道", trigger: "change" }]
        : [{ required: false, message: "请选择渠道", trigger: "change" }],
    sharingType:
      form.reason === "Behavior"
        ? [{ required: true, message: "请选择类型", trigger: "change" }]
        : [],
    journal:
      form.reason === "Behavior"
        ? [{ required: true, message: "请选择Journal", trigger: "change" }]
        : [],
    paperTitle:
      form.reason === "Behavior"
        ? [{ required: true, message: "请输入标题", trigger: "blur" }]
        : [],
    paperId: form.reason === "Behavior"
        ? [{ required: true, message: "请输入", trigger: "blur" }]
        : [],
    screenshot:
      form.reason === "Behavior"
        ? [{ required: true, message: "请上传截图", trigger: "change" }]
        : [],
    remark: form.reason === "Behavior"
        ? [{ required: true, message: "请输入", trigger: "blur" }]
        : [],
    rewardSelect:
      form.reason === "Behavior"
        ? [{ required: true, message: "请选择奖励发放方式", trigger: "change" }]
        : [],
    points:
      form.reason === "Other"
        ? [
            {
              validator: (rule: any, value: number, callback: any) => {
                if (value < 0 || value > 100) callback("范围0-100");
                else if (form.points === 0 && form.voucher === 0)
                  callback("至少有一项大于0");
                else callback();
              },
              trigger: "change",
            },
          ]
        : [],
    voucher:
      form.reason === "Other"
        ? [
            {
              validator: (rule: any, value: number, callback: any) => {
                if (value < 0 || value > 100) callback("范围0-100");
                else if (form.points === 0 && form.voucher === 0)
                  callback("至少有一项大于0");
                else callback();
              },
              trigger: "change",
            },
          ]
        : [],
  };
});

function handleConfirm() {
  formRef.value?.validate((valid) => {
    if (!valid) return;
    let send;
    if (form.reason === "Other") {
      send = { email: form.email, points: form.points, voucher: form.voucher };
    } else {
      const [p, v] = (form.rewardSelect || "").split("|");
      send = { email: form.email, points: Number(p), voucher: Number(v) };
    }
    emit("success", send);
  });
}
</script>
<style scoped lang="scss">

:deep(.upload-areas){
  margin-bottom: 0!important;
}
:deep(.example-section){
  top:-20px!important;
}

// 弹窗标题样式
.distribute-rewards-modal :deep(.el-dialog__header) {
  background: #f9fafb;
  margin: 0;
  padding: 16px;
  border-bottom: 1px solid #e5e7eb;
}

.distribute-rewards-modal :deep(.el-dialog__body) {
  padding: 24px 24px 15px 24px;
}
</style>