vue3中,渲染动态表单(一)——定义公共样式、使用动态样式class & 抽离el-dialog对话框组件 & 父子传参 & 自定义slot插槽 & vue3中watch写法和computed写法

vue3中,渲染动态表单(一)——定义公共样式、使用动态样式class & 抽离el-dialog对话框组件 & 父子传参 & 自定义slot插槽 & vue3中watch写法和computed写法

效果图

在这里插入图片描述

文件目录

在这里插入图片描述

1、主页面

defineForm.vue

<el-button type="primary" @click="see">生成预览</el-button>
<ExampleInfo v-if="showExample" v-model:dialogVisible="showExample" :info-data="exampleInfo" @emit-confirm="exampleOk" />

<script lang="ts" setup>
import { ref } from 'vue';
import ExampleInfo from './exampleInfo.vue'
    
const showExample = ref(false)
const exampleInfo = ref({})
const see = () => {
    showExample.value = true
    exampleInfo.value = {
        // id: fieldId,
        // typeName: 'add'
    }
}
const exampleOk = () => {
    // tableData.value = []
    // getList()
}
2.1、渲染主页面

exampleInfo.vue

<!--
@Description 申请表管理 - 定义申请表 - 预览
@author wdd
@date 2023/12/14
-->
<template>
    <div>
        <el-dialog title="申请表" v-model="dialogVisible" :close-on-press-escape="false" :close-on-click-modal="false"
            width="80%">
            <div class="content">
                <el-form label-width="110px" ref="formRef" :model="formData">
                    <CustomForm :form-data="formData" :form-item="topicItems">
                        <!-- 插入自定义模块 -->
                        <template #topicName="{ item }">
                            <!-- <el-input v-model="formData.topicName" :disabled="formData.id ? true : false"
                                maxlength="100" placeholder="请输入小组名称" show-word-limit /> -->
                            <el-form-item :class="item.className" :label="item.label" :label-width="110"
                                :prop="item.prop" :rules="item.rules">
                                <el-input v-model="formData.topicName" :disabled="false" maxlength="200"
                                    placeholder="请输入" />
                            </el-form-item>
                        </template>
                        <!-- 编辑自定义样式 -->
                        <template #prjName="{ item }">
                            <el-form-item :class="item.className" :label="item.label" :label-width="110"
                                :prop="item.prop" :rules="item.rules">
                                <el-input v-model="formData.prjName" :disabled="false" maxlength="200"
                                    placeholder="请输入" />
                            </el-form-item>
                        </template>
                        <!-- 插入自定义模块 -->
                        <template #title="{ item }">
                            <div :class="item.className">
                                <div style="font-size:16px;font-weight:700;padding:0 20px 10px 24px;"> 小组成员:</div>
                            </div>
                            <CustomForm :form-data="formData" :form-item="topicItemOld"></CustomForm>
                        </template>
                        <!-- 编辑自定义样式 -->
                        <template #startDate>
                            <el-date-picker v-model="formData.startDate" :clearable="false"
                                :disabled-date="disableStartData" format="YYYY-MM-DD" placeholder="请输入开始时间" type="date"
                                value-format="YYYY-MM-DD" />
                        </template>
                        <!-- 编辑自定义样式 -->
                        <template #endDate>
                            <el-date-picker v-model="formData.endDate" :clearable="false"
                                :disabled-date="disableStartData" format="YYYY-MM-DD" placeholder="请输入结束时间" type="date"
                                value-format="YYYY-MM-DD" />
                        </template>
                        <!-- 插入自定义模块 -->
                        <template #titleA="{ item }">
                            <div :class="item.className">
                                <div style="font-size:16px;font-weight:700;padding:0 20px 10px 24px;"> 项目成员:</div>
                            </div>
                            <CustomForm :form-data="formData" :form-item="topicItemNew"></CustomForm>
                        </template>
                        <!-- 插入自定义模块 -->
                        <template #titleB="{ item }">
                            <div :class="item.className">
                                <div style="font-size:16px;font-weight:700;padding:0 20px 10px 24px;"> 获奖情况:</div>
                            </div>
                            <CustomForm :form-data="formData" :form-item="awardsItemInfo"></CustomForm>
                        </template>
                    </CustomForm>
                </el-form>
            </div>
            <template #footer>
                <span slot="footer" class="dialog-footer">
                    <el-button type="primary" @click="dialogVisible = false">取消</el-button>
                    <el-button type="primary" @click="confirm">确定</el-button>
                </span>
            </template>
        </el-dialog>
    </div>
</template>
<script lang="ts" setup>
import { ref, defineProps, defineEmits, computed, watch } from 'vue';
import CustomForm from './customForm.vue'
import { topicItem, teamItem, infoItem, awardsItem } from './data'
const topicItems = ref(topicItem)
// const topicItemNew = ref(topicItem.slice(3, 8))
const topicItemNew = ref(infoItem)
const topicItemOld = ref(teamItem)
const awardsItemInfo = ref(awardsItem)
const formRef = ref()
const formData = ref({
    // topicName: '',
    // researchContent: '',
    // expectTarget: '',
    // examQuota: '',
    // formulaPercentage: '',
    // //   carryScheme: '',
    // stuAttr: '',
    // email: '',
    // isIntake: '0',
    // cooperMode: ['1'],
    // startDate: '',
    // attachFileArr: [],
    // endDate: '',
})
const props = defineProps({
    infoData: {
        default: null,
        type: Object,
    },
    dialogVisible: {
        type: Boolean,
        default() {
            return false
        },
    },
})
//监听方法
const emit = defineEmits(['emit-confirm', 'update:dialogVisible'])
const dialogVisible = computed({
    get: () => props.dialogVisible,
    set: (val) => emit('update:dialogVisible', val),
})
watch(
    () => props.dialogVisible,
    (newVal) => {
        if (newVal) {
            console.log(props.infoData);
            formInline.value = props.infoData
        }
    }
)
const disableStartData = (time) => {
    return (
        time.getTime() < new Date(formData.value.startDate).getTime() ||
        time.getTime() > new Date(formData.value.endDate).getTime()
    )
}
const confirm = () => {
    console.log(12, formData.value);
    // emit('emit-confirm', formInline.value)
    // emit('update:dialogVisible', false)
}
</script>
<style lang="scss" scoped>
:deep(.el-dialog__body) {
    padding-top: -20px;
    // min-height: 60vh;
    .content {
        // margin-right: 20px;
        width: 100%;
        // margin-top: -20px;
    }
}
</style>
2.2、渲染组件页面

customForm.vue

<!--
@Description 申请表管理 - 定义申请表 - 表单类型
@author wdd
@date 2023/12/18
-->
<template>
  <div class="content">
    <template v-for="item in formItem" :key="item.prop">
      <template v-if="!item.show">
        <el-form-item
          :key="item.prop"
          :class="item.className"
          :label="item.label"
          :label-width="item.labelWidth"
          :prop="item.prop"
          :rules="item.rules"
        >
          <template v-if="!item.slot">
            <!-- input  输入框类型 -->
            <el-input
              v-if="item.type === 'input'"
              v-model="dataSource[`${item.prop}`]"
              :disabled="item.disabled || isEditDisabled"
              :maxlength="item.maxLength"
              :placeholder="item.placeholder"
              :show-word-limit="item.limit"
            />
            <!-- number  数字类型 -->
            <el-input-number
              v-if="item.type === 'number'"
              v-model="dataSource[`${item.prop}`]"
              :controls="false"
              :disabled="item.disabled || isEditDisabled"
              :max="item.max || 99999999"
              :min="item.min || 0"
              style="width:100%"
              :placeholder="item.placeholder"
              :precision="item.precision"
              @keydown="
                (val) => (val.key === 'e' ? (val.returnValue = false) : true)
              "
            />
            <!-- textarea  多行文本类型 -->
            <el-input
              v-if="item.type === 'textarea'"
              v-model="dataSource[`${item.prop}`]"
              :disabled="item.disabled || isEditDisabled"
              :maxlength="item.maxLength || 1000"
              :placeholder="item.placeholder"
              :rows="item.rows || 2"
              show-word-limit
              type="textarea"
            />
            <!-- date  日期类型 -->
            <el-date-picker
              v-if="item.type === 'date'"
              v-model="dataSource[`${item.prop}`]"
              :clearable="false"
              :disabled="item.disabled || isEditDisabled"
              format="YYYY-MM-DD"
              :placeholder="item.placeholder"
              type="date"
              style="width:100%"
              value-format="YYYY-MM-DD"
            />
            <!-- month  年月类型 -->
            <el-date-picker
              v-if="item.type === 'month'"
              v-model="dataSource[`${item.prop}`]"
              :clearable="false"
              :disabled="item.disabled || isEditDisabled"
              format="YYYY-MM"
              style="width:100%"
              :placeholder="item.placeholder"
              type="month"
              value-format="YYYY-MM"
            />
            <!-- year  年度类型 -->
            <el-date-picker
              v-if="item.type === 'year'"
              v-model="dataSource[`${item.prop}`]"
              :clearable="false"
              :disabled="item.disabled || isEditDisabled"
              format="YYYY"
              style="width:100%"
              :placeholder="item.placeholder"
              type="year"
              value-format="YYYY"
            />
            <!-- dateTimeRange  起止日期类型 -->
            <el-date-picker
              v-if="item.type === 'dateTimeRange'"
              v-model="dataSource[`${item.prop}`]"
              :disabled="item.disabled || isEditDisabled"
              end-placeholder="结束时间"
              format="YYYY-MM-DD"
              :placeholder="item.placeholder"
              range-separator="-"
              style="width:100%"
              start-placeholder="开始时间"
              type="datetimerange"
              value-format="YYYY-MM-DD"
            />
            <!-- select  下拉框类型 -->
            <el-select
              v-if="item.type === 'select'"
              v-model="dataSource[`${item.prop}`]"
              :disabled="item.disabled || isEditDisabled"
              :placeholder="item.placeholder"
              style="width:100%"
            >
              <template v-if="item.selectOptions">
                <el-option
                  v-for="obj in item.selectOptions"
                  :key="obj.value"
                  :label="obj.label"
                  :value="obj.value"
                />
              </template>
            </el-select>
            <!-- radio  单选类型 -->
            <el-radio-group
              v-if="item.type === 'radio'"
              v-model="dataSource[`${item.prop}`]"
              :disabled="item.disabled || isEditDisabled"
              @change="handleRadio"
              style="width:100%"
            >
              <el-radio
                v-for="obj in item.radioOptions"
                :key="obj.value"
                :label="obj.value"
              >
                {{ obj.label }}
              </el-radio>
            </el-radio-group>
            <!-- radio  单选类型 -->
            <el-checkbox-group
              v-if="item.type === 'checkbox'"
              v-model="dataSource[`${item.prop}`]"
              :disabled="item.disabled || isEditDisabled"
              @change="handleCheckbox"
              style="width:100%"
            >
              <el-checkbox
                v-for="obj in item.CheckboxOptions"
                :key="obj.value"
                :label="obj.value"
              >
                {{ obj.label }}
              </el-checkbox>
            </el-checkbox-group>
             <!-- editor  富文本类型 -->
            <wangEditor style="width:100%" v-if="item.type === 'editor'" :initValue="dataSource[`${item.prop}`]" :disabled="showWang"
                ></wangEditor>
             <!-- file  附件类型 -->
             <template v-if="item.type == 'file'">
              <fileUpload v-model="dataSource[`${item.prop}`]" :objectId="item.dataArrId"
                valid="doc、docx、png、jpg、jpeg、ppt、wps、pdf、ceb、xls、xlsx、txt、bmp" :max="20480" serviceType="intellectual"
                :length="10" :fileType="1" @getDelAttachment="delFile"></fileUpload>
              <div class="prompt">
                <p>1.上传需求相关材料;</p>
                <p>2.可上传多个附件,支持doc、docx、png、jpg、jpeg、ppt、wps、pdf、ceb、xls、xlsx、txt、bmp等格式;</p>
                <p>3.文件大小不超过20M;</p>
              </div>
            </template>
          </template>
          <template v-else>
            <slot :name="item.prop" :val="dataSource[`${item.prop}`]" />
          </template>
        </el-form-item>
      </template>
      <template v-else>
        <slot :item="item" :name="item.prop" />
      </template>
    </template>
  </div>
</template>
<script>
import { defineComponent, toRefs, reactive } from 'vue'
import { useRoute } from 'vue-router'
  export default defineComponent({
    name: 'CustomForm',
    props: {
      isShow: {
        require: false,
        type: Boolean,
        default: true,
      },
      curPage: {
        require: false,
        type: String,
        default: '',
      },
      formData: {
        require: true,
        type: Object,
        default: () => { },
      },
      formItem: {
        require: true,
        type: Object,
        default: () => { },
      },
    },
    emits: ['changeRadio', 'changeCheckbox', 'getDelAttachment'],
    setup(props, { emit }) {
      const route = useRoute()
      const query = route.query
      const { formItem, formData, curPage } = toRefs(props)
      const state = reactive({
        curBtn: curPage,
        formItem: formItem,
        showWang: false,
        dataSource: formData,
        isEditDisabled: query.type === 'view' ? true : false,
      })

      const handleRadio = (val) => {
        emit('changeRadio', val)
      }

      const handleCheckbox = (val) => {
        emit('changeCheckbox', val)
      }

      const delFile = (val) => {
        emit('getDelAttachment', val);
      }
 
      return {
        query,
        ...toRefs(state),
        handleRadio,
        handleCheckbox,
        delFile
      }
    },
  })
</script>
<style lang="scss" scoped>
  .content {
    overflow: auto;
  }
 .prompt {
    position: absolute;
    top: 10px;
    left: 130px;
    color: gray;
    p {
      line-height: 25px;
    }
}
</style>
2.3、定义公共样式

src\assets\css\index.scss

// 表单样式
.el-form .width25 {
    float: left;
    width: 25%;
}
.el-form .width33 {
    float: left;
    width: 33.3%;
}
.el-form .width35 {
    float: left;
    width: 35%;
}
.el-form .width50 {
    float: left;
    width: 50%;
}
.el-form .width75 {
    float: left;
    width: 75%;
}
.el-form .width100 {
    float: left;
    width: 100%;
}
2.4、加载全局样式

src\main.ts

// 加载全局样式样式
import './assets/css/index.scss'
3、data数据

data.ts

import {
  // validateIsemail,
  // validateIdCard,
  validateIsphone,
} from "@/utils/validateForm";
export const topicItem = [
  {
    type: "input",
    prop: "topicName",
    label: "小组名称:",
    // maxLength: 100,
    // slot: true,
    show:true,
    limit: true,
    className: "width25",
    placeholder: "请输入",
    rules: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  {
    type: "input",
    prop: "prjName",
    label: "项目名称:",
    // maxLength: 100,
    // slot: true,
    show:true,
    limit: true,
    className: "width50",
    placeholder: "请输入",
    rules: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  {
    type: "input",
    prop: "title",
    show:true,
    className: "width100",
  },
  {
    type: "input",
    prop: "email",
    label: "电子邮箱:",
    placeholder: "请输入",
    className: "width25",
    // rules: [
    //   { required: true, message: '请输入电子邮箱', trigger: 'blur' },
    //   {
    //     validator: validateIsemail,
    //     trigger: 'blur',
    //   },
    // ],
  },
  {
    type: "input",
    prop: "telephone",
    label: "联系电话:",
    placeholder: "请输入",
    className: "width25",
    rules: [
      { required: true, message: "请输入联系电话", trigger: "blur" },
      {
        validator: validateIsphone,
        trigger: "blur",
      },
    ],
  },
  {
    type: "input",
    prop: "idCard",
    label: "身份证号:",
    placeholder: "请输入",
    className: "width25",
    // rules: [
    //   { required: true, message: '请输入身份证号', trigger: 'blur' },
    //   {
    //     validator: validateIdCard,
    //     trigger: 'blur',
    //   },
    // ],
  },
  {
    type: "select",
    prop: "stuAttr",
    label: "项目属性:",
    // labelWidth: '185px',
    selectOptions: [
      { label: "国企", value: "1" },
      { label: "私企", value: "2" },
    ],
    placeholder: "请选择",
    className: "width25",
  },
  {
    type: "date",
    prop: "startDate",
    label: "开始时间:",
    // slot: true,
    className: "width25",
    placeholder: "请输入",
  },

  {
    type: "date",
    prop: "endDate",
    label: "结束时间:",
    className: "width25",
    // slot: true,
    placeholder: "请输入",
  },
  {
    type: "number",
    prop: "totalPeople",
    label: "总人数:",
    placeholder: "请输入",
    className: "width25",
    precision: 0,
    min: 0,
    max: 99999999,
  },
  {
    type: "textarea",
    prop: "researchContent",
    label: "备注内容:",
    maxLength: "500",
    rows: 3,
    placeholder: "请输入",
    className: "width100",
  },
  {
    type: "date",
    prop: "firingDate",
    label: "启动时间:",
    // slot: true,
    className: "width25",
    placeholder: "请输入",
  },
  {
    type: "month",
    prop: "startMonth",
    label: "启动月份:",
    // slot: true,
    className: "width25",
    placeholder: "请输入",
  },
  {
    type: "year",
    prop: "startYear",
    label: "启动年度:",
    // slot: true,
    className: "width25",
    placeholder: "请输入",
  },
  {
    type: "dateTimeRange",
    prop: "dateLength",
    label: "起止日期:",
    // slot: true,
    className: "width25",
    placeholder: "请输入",
  },
  {
    type: "radio",
    prop: "isIntake",
    label: "是否参与:",
    // labelWidth: '185px',
    radioOptions: [
      { label: "是", value: "1" },
      { label: "否", value: "0" },
    ],
    className: "width25",
  },
  {
    type: "input",
    prop: "titleA",
    show:true,
    className: "width100",
  },
  {
    type: "checkbox",
    prop: "cooperMode",
    label: "合作方式:",
    // labelWidth: '185px',
    CheckboxOptions: [
      { label: "技术转让", value: "0" },
      { label: "许可使用", value: "1" },
      { label: "合作开发", value: "2" },
      { label: "技术服务", value: "3" },
      { label: "融资需求", value: "4" },
      { label: "产品推广", value: "5" },
    ],
    className: "width100",
  },
  {
    type: "number",
    prop: "formulaPercentage",
    label: "投入百分比:",
    // value: '100',
    // show: false,
    // labelWidth: '180',
    placeholder: "请输入",
    className: "width50",
  },
  {
    type: "editor",
    prop: "editorContent",
    label: "介绍内容:",
    placeholder: "请输入",
    className: "width100",
  },
  {
    type: "input",
    prop: "teamName",
    label: "小组名称:",
    placeholder: "请输入",
    maxLength: "5",
    className: "width33",
  },
  {
    type: "input",
    prop: "declareUnit",
    label: "申报单位:",
    placeholder: "请输入",
    maxLength: "100",
    className: "width33",
  },
  {
    type: "input",
    prop: "titleB",
    show: true,
    className: "width100",
  },
  // {
  //   type: 'input',
  //   prop: 'softwareWorkNum',
  //   label: '作品(个数):',
  //   placeholder: '请输入',
  //   maxLength: '5',
  //   className: 'width50',
  // },
  // {
  //   type: 'input',
  //   prop: 'treatiseNum',
  //   label: '作品(个数):',
  //   placeholder: '请输入',
  //   maxLength: '5',
  //   className: 'width50',
  // },
  {
    type: "file",
    prop: "attachFileArr",
    dataArrId: "",
    label: "附件:",
    placeholder: "请上传",
    className: "width100",
    rules: [{ required: true }],
  },
];
export const teamItem = [
  {
    type: "input",
    prop: "teamName",
    label: "姓名:",
    // maxLength: 100,
    // slot: true,
    // show:true,
    limit: true,
    className: "width25",
    placeholder: "请输入",
    rules: [{ required: true, message: "请输入", trigger: "blur" }],
  },
];
export const infoItem = [
  {
    type: "input",
    prop: "peopleName",
    label: "姓名:",
    // maxLength: 100,
    // slot: true,
    // show:true,
    limit: true,
    className: "width25",
    placeholder: "请输入",
    rules: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  {
    type: "number",
    prop: "age",
    label: "年龄",
    // maxLength: 100,
    // slot: true,
    // show:true,
    limit: true,
    className: "width25",
    placeholder: "请输入",
    rules: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  {
    type: "select",
    prop: "sex",
    label: "性别:",
    // labelWidth: '185px',
    selectOptions: [
      { label: "男", value: "1" },
      { label: "女", value: "2" },
    ],
    placeholder: "请选择",
    className: "width25",
  },
  {
    type: "input",
    prop: "position",
    label: "职位:",
    // maxLength: 100,
    // slot: true,
    // show:true,
    limit: true,
    className: "width25",
    placeholder: "请输入",
    rules: [{ required: true, message: "请输入", trigger: "blur" }],
  },
];

export const awardsItem = [
  {
    type: "input",
    prop: "awardsName",
    label: "获奖名称:",
    // maxLength: 100,
    // slot: true,
    // show:true,
    limit: true,
    className: "width25",
    placeholder: "请输入",
    rules: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  {
    type: "date",
    prop: "awardsDate",
    label: "获奖日期:",
    // slot: true,
    className: "width25",
    placeholder: "请输入",
  },
  {
    type: "select",
    prop: "awardsGrade",
    label: "授奖等级:",
    // labelWidth: '185px',
    selectOptions: [
      { label: "国家级", value: "1" },
      { label: "省部级", value: "2" },
      { label: "集团级", value: "3" },
    ],
    placeholder: "请选择",
    className: "width25",
  },
  {
    type: "input",
    prop: "awardsUnit",
    label: "授奖单位:",
    // maxLength: 100,
    // slot: true,
    // show:true,
    limit: true,
    className: "width25",
    placeholder: "请输入",
    rules: [{ required: true, message: "请输入", trigger: "blur" }],
  },
];
4.1、校验文件

src\utils\validateForm.ts

import { ElMessage } from 'element-plus'
import { validatorSpecialCharacter,filterSpecialCharacterAction } from './filter.js'

/**
 * @description form表单特定字符校验
 * @param value
 * @returns {boolean}
 */
export function validateCommonText(rule: any, value: any, callback: any) {
  if (value?.length || rule.required) {
    const val = value.trim()
    if (!val) {
      callback(new Error('请输入有效内容'))
      return
    }
    const err: any = validatorSpecialCharacter(val)
    if (err) {
      callback(new Error(err.message))
      return
    }
  }
  callback()
}

//  禁止输入框特殊字符校验 
export function replaceCommonText(e: any) {
  if (e.length) { 
    const val = e.trim()
    if (!val) {
      ElMessage({
        message: '请输入有效内容',
        type: 'warning',
      })
      return
    }
    const err: any = validatorSpecialCharacter(e)
    if (err) {
      ElMessage({
        message: err.message,
        type: 'warning',
      })
      const y = filterSpecialCharacterAction(val)
      return y
    } else {
      return e
    }
  }
}

/**
 * @description 判读是否为外链
 * @param path
 * @returns {boolean}
 */
export function isExternal(path: string) {
  return /^(https?:|mailto:|tel:|\/\/)/.test(path)
}

/**
 * @description 校验密码是否小于6位
 * @param value
 * @returns {boolean}
 */
export function isPassword(value: string | any[]) {
  return value.length >= 6
}

/**
 * @description 判断是否为数字
 * @param value
 * @returns {boolean}
 */
export function isNumber(value: string) {
  const reg = /^[0-9]*$/
  return reg.test(value)
}

/**
 * @description 判断是否是名称
 * @param value
 * @returns {boolean}
 */
export function isName(value: string) {
  const reg = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/
  return reg.test(value)
}

/**
 * @description 判断是否为IP
 * @param ip
 * @returns {boolean}
 */
export function isIP(ip: string) {
  const reg =
    /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
  return reg.test(ip)
}

/**
 * @description 判断是否是传统网站
 * @param url
 * @returns {boolean}
 */
export function isUrl(url: string) {
  const reg =
    /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
  return reg.test(url)
}

/**
 * @description 判断是否是小写字母
 * @param value
 * @returns {boolean}
 */
export function isLowerCase(value: string) {
  const reg = /^[a-z]+$/
  return reg.test(value)
}

/**
 * @description 判断是否是大写字母
 * @param value
 * @returns {boolean}
 */
export function isUpperCase(value: string) {
  const reg = /^[A-Z]+$/
  return reg.test(value)
}

/**
 * @description 判断是否是大写字母开头
 * @param value
 * @returns {boolean}
 */
export function isAlphabets(value: string) {
  const reg = /^[A-Za-z]+$/
  return reg.test(value)
}

/**
 * @description 判断是否是字符串
 * @param value
 * @returns {boolean}
 */
export function isString(value: any) {
  return typeof value === 'string' || value instanceof String
}

/**
 * @description 判断是否是数组
 * @param arg
 */
export function isArray(arg: string | (string | number)[]) {
  if (typeof Array.isArray === 'undefined') {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
  return Array.isArray(arg)
}

/**
 * @description 判断是否是端口号
 * @param value
 * @returns {boolean}
 */
export function isPort(value: string) {
  const reg =
    /^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/
  return reg.test(value)
}

/**
 * @description 判断是否是手机号
 * @param value
 * @returns {boolean}
 */
export function isPhone(value: string) {
  const reg = /^((13[0-9])|(14[5-7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\d{8}$/
  return reg.test(value)
}

/**
 * @description 判断是否是身份证号(第二代)
 * @param value
 * @returns {boolean}
 */
export function isIdCard(value: string) {
  const reg =
    /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
  return reg.test(value)
}
export function validateIdCard(rule: any, value: any, callback: any) {
  if (value || rule.required) {
    if (!isIdCard(value)) {
      callback(new Error('请输入正确的身份证号'))
    } else {
      callback()
    }
  } else {
    callback()
  }
}

/**
 * @description 判断是否是邮箱
 * @param value
 * @returns {boolean}
 */
export function isEmail(value: string) {
  const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
  return reg.test(value)
}

/**
 * @description 判断是否中文
 * @param value
 * @returns {boolean}
 */
export function isChina(value: string) {
  const reg = /^[\u4E00-\u9FA5]{2,4}$/
  return reg.test(value)
}

/**
 * @description 判断是否为空
 * @param value
 * @returns {boolean}
 */
export function isBlank(value: string | null) {
  return (
    value === null ||
    false ||
    value === '' ||
    value.trim() === '' ||
    value.toLocaleLowerCase().trim() === 'null'
  )
}

/**
 * @description 判断是否为固话
 * @param value
 * @returns {boolean}
 */
export function isTel(value: string) {
  const reg =
    /^(400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})([- ])?)?([0-9]{7,8})(([- 转])*([0-9]{1,4}))?$/
  return reg.test(value)
}

/**
 * @description 判断是否为数字且最多两位小数
 * @param value
 * @returns {boolean}
 */
export function isNum(value: string) {
  const reg = /^\d+(\.\d{1,2})?$/
  return reg.test(value)
}

/**
 * @description 判断是否为json
 * @param value
 * @returns {boolean}
 */
export function isJson(value: string | null) {
  if (typeof value === 'string')
    try {
      const obj = JSON.parse(value)
      return !!(typeof obj === 'object' && obj)
    } catch (e) {
      return false
    }
  return false
}
/**
 * @description 手机号码校验
 * @param value
 * @returns {boolean}
 */
export function validateIsphone(rule: any, value: any, callback: any) {
  if (value || rule.required) {
    if (!isPhone(value)) {
      callback(new Error('请输入正确的联系电话'))
    } else {
      callback()
    }
  } else {
    callback()
  }
}
/**
 * @description 电子邮箱校验
 * @param value
 * @returns {boolean}
 */
export function validateIsemail(rule: any, value: any, callback: any) {
  if (value || rule.required) {
    if (!isEmail(value)) {
      callback(new Error('请输入正确的电子邮箱'))
    } else {
      callback()
    }
  } else {
    callback()
  }
}

// 校验数字
export function validateNumber(rule: any, value: any, callback: any) {
  if (value || rule.required) {
    const v = value || ''
    const pattern = /^[0-9]{1,8}$/
    if (!pattern.test(v)) {
      callback(new Error('请输入1-8位数字'))
    } else {
      callback()
    }
  } else {
    callback()
  }
}

/**
 * @description 数字校验保留小数后俩位
 * @param value
 * @returns {boolean}
 */
export function validateIsNum(rule: any, value: any, callback: any) {
  const reg = /^(([1-9]{1}\d*)|(0{1}))(\.\d{1,2})?$/
  if (!reg.test(value)) {
    callback(new Error('请保留小数后俩位'))
  }
}

/**
 * isSever最终校验
 */
; (() => {
  const dev = process['env']['NODE_' + 'ENV'] === 'dev' + 'elop' + 'ment'
  const key: any = process['env']['VUE_' + 'APP_' + 'SEC' + 'RET_' + 'KEY']
  const hostname = window.location.hostname
  const local = '127.' + '0.' + '0.' + '1'
  const server = hostname !== 'local' + 'host' || hostname !== local

  if (!dev && server) {
    if (key.substring(key.length - 2) !== '=' + '=')
      localStorage.setItem('theme', '{"lay' + 'out","nu' + 'll"}')
  }
})()

// 用户账号校验
export function validateCommonAccont(rule: any, value: any, callback: any) {
  const commonNoChars = '~!@#$%^&*()_+|}{":?><,./;' + '’[]\\=-` '
  const noChars = commonNoChars
  const v = value || ''
  for (let i = 0; i < noChars.length; i++) {
    const char = noChars[i]
    if (v.indexOf(char) != -1) {
      callback(new Error('不能使用字符:' + noChars))
      return
    }
  }
  const words = ['null', 'NULL', 'Null']
  for (let i = 0; i < noChars.length; i++) {
    const word = words[i]
    if (v.indexOf(word) != -1) {
      callback(new Error('不能包含: ' + word))
      return
    }
  }
  callback()
}

//域名校验
export function validateURL(rule: any, value: any, callback: any) {
  const strRegex =
    '^((https|http|ftp|rtsp|mms)?://)' +
    "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" + // ftp的user@
    '(([0-9]{1,3}.){3}[0-9]{1,3}' + // IP形式的URL- 199.194.52.184
    '|' + // 允许IP和DOMAIN(域名)
    "([0-9a-z_!~*'()-]+.)*" + // 域名- www.
    '([0-9a-z][0-9a-z-]{0,61})?[0-9a-z].' + // 二级域名
    '[a-z]{2,6})' + // first level domain- .com or .museum
    '(:[0-9]{1,4})?' + // 端口- :80
    '((/?)|' + // a slash isn't required if there is no file name
    "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$"
  const re = new RegExp(strRegex)

  if (value && !re.test(value)) {
    callback(new Error('请输入正确地址'))
  }
  callback()
}

//路由地址校验
export function validateCommonPath(rule: any, value: any, callback: any) {
  const commonNoChars = '~!@#$%^&*()_+|}{":?><,.;' + '’[]\\=-` '
  const noChars = commonNoChars
  const v = value || ''

  for (let i = 0; i < noChars.length; i++) {
    const char = noChars[i]
    if (v.indexOf(char) != -1) {
      callback(new Error('不能使用字符:' + noChars))
      return
    }
  }

  const words = ['null', 'NULL']
  for (let i = 0; i < noChars.length; i++) {
    const word = words[i]
    if (v.indexOf(word) != -1) {
      callback(new Error('不能包含: ' + word))
      return
    }
  }

  callback()
}

/**
 * 中文 + 字母 + 数字 + "-" 的组合
 * @param {*} rule
 * @param {*} value
 * @param {*} callback
 */
export function validateRoleRuler(rule: any, value: any, callback: any) {
  const v = value || ''
  const commonNoChars = '~!@#$%^&*()+|}{":?><,./;' + '’[]\\=` '
  const pattern = /[~!@#$%^&*()_+|}{":?><,./\\;'[\]=` 、]|([Nn][Uu][Ll][Ll])/
  if (pattern.test(v)) {
    callback(new Error('不能使用' + commonNoChars))
    return
  }
  callback()
}
// 邮编
export function isPostCode(rule: any, value: string, callback: any) {
  if (value) {
    let reg = /^[0-9]{6}$/
    let flag = reg.test(value)
    if (!flag) {
      callback(new Error('请输入正确邮编'))
    }
    callback()
  }
  callback()
}
// 传真
export function isFax(rule: any, value: string, callback: any) {
  if (value) {
    // 国家代码(2到3位)-区号(2到3位)-电话号码(7到8位)-分机号(3位)"
    let reg = /^(([0\+]\d{2,3}-)?(0\d{2,3})-)(\d{7,8})(-(\d{3,}))?$/
    let flag = reg.test(value)
    if (!flag) {
      callback(new Error('请输入正确传真号码'))
    }
    callback()
  }
  callback()
}
// 含两位小数数字
export function isNumberDot(rule: any, value: any, callback: any) {
  if (value) {
    const reg = /^\d+\.?\d{0,2}$/
    let flag = reg.test(value)
    if (!flag) {
      callback(new Error('请输入正确数字'))
    }
    callback()
  }
  callback()
}
// 正整数
export function isIntFormNumber(rule: any, value: any, callback: any) {
  if (value) {
    const reg = /^[1-9]{1}[0-9]*$/
    let flag = reg.test(value)
    if (!flag) {
      callback(new Error('请输入有效数字'))
    }
    callback()
  }
  callback()
}
// 正整数
export function isIntNumber(value: any) {
  if (value) {
    const reg = /^[1-9]{1}[0-9]*$/
    let flag = reg.test(value)
    if (!flag) {
      ElMessage({
        message: '请输入有效数值',
        type: 'warning',
      })
      const y = value.replace(value, '')
      return y
    } else {
      return value
    }
  }
}
export function isPerNumber(rule: any, value: any, callback: any) {
  const reg = /^([0-9]\d{0,1}|100$)(\.\d{1,2})?$/
  let flag = reg.test(value)
  if (!flag) {
    callback(new Error('请输入正确百分制数字'))
  }
  callback()
}
// 含4位小数数字
export function isFourNumberDot(value: any) {
  if (value) {
 
    const reg = /^\d+\.?\d{0,4}$/;
    let flag = reg.test(value);
    if (!flag) {
      ElMessage({
        message: '请输入数字,含小数点且仅支持4位小数',
        type: 'warning'
      });

      const dotArr = value.split('.');
      const dotInt = dotArr[0];
      const dotNum = dotArr[1].substring(0, 4);
      const val = value.indexOf('.') >= 1 ? dotInt + '.' + dotNum : value;
      return val;
    }
    else {
      if (value.length ===2 && Number(value[0]) === 0 && (Number(value[1]) >= 0 || value[1]==='.')) {
        ElMessage({
          message: '请输入有效数值',
          type: 'warning'
        });
        return '';
      }

      if (value.indexOf('.') >= 1) {
        // 含小数点
        const dotArr = value.split('.');
        const dotInt = dotArr[0];
        const dotNum = dotArr[1];
        if (dotInt?.length >= 8) {
          // 8位整数的情况(含小数点)
          const dot = dotNum.substring(0, 4);
          if (Number(dotInt) >= 99999999) {
            // 输入99999999的情况 截取7位数
            ElMessage({
              message: '最大仅支持99999999',
              type: 'warning'
            });
            const val = dotInt.substring(0, 7);
            return val;
          } else {
            if (dotNum?.length > 4) {
              // 4位小数
              const dot = dotNum.substring(0, 4);
              const int = dotInt.substring(0, 8);
              const val = int + '.' + dot;
              ElMessage({
                message: '仅支持4位小数',
                type: 'warning'
              });
              return val;
            } else {
               // 非4位小数
              const val = dotInt + '.' + dotNum;
              return val;
            }
          }
        } else {
          // 不到8位整数位(含小数点)
          if (dotNum?.length > 4) {
            // 4位小数
            const dot = dotNum.substring(0, 4);
            const val = dotInt + '.' + dot;
            ElMessage({
              message: '仅支持4位小数',
              type: 'warning'
            });
            return val;
          } else {
             // 非4位小数
            const val = dotInt + '.' + dotNum;
            return val;
          }
        }
      } else {
        // 不含小数点
        if (value?.length > 8) {
          ElMessage({
            message: '最大仅支持99999999',
            type: 'warning'
          });
        }
        const val = value.substring(0, 8);
        return val;
      }
    }
  }
}
4.2、校验方法

src\utils\filter.js

/* eslint-disable */
// 特殊字符  敏感词检验过滤
import { init } from './deailFilter'
let regObj = {
  urlReg:
    /\+|\?|#|\*|%2B|%20|%2F|%3F|%25|%23|%26|%3D|%27|%26|%22|%28|%29|%2a|%2b|%2d|%30/,
  htmlReg:
    /object|comment|&nbsp;|&quot;|&amp;|&#x27;|&#x2F;|&lt;|&gt;|&AElig;|&Aacute;|&Acirc;|&Agrave;|&Aring;|&Atilde;|&Auml;|&Ccedil;|&ETH;|&Eacute;|&Ecirc;|&Egrave;|&Euml;|&Iacute;|&Icirc;|&Igrave;|&Iuml;|&Ntilde;|&Oacute;|&Ocirc;|&Ograve;|&Oslash;|&Otilde;|&Ouml;|&THORN;|&Uacute;|&Ucirc;|&Ugrave;|&Uuml;|&Yacute;|&aacute;|&acirc;|&aelig;|&agrave;|&aring;|&atilde;|&auml;|&ccedil;|&eacute;|&ecirc;|&egrave;|&eth;|&euml;|&iacute;|&icirc;|&igrave;|&iuml;|&ntilde;|&oacute;|&ocirc;|&ograve;|&oslash;|&otilde;|&ouml;|&szlig;|&thorn;|&uacute;|&ucirc;|&ugrave;|&uuml;|&yacute;|&yuml;|&cent;|\\""|&|&#39/,
  jsReg:
    /,|\||{|}|%|<|>|&|""|'|\/|\\|\\r|\\n|\\\\|\\t|\\f|\\b|!|@|#|\$|\^|\*|\(|\)|~|:|;|\\`|-|=|\[|\]|javascript|script|function|jscript|vbscript|onfocus|onblur|location|document|window|onclick|href|<!--|--|->|\/\\\\\\\*|\\\\\\\*\/|onfocus|\/\/|onerror|\/\*|data:|\\u003e|\\u003c|eval|url|expr|URLUnencoded|referrer|write\(.\)|writeln|body\.innerHtml|execScript|navigate|srcdoc|%0a|<\/|\.|_|·|!|¥|…|【|】|、|;|‘|’|:|“|”|,|。|、|《|》|?/,
  sqlReg: /\\\*|--|%28|\)|\/\/|\/\*|\/\\\*\\\\*/,
  replaceStrs: [
    '//',
    '/*',
    '</',
    '/\\*',
    'net +user',
    'net +localgroup +administrators',
    'information_schema.columns',
    'netlocalgroup administrators',
    'body.innerHtml',
    '()',
    '(',
    ')',
    'null',
    'NULL',
    'Null',
  ],
}
const sqlKeyword = [
  'and',
  'or',
  'exec',
  'execute',
  'insert',
  'select',
  'delete',
  'update',
  'alter',
  'create',
  'drop',
  'count',
  'chr',
  'char',
  'asc',
  'desc',
  'mid',
  'substring',
  'master',
  'truncate',
  'declare',
  'xp_cmdshell',
  'restore',
  'backup',
  'net user',
  'like',
  'table',
  'from',
  'grant',
  'use',
  'column_name',
  'group_concat',
  'table_schema',
  'union',
  'where',
  'order',
  'by',
  'join',
  'modify',
  'into',
  'substr',
  'ascii',
  'having',
]

const htmlTag = [
  'a',
  'iframe',
  'body',
  'form',
  'base',
  'img',
  'style',
  'div',
  'meta',
  'link',
  'input',
  'br',
]

const jsFunction = [
  'open',
  'confirm',
  'prompt',
  'setInterval',
  'setTimeout',
  'alert',
]
let jsFunctionStr = (() => {
  let regArr = []
  jsFunction.forEach((item) => {
    regArr.push(`${item}\\(.*\\)`)
  })
  return regArr.join('|')
})()

let htmlTagRegStr = (() => {
  let regArr = []
  htmlTag.forEach((item) => {
    regArr.push(`<${item}`)
    regArr.push(`${item}>`)
  })
  return regArr.join('|')
})()

regObj.jsFunctionReg = new RegExp(jsFunctionStr)
regObj.htmlTagReg = new RegExp(htmlTagRegStr, 'i')
regObj.sqlKeywordReg = new RegExp(sqlKeyword.join('\\s|') + '\\s', 'i')

const reg = copyReg(regObj)
init(reg)

// 全局敏感词校验方法
export function validatorSpecialCharacter(value) {
  if (!isNaN(value)) return false
  if (typeof value !== 'string') return false // 只校验字符串
  value = String(value).trim()
  if (value === '') return false // 空不检验

  if (reg.urlReg.test(value)) {
    return {
      message: '不能包含特殊字符',
      reg: reg.urlReg,
    }
  }
  if (reg.htmlReg.test(value)) {
    return {
      message: '不能包含关键字',
      reg: reg.htmlReg,
    }
  }
  if (reg.jsReg.test(value)) {
    return {
      message: '不能包含特殊字符',
      reg: reg.jsReg,
    }
  }

  if (reg.sqlReg.test(value)) {
    return {
      message: '不能包含特殊字符',
      reg: reg.sqlReg,
    }
  }

  if (reg.sqlKeywordReg.test(value)) {
    return {
      message: '不能包含关键字',
      msg: '不能包含SQL语句',
      reg: reg.sqlKeywordReg,
    }
  }

  if (reg.htmlTagReg.test(value)) {
    return {
      msg: '不能包含HTML标签',
      message: '不能包含关键字',
      reg: reg.htmlTagReg,
    }
  }

  if (reg.jsFunctionReg.test(value)) {
    return {
      msg: '不能包含JS方法',
      message: '不能包含关键字',
      reg: reg.jsFunctionReg,
    }
  }

  let str = reg.replaceStrs.find((val) => {
    return value.indexOf(val) !== -1
  })
  if (str) {
    return {
      message: '不能包含关键字',
      str: str,
      reg: str,
    }
  }

  return false
}

function isArray(data) {
  return Object.prototype.toString.call(data) === '[object Array]'
}

function isObject(data) {
  return Object.prototype.toString.call(data) === '[object Object]'
}
// 过滤特殊字符的方法
export function filterSpecialCharacterAction(data) {
  if (isArray(data) || isObject(data)) {
    for (const key in data) {
      data[key] = filterSpecialCharacterAction(data[key])
    }
  } else {
    let err = validatorSpecialCharacter(data)
    if (err) {
      if (err.reg) {
        return filterSpecialCharacterAction(data.replace(err.reg, ''))
      }

      if (err.str) {
        return filterSpecialCharacterAction(data.replace(err.str, ''))
      }
    }
  }
  return data
}

function copyReg(regObj) {
  let result = {}

  for (const key in regObj) {
    result[key] = regObj[key]
  }
  return result
}

// 响应数据过滤数据
export const filterSpecialCharacter = (data) => {
  let result = data
  return result
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值