vue element 封装表单
表单组件的封装。涉及到表单组件的初始渲染,表单提交事件,表单的数据回显。根据系统的不断开发,表单组件的封装也会一直不断优化。
·
背景:
在前端系统开发中,系统页面涉及到的表单组件比较多,所以进行了简单的封装。封装的包括一些Form表单组件,如下:input输入框、select下拉框、等
实现效果:

理论知识:
表单组件官方链接:点击跳转

封装组件:
封装组件的思路:
不封装element组件,每一个input组件绑定一个form对象,例如官网。
简单封装element组件,利用for循环生成form表单的每一项el-form-item。
进一步封装,利用判断表单组件的类型,是input、select或者其它表单组件。
终极封装,把el-form表单封装成一个独立的表单,自定义命名为AreaFormItem.vue;然后单页面vue调用AreaFormItem.vue传参以及表单数据回显等操作。
封装组件的过程:
1.不封装表单组件,代码如下:
实现代码:【表单组件未封装】
<el-form ref="ruleForm" :model="state.form" label-width="auto" :rules="rules" class="form-box">
<el-row class="form-row">
<el-col :span="5" :gutter="20" style="padding-left: 0px">
<el-form-item label="航次" prop="voyageNo">
<el-input v-model="state.form.voyageNo" placeholder="请输入航次" />
</el-form-item>
</el-col>
<el-col :span="4" :gutter="20" style="padding-left: 10px">
<el-form-item label="船名" prop="name">
<el-input v-model="state.form.name" placeholder="请输入船名" />
</el-form-item>
</el-col>
<el-col :span="4" :gutter="20" style="padding-left: 10px">
<el-form-item label="装港" prop="loadName">
<el-input v-model="state.form.loadName" placeholder="请输入装港" />
</el-form-item>
</el-col>
<el-col :span="5" :gutter="20" style="padding-left: 10px">
<el-form-item label="卸港" prop="dischargName">
<el-input v-model="state.form.dischargName" placeholder="请输入卸港" />
</el-form-item>
</el-col>
<el-col :span="5" :gutter="20" style="padding-left: 10px">
<el-form-item label="卸港" prop="cargoType">
<el-input v-model="state.form.cargoType" placeholder="请输入卸港" />
</el-form-item>
</el-col>
</el-row>
</el-form>
备注:因为是横着的表单,这里加入了el-row和el-col 。
2.表单组件封装,for循环渲染表单组件input:

//html
<el-form ref="ruleForm" :model="state.form" label-width="auto" :rules="rules" class="form-box">
<el-row class="form-row">
<el-col :span="item.span || 5" :gutter="20" style="padding-right: 10px" v-for="(item, index) in formItems"
:key="index">
<el-form-item :label="item.label" :prop="item.key">
<el-input v-model="state.form[item.key]" :placeholder="item.placeholder || '请输入'" />
</el-form-item>
</el-col>
</el-row>
</el-form>
//涉及到的变量
const state = reactive({
form: {
name: '',
voyageNo: '',
loadName: '',
dischargName: '',
cargoType: ''
},
})
const formItems = [
{
type: 'input',
label: '船名',
key: 'name',
placeholder: '请输入或者选择船名'
},
{
type: 'input',
label: '航次',
key: 'voyageNo',
placeholder: '请输入或者选择航次'
},
{
type: 'select',
label: '装港',
key: 'loadName',
placeholder: '请输入或者选择装港'
},
{
type: 'select',
label: '卸港',
key: 'dischargName',
placeholder: '请输入或者选择始港'
},
{
type: 'input',
label: '货种',
span : 4,
key: 'cargoType',
placeholder: '请输入或者选择货种',
}
]
3.封装一个input或者select下拉列表,封装如下:
<el-form ref="ruleForm" :model="state.form" label-width="auto" :rules="rules" class="form-box">
<el-row class="form-row">
<el-col :span="item.span || 5" :gutter="20" style="padding-right: 10px"
v-for="(item, index) in formItems" :key="index">
<el-form-item :label="item.label" :prop="item.key">
<el-input v-model="state.form[item.key]" :placeholder="item.placeholder || '请输入'"
v-if="item.type === 'input'" />
<template v-else-if="item.type === 'select'">
<el-select v-model="state.form[item.key]" :clearable="true"
:loading="routeState.loading_startPoint" :multiple="false"
:remote-method="(query) => remoteMethod_Point(query, 'cargoType')"
filterable placeholder="请输入或者选择货种" remote reserve-keyword
@change="(value) => blur_Point(value, 'cargoType', '')"
@clear="clear_select('cargoType')">
<el-option v-for="item in routeState.options_startPoint" :key="item.value"
:label="item.label" :value="item.label" />
</el-select>
</template>
</el-form-item>
</el-col>
</el-row>
</el-form>
//涉及到的数据
const routeState = reactive({
loading_startPoint:false,
options_startPoint:[]
})
4.终极封装,把上面的form表单封装成一个vue组件,自定义命名为 AreaFormItem.vue,
AreaFormItem.vue组件,封装如下:
<template>
<el-form :model="data.formInline" class="area-from" :inline="true" :label-position="'left'" ref="ruleAreaFormRef"
:rules="data.rules">
<template v-for="(item, index) in props.formItems" :key="index">
<template v-if="
item.type &&
![
'location',
'medium',
'password',
'sexRadio',
'coordinate',
'radio',
'textarea',
'multipleSelect',
'uploadFile',
'uploadSingleImg',
].includes(item.type)
">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key" :class="item.class" :style="{
'max-width': item.width || '50%',
width: item.specialWidth || item.width,
}" v-if="item.linkageShow === undefined || item.linkageShow">
<template v-if="item.type == 'select'">
<el-select v-model="data.formInline[item.key]" placeholder="请选择"
:style="{ width: item.specialWidth || item.width || '10vw' }" :disabled="item.isEditAbled && data.isAbled"
:filterable="item.filterable" :filter-method="(filterVal) => {
handleSelectEdit(filterVal, item);
}
" @blur="
() => {
item.filterable &&
data.editSelectValue &&
(data.formInline[item.key] = data.editSelectValue);
}
" @change="
(val) => {
handleSelectEdit(val, item);
searchChange(
item.linkageKey,
item.optionsData,
data.formInline[item.key],
item.linkageDraw,
item.autoPosition,
item.dynamicLinkageKey,
props.formItems,
item.linkageKeyList,
item
);
}
">
<template v-if="item.optionsData.length">
<el-option v-for="(optionItem, index) in item.optionsData" :key="index" :label="optionItem.label"
:value="optionItem.value" />
</template>
<template v-else>
<el-option v-for="(optionItem, index) in data.selectData" :key="index" :label="optionItem.label"
:value="optionItem.value" />
</template>
</el-select>
</template>
<template v-if="item.type == 'selectTree'" style="
.el-select {
width: '10vw';
}
">
<el-tree-select v-model="data.formInline[item.key]" :placeholder="item.placeholder || '请选择所属区域'"
:style="{ width: item.specialWidth || item.width || '10vw' }" :data="data.treeData"
:props="data.defaultProps" check-strictly clearable :multiple="item.multiple || false"
:disabled="item.disabled || false" @change="
(value) => handlechange(value, item.limit, item.isJump || true)
" :filterable="item.filterable || false">
</el-tree-select>
</template>
<template v-if="item.type == 'treeMuiltSelect'" style="
.el-select {
width: '10vw';
}
">
<el-tree-select v-model="data.formInline[item.key]" :placeholder="item.placeholder || '请选择渡口'"
:style="{ width: item.specialWidth || item.width || '10vw' }" :data="data.treeDataList"
:props="item.optionParam || data.defaultProps" :multiple="item.multiple || false" :collapse-tags="true"
:collapse-tags-tooltip="true" :max-collapse-tags="1" clearable :disabled="item.disabled || false">
</el-tree-select>
</template>
<template v-if="item.type == 'treeLocationSelect'" style="
.el-select {
width: '10vw';
}
">
<el-tree-select v-model="data.formInline[item.key]" :placeholder="item.placeholder || '请选择区域下的渡口'"
:style="{ width: item.specialWidth || item.width || '10vw' }" :data="data.treeLocationSelect"
:props="item.optionParam || data.defaultProps" :multiple="item.multiple || false" :collapse-tags="true"
:collapse-tags-tooltip="true" :max-collapse-tags="1" clearable :disabled="item.disabled || false" :check-strictly="false"
default-expand-all @change="(value) => handleSelectchange(value, item)"
:filterable="item.filterable || false" :filter-method="(filterVal) => {
filtertreeLocationSelect(filterVal, item, 'treeLocationSelect');
}" @blur="blur(item, 'treeLocationSelect')">
</el-tree-select>
</template>
<template v-if="item.type == 'remoteSelect'">
<el-select v-model="data.formInline[item.key]" filterable remote :placeholder="item.placeholder"
remote-show-suffix :remote-method="(query) => {
remoteMethod(query, item);
}
" :loading="data.remoteSelectLoading" @change="
(val) => {
item.getSelectRemoteValue &&
item.getSelectRemoteValue(val, data.remoteSelectData);
}
" :disabled="item.isEditAbled && data.isAbled">
<el-option v-for="optionItem in data.remoteSelectData" :key="optionItem.value" :label="optionItem.label"
:value="optionItem.value" />
</el-select>
</template>
<template v-if="item.type == 'input'">
<el-input v-model.trim="data.formInline[item.key]" placeholder="请输入" clearable @clear="searchChange"
:disabled="(item.isEditAbled && data.isAbled) || item.disabled"
:style="{ width: item.inputWidth || '10vw' }" :readonly="item.isReadonly || false">
<template #suffix v-if="item.suffix">
{{ item.suffix }}
</template>
</el-input>
</template>
<template v-if="item.type == 'checkBox'">
<div class="warn-type">
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="
(state) => {
handleCheckAllChange(state, item.key);
}
" v-if="item.optionCheckAll">全部</el-checkbox>
<el-checkbox-group v-model="data.formInline[item.key]" @change="handleCheckedChange">
<el-checkbox v-for="checkItem in item.optionsData" :key="checkItem.value" :label="checkItem.value"
:value="checkItem.value">{{ checkItem.label }}</el-checkbox>
</el-checkbox-group>
</div>
</template>
<template v-if="item.type == 'inputNumber'">
<el-input v-model.trim.number="data.formInline[item.key]" placeholder="请输入数字" clearable
@clear="searchChange" :disabled="item.isEditAbled && data.isAbled" style="width: 10vw"
:readonly="item.isReadonly || false" oninput="value = value.replace(/[^\d.]/g,'')">
<template #suffix v-if="item.suffix">
{{ item.suffix }}
</template>
</el-input>
</template>
<template v-if="item.type == 'inputPhoneNumber'">
<el-input v-model.trim.number="data.formInline[item.key]" placeholder="请输入数字" clearable
@clear="searchChange" :disabled="item.isEditAbled && data.isAbled"
:style="{ width: item.inputWidth || '10vw' }" :readonly="item.isReadonly || false"
oninput="value = value.replace(/[^\d.]/g,'')">
<template #suffix v-if="item.suffix">
{{ item.suffix }}
</template>
</el-input>
</template>
<template v-if="item.type == 'cascader'">
<el-cascader v-model="data.formInline.cityValue" :options="data.citiesData" :props="data.cityDefalut"
@change="citySelectChange" style="width: 10vw" />
</template>
<template v-if="item.type == 'datePicker'">
<el-date-picker v-model="data.formInline[item.key]" type="datetime" placeholder="请选择"
:default-time="item.defaultTime || defaultTime" value-format="YYYY-MM-DD HH:mm:ss"
:style="{ width: item.specialWidth || '10vw' }" :readonly="item.isReadonly || false"
:disabled-date="item.disabledDate" />
</template>
<template v-if="item.type == 'timePicker'">
<el-time-picker v-model="data.formInline[item.key]" placeholder="请选择时间"
:default-time="item.defaultTime || defaultTime" value-format="HH:mm:ss"
:style="{ width: item.specialWidth || '10vw' }" :readonly="item.isReadonly || false"
:disabled-date="item.disabledDate" />
</template>
<template v-if="item.type == 'dateNoTimePicker'">
<el-date-picker v-model="data.formInline[item.key]" type="date" placeholder="请选择"
:default-time="item.defaultTime || defaultTime" value-format="YYYY-MM-DD"
:style="{ width: item.specialWidth || '10vw' }" :readonly="item.isReadonly || false"
:disabled-date="item.disabledDate" />
</template>
</el-form-item>
</template>
<template v-if="item.type == 'multipleSelect'">
<div class="select-area">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key">
<el-select v-model="data.formInline[item.key]" multiple clearable collapse-tags
:placeholder="item.placeholder" popper-class="custom-header" :max-collapse-tags="1"
style="width: 100%; height: 50px" @change="
(val) => {
item.selectOptionChange(val, item);
}
">
<template #header v-if="item.optionsData.length">
<el-checkbox v-model="item.checkAll" :indeterminate="item.indeterminate" @change="
(val) => {
item.handleCheckAll(val, item, data.formInline);
}
">
全部区域
</el-checkbox>
</template>
<el-option v-for="(optionItem, index) in item.optionsData" :key="index" :label="optionItem.label"
:value="optionItem.value" />
</el-select>
</el-form-item>
</div>
</template>
<template v-if="item.type == 'radio'">
<div class="warning-level" v-if="
item.editShow === undefined ? true : !data.isAbled && !item.editShow
" :style="{
display:
(item.onlySingleSelect || item.onlySingleSelect) &&
'inline-block',
width: item.width || '100%',
}">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key"
v-if="!item.onlyDrawType && !item.hiddenFormItem">
<el-radio-group v-model="data.formInline[item.key]" @change="
(val) =>
item.linkageAction
? finallyLinkageSolve(item)
: warningLevelChange(val)
">
<template v-if="item.onlySingleSelect">
<el-radio v-for="(aitem, aindex) in item.optionsData" :key="aindex" :label="aitem.value">{{ aitem.label
}}</el-radio>
</template>
<template v-else>
<el-radio :label="1">一级</el-radio>
<el-radio :label="2">二级</el-radio>
<el-radio :label="3">三级</el-radio>
</template>
</el-radio-group>
</el-form-item>
<el-form-item :label="'绘制类型:'" :label-width="item.labelWidth" :prop="'drawType'"
v-if="!item.onlySingleSelect && data.showDraw">
<el-select v-model="data.formInline.drawType" placeholder="请选择" @change="drawTypeChange"
style="width: 10vw">
<el-option v-for="(optionItem, index) in data.drawTypes" :key="index" :label="optionItem.label"
:value="optionItem.value" />
</el-select>
</el-form-item>
</div>
</template>
<template v-if="item.isBufferArea && !item.hiddenFormItem">
<div class="warning-distance">
<template v-for="(bufferItem, bufferIndex) in data.bufferData" :key="bufferIndex">
<template v-if="bufferItem.level <= data.formInline.level">
<el-form-item :label="bufferItem.label" :label-width="bufferItem.labelWidth" :prop="bufferItem.key">
<el-input v-model.trim="data.formInline[bufferItem.key]" style="width: 4.2vw" placeholder="请输入"
clearable>
</el-input>
<span class="unit">m</span>
</el-form-item>
</template>
</template>
</div>
</template>
<template v-if="item.type && item.type == 'textarea'">
<div class="area-remark" :style="{ 'margin-top': item.marginTop || '0px' }"
v-show="!item.conditionShow || item.conditionShow(props.formItemsVal)">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key" :class="item.class">
<el-input v-model="data.formInline[item.key]" :placeholder="item.placeholder || '请输入备注信息'" type="textarea"
:disabled="item.isEditAbled && data.isAbled" />
</el-form-item>
</div>
</template>
<template v-if="
item.type && item.editShow === undefined
? item.type == 'coordinate'
: item.type == 'coordinate' && !data.isAbled && !item.editShow
">
<div class="cordinate-container">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key">
<div class="wrapper">
<div class="row-input-box" v-for="(columnItem, index) in tableCloumn" :key="index">
<div class="row-name">{{ columnItem.label }}</div>
<div class="input-item" v-for="(inpItem, ipIndex) in columnItem.inpModelParam" :key="ipIndex">
<el-tooltip placement="top" :manual="true" :content="inpItem.msg">
<el-input v-model="data.coordinate[inpItem.value]"></el-input>
</el-tooltip>
<div class="inp-unit">{{ inpItem.label }}</div>
</div>
</div>
</div>
<div class="box">
<img :src="getAssetsFile('buttons/select_point.png')" @click="drawArea" />
</div>
</el-form-item>
</div>
</template>
<template v-if="item.type == 'sexRadio'">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key">
<el-radio-group v-model="data.formInline[item.key]">
<el-radio :label="1">男</el-radio>
<el-radio :label="2">女</el-radio>
</el-radio-group>
</el-form-item>
</template>
<template v-if="
item.editShow === undefined
? item.type == 'password'
: item.type == 'password' && !data.isAbled && !item.editShow
">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key" :class="item.class">
<el-input v-model="data.formInline[item.key]" placeholder="请输入" type="password" autocomplete="new-password"
show-password style="width: 10vw" />
</el-form-item>
</template>
<template v-if="item.type == 'uploadFile'">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key">
<el-input v-model="data.formInline[item.key]" :placeholder="item.placeholder || data.headerResult.placeholder || '请输入'
" clearable style="width: 245px" @clear="
(value) =>
fileClear('fileClear', data.headerResult.fileName, item)
">
<template #append>
<div class="opration-box" v-if="item.option">
<div v-for="oprationItem in item.option" :key="oprationItem.text">
<el-tooltip class="box-item" effect="dark" :content="oprationItem.text" placement="top">
<template v-if="oprationItem.info == 'upload'">
<el-upload ref="uploadRef" class="upload-demo" :show-file-list="false" :auto-upload="true"
accept=".docx,.pdf,.doc" :action="oprationItem.params.importUrl"
:data="oprationItem.uploadParames" :headers="{ Authorization: store.userInfo.token }"
:on-success="(info) => handleResult('success', info, oprationItem)
" :on-error="(info) => handleResult('error', info)">
<img :src="upIcon" style="padding: 0 7px" />
</el-upload>
</template>
<template v-else>
<img :src="upIcon" style="padding: 0 7px" @click="
handleOpenDialog(oprationItem.params, oprationItem)
" />
</template>
</el-tooltip>
</div>
</div>
</template>
</el-input>
</el-form-item>
</template>
<template v-if="item.type == 'uploadMuliteImg'">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key" :style="{
width: item.specialWidth || item.width || '10vw',
maxWidth: item.specialWidth || item.width || '10vw',
}">
<el-upload :auto-upload="false" list-type="picture-card" :on-preview="handlePictureCardPreview"
:on-change="uploadSingleImg" :limit="5" :file-list="upImgState.fileList">
<el-icon v-if="upImgState.fileList.length == 0">
<Plus />
</el-icon>
</el-upload>
<el-dialog v-model="upImgState.dialogVisible">
<img :src="upImgState.dialogImageUrl" alt="Preview Image" style="width: 100%" />
</el-dialog>
</el-form-item>
</template>
<template v-if="item.type == 'uploadSingleImg'">
<el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.key" :style="{
width: item.specialWidth || item.width || '10vw',
maxWidth: item.specialWidth || item.width || '10vw',
}">
<el-upload list-type="picture-card" :limit="1" :file-list="data.formInline[item.key]" :style="{
width: item.width || '10vw',
height: item.width || '10vw',
}" :action="item.option.importUrl" :data="item.option.uploadParames" :show-file-list="true"
:headers="{ Authorization: store.userInfo.token }"
:on-success="(info) => uploadSingleImg('success', info, item)"
:on-error="(info) => uploadSingleImg('error', info)" :before-upload="beforeUploadSingleImg" :before-remove="(info) => beforeRemoveSingleImg('remove', info, item)
" :on-preview="handlePictureCardPreview" accept="image/jpeg,image/png" :disabled="data.formInline[item.key] && data.formInline[item.key].length
? true
: false
">
<el-icon class="avatar-uploader-icon">
<Plus />
</el-icon>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
<el-icon><zoom-in /></el-icon>
</span>
<span class="el-upload-list__item-delete" @click="beforeRemoveSingleImg('remove', file, item)">
<el-icon>
<Delete />
</el-icon>
</span>
</span>
</div>
</template>
</el-upload>
<el-dialog v-model="upImgState.dialogVisible">
<img :src="upImgState.dialogImageUrl" alt="Preview Image" style="width: 100%" />
</el-dialog>
</el-form-item>
</template>
</template>
</el-form>
</template>
<script setup>
import { max, merge } from "lodash";
import { useAxios } from "@/utils/useAxios";
import axios from "axios";
import { transformObject, getAssetsFile, debounce } from "@/utils";
import { arrayToTreeRec } from "@/utils/utils.js";
import { mapDrawGraph } from "@/utils/map/mapDraw";
import { reactive, ref, watch } from "vue";
import useShoreBasedStore from "@/store/index";
import { BASEUrl } from "@/utils/request";
import { locationPoint } from "@/assets/js/commont";
import upIcon from "@/assets/images/table/upload.png";
const store = useShoreBasedStore();
const defaultTime = new Date();
const tableCloumn = [
{
label: "经度",
prop: "lat",
type: "lat",
inpModelParam: [
{ value: "jd", label: "度", msg: "大于等于0小于等于180的整数" },
{ value: "jf", label: "分", msg: "大于等于0小于60的整数" },
{ value: "jm", label: "秒", msg: "大于等于0小于60" },
],
},
{
label: "纬度",
prop: "lon",
type: "lon",
inpModelParam: [
{ value: "wd", label: "度", msg: "大于等于0小于等于90的整数" },
{ value: "wf", label: "分", msg: "大于等于0小于60的整数" },
{ value: "wm", label: "秒", msg: "大于等于0小于60" },
],
},
];
let props = defineProps({
dialogType: {
type: String,
default: "",
},
formItems: {
type: Array,
default: [],
},
formRules: Object,
formItemsVal: Object,
MapData: Object,
/* tableData: {
type: Array,
default: [],
}, */
inputTableData: {
type: Array,
default: [],
},
});
const ruleAreaFormRef = ref();
const validateInteger = (rule, value, callback) => {
if (Number(value) > 0) callback();
else callback(new Error("请输入大于零的数字"));
};
const data = reactive({
formInline: {
drawType: "3", //绘制类型,默认是面
level: "2",
tempObj: {}, //临时的选中lable
},
coordinate: {
jd: "",
jf: "",
jm: "",
wd: "",
wf: "",
wm: "",
},
selectData: [],
remoteSelectData: [],
count: 0, //下拉列表请求接口的 次数
bufferData: [
{ label: "一级缓冲距离:", level: 1, key: "buffDistance1" },
{ label: "二级缓冲距离:", level: 2, key: "buffDistance2" },
{ label: "三级缓冲距离:", level: 3, key: "buffDistance3" },
],
drawTypes: [
{
value: "1",
label: "点",
},
{
value: "2",
label: "线",
},
{
value: "3",
label: "面",
},
],
rules: {
level: [{ required: true, trigger: "change" }],
buffDistance1: [
{ required: true, message: "一级缓冲距离不能为空", trigger: "blur" },
{ validator: validateInteger, rigger: "blur" },
],
buffDistance2: [
{ required: true, message: "二级缓冲距离不能为空", trigger: "blur" },
{ validator: validateInteger, rigger: "blur" },
],
buffDistance3: [
{ required: true, message: "三级缓冲距离不能为空", trigger: "blur" },
{ validator: validateInteger, rigger: "blur" },
],
},
cityDefalut: {
label: "name",
value: "code",
children: "cities",
},
citiesData: [],
isAbled: false,
showDraw: true,
clearFlag: null, //联动值切换时清空初始化标志
editSelectValue: "",
remoteSelectLoading: false, //可以输入搜索的下拉框
headerApi: {},
headerResult: {
fileName: "",
address: "",
placeholder: "点击右侧按钮进行上传文件",
},
resTreeData: [],
treeData: [],
treeDataList: [],
treeLocationSelect: [],
storeTrseeData: [],
defaultProps: {
label: "name",
value: "pid",
},
});
const emit = defineEmits(["searchChange", "drawTypeChange", "controlDraw"]);
const searchChange = (
linkageKey,
optionsData,
target,
linkageDraw,
autoPosition,
dynamicLinkageKey,
list,
linkageKeyList,
targetItem
) => {
if (target?.length == 9) {
data.formInline.tempObj = optionsData?.filter((item) => {
return item.value === target;
});
}
if (linkageDraw) {
data.showDraw = linkageDraw(target);
emit("controlDraw", data.showDraw);
}
if (linkageKey) {
if (optionsData.length) {
//联动下拉数据静态
const result = optionsData.find((item) => {
return item.value === target;
});
data.clearFlag = target;
if (result?.url) {
const buildData = {
url: result.url,
optionParam: result.optionParam,
};
getSelectData(buildData);
} else {
data.selectData = [
{
label: "假数据",
value: "test",
},
];
}
} else {
//联动下拉数据动态获取
}
}
if (autoPosition) {
let positionTaget = null;
if (data.formInline.itemsString && data.formInline.itemsString === target) {
positionTaget = JSON.parse(target).guid;
} else {
positionTaget = target;
}
mapData.MapData.location(
mapData._layer.layers["windFarmArea"],
positionTaget,
12,
mapData._layer
);
}
if (dynamicLinkageKey) {
const linkagedTaget = list.find((item) => item.key === dynamicLinkageKey);
data.formInline[dynamicLinkageKey] = "";
getSelectData(linkagedTaget, {
[linkagedTaget.linkageMoreParamKey]: target,
});
}
if (linkageKeyList) {
finallyLinkageSolve(targetItem);
}
emit("searchChange", data.formInline);
};
//船舶白名单预警类型的控制逻辑
const checkAll = ref(true);
const isIndeterminate = ref(false);
const handleCheckAllChange = (val, formItemKey) => {
data.formInline[formItemKey] = val ? data.checkedAll : [];
isIndeterminate.value = false;
};
const handleCheckedChange = (value) => {
const checkedCount = value.length;
checkAll.value = checkedCount === data.checkedAll.length;
isIndeterminate.value =
checkedCount > 0 && checkedCount < data.checkedAll.length;
};
// 最终联动逻辑解决方案
const finallyLinkageSolve = async (targetItem) => {
if (!targetItem.linkageKeyList?.length) return;
const targetLinkItems = props.formItems.filter((aitem) =>
targetItem.linkageKeyList.includes(aitem.key)
);
if (targetItem.linkageAction) {
if (targetItem.linkOpenSetTimeout) {
setTimeout(() => {
targetItem.linkageAction(
data.formInline[targetItem.key],
targetLinkItems,
targetItem.optionsData,
data.formInline
);
}, 500);
} else {
let value =
data.formInline[targetItem.key] || data.formInline[targetItem.key] == 0
? data.formInline[targetItem.key]
: props.formItemsVal[targetItem.key];
targetItem.linkageAction(value, targetLinkItems, targetItem.optionsData);
}
}
};
const drawTypeChange = () => {
emit("drawTypeChange", data.formInline.drawType);
};
const validateForm = async () => {
let result = true;
await ruleAreaFormRef.value.validate((valid, fields) => {
const coordinateTarget = props.formItems.find(
(item) => item.type === "coordinate"
);
if (coordinateTarget && fields) {
const fieldsKey = Object.keys(fields);
if (fieldsKey.length === 1 && fieldsKey[0] === coordinateTarget.key) {
if (coordinateTarget.rule?.[0].required) {
for (let coorItem in data.coordinate) {
if (!data.coordinate[coorItem]) {
return (result = false);
}
}
return (result = true);
}
}
}
return (result = valid);
});
let formObj = Object.assign({}, data.formInline);
return {
isValidate: result,
formData: transformObject(formObj, ["cityValue", "drawType", "BufferArea"]),
coordinate: data.coordinate,
};
};
const resetForm = () => {
ruleAreaFormRef.value.resetFields();
};
const getFormItems = () => {
const formItemsKeys = props.formItems
.map((aitem) => aitem.key)
.filter(Boolean);
return formItemsKeys;
};
const getFromData = () => data.formInline;
//切换预警类型的选择
const warningLevelChange = (val) => {
if (val == 1) {
data.bufferData.forEach((el) => {
if (el.level > val && data.formInline[el.key] != "") {
data.formInline[el.key] = "";
}
});
}
};
const getSelectData = async (selectOption, moreparams) => {
const { resData } = await useAxios({
url: selectOption.url,
method: "post",
param: { pageNo: 1, pageSize: 0, ...moreparams },
});
if (resData.data && resData.data.length > 0) {
let seletData = resData.data.map((item) => {
return {
label: selectOption.optionParam && item[selectOption.optionParam.label],
value:
selectOption.optionParam &&
selectOption.optionParam.value !== "itemsString"
? item[selectOption.optionParam.value]
: JSON.stringify(item),
};
});
selectOption.optionsData = seletData;
//下拉框需要设置默认值的
if (selectOption.default) {
data.formInline[selectOption.key] = selectOption.optionsData[0].value;
if (selectOption.cablckData) {
selectOption.cablckData(selectOption.optionsData);
}
} else {
data.selectData = selectOption.optionsData;
if (selectOption.all)
selectOption.optionsData.unshift({ label: "全部", value: "" });
}
} else {
selectOption.optionsData = [{ label: "暂无数据", value: "" }];
}
};
const getSelectTreeData = async (selectOption, moreparams) => {
const { resData } = await useAxios(
{
url: selectOption.url,
method: selectOption.methed || "post",
param: selectOption?.isNoGetParams
? ""
: { pageNo: 1, pageSize: 10, ...moreparams },
},
selectOption.headers
);
const myModifyData = arrayToTreeRec({
data: resData.data.filter((el) => el.type !== -1),
pid: 0,
idKey: "pid",
pidKey: "parentId",
});
data.resTreeData = resData.data;
data.treeData = myModifyData;
};
const getListData = async (selectOption, moreparams) => {
const { resData } = await useAxios(
{
url: selectOption.url,
method: selectOption.methed || "post",
param: selectOption?.isNoGetParams ? "" : { ...moreparams },
},
selectOption.headers
);
let _topData = [];
const _temArr = resData.list ? resData.list : resData.data;
let _data = _temArr.map((el) => {
el.label = el.name;
el.value = el.pid;
el.parentId = 0;
if (selectOption.isPinxixi) {
if (!el.shipCnName) {
el.nameAndShipName = el.name;
_topData.push(el);
} else {
el.nameAndShipName = el.name + "【" + el.shipCnName + "】";
}
}
return el;
});
const _data2 = _data.filter((el) => el.shipCnName);
const _data3 = [..._topData, ..._data2];
const myModifyData = arrayToTreeRec({
data: selectOption.isPinxixi ? _data3 : _data,
pid: 0,
idKey: selectOption.isPinxixi ? "name" : "pid",
pidKey: "parentId",
});
data.treeDataList = myModifyData;
};
const gettreeLocationSelect = async (selectOption, moreparams) => {
if (!store.areaList) {
const { resData } = await useAxios(
{
url: selectOption.url,
method: selectOption.methed || "post",
param: selectOption?.isNoGetParams ? "" : { ...moreparams },
},
selectOption.headers
);
const _data = resData.list.map((el) => {
el.label = el.name;
el.value = el.pid;
el.parentId = 0;
if (selectOption.isPinxixi) {
el.nameAndShipName = el.name + "【" + el.shipCnName + "】";
}
return el;
});
const myModifyData = arrayToTreeRec({
data: _data,
pid: 0,
idKey: "pid",
pidKey: "parentId",
});
data.treeLocationSelect = myModifyData;
} else {
const myModifyData = arrayToTreeRec({
data: store.areaList.filter((el) => el.type !== -1),
pid: 0,
idKey: "pid",
pidKey: "parentId",
});
data.treeLocationSelect = myModifyData;
data.storeTrseeData = myModifyData;
}
};
//切换省市地址
const citySelectChange = (value) => {
data.formInline["province"] = value[0];
data.formInline["city"] = value[1];
};
// 自定义el-select组件的filter-method使得其可以编辑内容
const handleSelectEdit = (filterVal, item) => {
if (item.filterable) data.editSelectValue = filterVal;
};
const filtertreeLocationSelect = async (filterVal, item, type) => {
if (!filterVal) return;
data.remoteSelectLoading = true;
if (item.type == 'treeLocationSelect') {
const _keyParam = item.filterablePrama.keyParam
const moreparams = { [_keyParam]: filterVal, ...item.filterablePrama };
delete moreparams.keyParam
await handleSubFilter(item, moreparams);
data.remoteSelectLoading = false;
}
};
const handleSubFilter = async (selectOption, moreparams) => {
const { resData } = await useAxios(
{
url: selectOption.url,
method: selectOption.methed || "post",
param: selectOption?.isNoGetParams ? "" : { ...moreparams },
},
selectOption.headers
);
let myModifyData
const _listArr = resData.list ? resData.list : resData.data;
if (selectOption.isTrss) {
//四川省树状结构
const _data = _listArr.map((el) => {
el.label = el.name;
el.value = el.pid;
if (selectOption.isPinxixi) {
el.nameAndShipName = el.name + "【" + el.shipCnName + "】";
}
return el;
});
myModifyData = arrayToTreeRec({
data: _data.filter((el) => el.type !== -1),
pid: 0,
idKey: "pid",
pidKey: "parentId",
});
} else {
myModifyData = _listArr
}
data.treeLocationSelect = myModifyData;
};
// onMounted(() => {
// getCtitysData();
// mapData = props.MapData;
// data.headerApi = { Authorization: store.userInfo.token }
// })
//获取区域所属省市
const getCtitysData = async () => {
let requrl = "./json/city.json";
let resData = await axios.get(requrl).then((res) => {
return res.data.citiesData;
});
resData.map((item) => {
item.name = item.province;
return item;
});
data.citiesData = resData;
};
const setCheckBoxData = (item) => {
setTimeout(() => {
data.formInline[item.key] = item.optionCheckAll
? ["0", ...item.optionsData.reduce((pre, cre) => [...pre, cre.value], [])]
: [];
data.checkedAll = data.formInline[item.key];
}, 0);
checkAll.value = true;
isIndeterminate.value = false;
};
//从服务器搜索下拉框内容
const remoteMethod = (query, selectItem) => {
debounce(async () => {
if (!query) return;
data.remoteSelectLoading = true;
const { resData } = await useAxios(
{
url: selectItem.url,
method: "post",
param: { [selectItem.searchKey]: query },
},
selectItem.headers
);
const finalListData = selectItem.handleSpecialData
? selectItem.handleSpecialData(resData.data)
: resData.data;
// data.selectDataAllInfo = finalListData;
if (finalListData.length) {
data.remoteSelectData = finalListData.map((item) => {
if (selectItem.labelMulti)
return {
label:
item[selectItem.optionParam.label]?.trim() ||
item[selectItem.optionParam.otherlable]?.trim(),
value: item[selectItem.optionParam.value],
itemInfo: {
...selectItem.labelMulti,
addDataInfo: item,
},
};
return {
label:
item[selectItem.optionParam.label]?.trim() ||
item[selectItem.optionParam.otherlable]?.trim(),
value: item[selectItem.optionParam.value],
itemInfo: JSON.stringify(item),
};
});
} else {
//避免重复插入多个
let isAdd = data.remoteSelectData.filter((item) => item.value == query);
if (!isAdd.length) {
data.remoteSelectData.unshift({ label: query, value: query });
}
}
data.remoteSelectLoading = false;
setTimeout(() => {
data.remoteSelectLoading = false;
}, 1000);
}, 500)();
};
const getRemoteSelect = async (selectOption, moreparams) => {
if (props.dialogType == "edit") {
selectOption.pageInfo = {
[selectOption.searchKey]: props.formItemsVal[selectOption.searchKey],
};
}
const { resData } = await useAxios(
{
url: selectOption.url,
method: "post",
param: selectOption.pageInfo ?? { pageNo: 1, pageSize: 0, ...moreparams },
},
selectOption.headers
);
const finalListData = selectOption.handleSpecialData
? selectOption.handleSpecialData(resData.data)
: resData.data;
// data.selectDataAllInfo = finalListData;
if (finalListData.length) {
data.remoteSelectData = finalListData.map((item) => {
if (selectOption.labelMulti)
return {
label:
item[selectOption.optionParam.label]?.trim() ||
item[selectOption.optionParam.otherlable]?.trim(),
value: item[selectOption.optionParam.value],
itemInfo: {
...selectOption.labelMulti,
addDataInfo: item,
},
};
return {
label:
item[selectOption.optionParam.label]?.trim() ||
item[selectOption.optionParam.otherlable]?.trim(),
value: item[selectOption.optionParam.value],
};
});
}
};
const watchKeys = reactive([]);
watch(
() => props.formItemsVal,
(val) => {
data.isAbled = false;
if (val) {
data.isAbled = true;
data.formInline = {};
data.formInline = Object.assign({}, val);
}
},
{ immediate: true }
);
watch(
() => props.formItems,
(val) => {
//构造需要动态显示的表单下拉项数据
val.forEach((item) => {
if (item.key) {
//某些值是否需要监视
if (item.watchChage) {
watchKeys.push(item);
}
//!props.formItemsVal代表添加
if (!props.formItemsVal) {
data.formInline[item.key] =
item.defaultValue !== undefined && item.defaultValue !== null
? item.defaultValue
: "";
}
if (item.type == "select") {
if (item.optionsData.length == 0 && item.url) {
// 动态获取下拉框内容
getSelectData(item);
} else if (item.defaultVal) {
data.formInline[item.key] = item.defaultVal;
} else if (item.defaultVal && !props.formItemsVal) {
data.formInline[item.key] = item.defaultVal;
}
if (item.linkageKey) {
if (props.formItemsVal) {
data.formInline[item.key] = props.formItemsVal[item.key];
searchChange(
item.linkageKey,
item.optionsData,
data.formInline[item.key],
item.linkageDraw
);
}
}
}
if (item.type == "selectTree") {
if (item.optionsData.length == 0 && item.url) {
// 动态获取下拉框内容
getSelectTreeData(item);
}
}
if (item.type == "treeMuiltSelect") {
if (item.optionsData.length == 0 && item.url) {
data.tempObj = item;
getListData(item, item.moreparams);
} else {
data.treeDataList = item.optionsData;
}
}
if (item.type == "treeLocationSelect") {
if (item.optionsData.length == 0 && item.url) {
data.tempObj = item;
gettreeLocationSelect(item, item.moreparams);
}
}
if (item.type == "textarea" && item.defaultVal)
props.dialogType !== "edit" &&
(data.formInline[item.key] = item.defaultVal);
if (item.type == "checkBox") {
data.formInline[item.key] = [];
//item.optionCheckAll==true,默认填充一个全部类型的值为0
setCheckBoxData(item);
}
if (item.type == "remoteSelect") {
getRemoteSelect(item);
}
if (item.rule) {
merge(data.rules, { [item.key]: item.rule });
}
// 联动逻辑最终解决方案(优于之前的分散的联动逻辑)
finallyLinkageSolve(item);
}
});
},
{
immediate: true,
}
);
let instance = null; //绘制地理要素
let drawLayer = null; //绘制时临时创建的图层
let mapData = {};
//初始化绘制
const drawArea = () => {
initDraw();
drawLayer && drawLayer.getSource().clear();
instance = mapDrawGraph(
mapData.MapData,
drawLayer,
"Point",
handlerDarwResult
);
};
const initDraw = () => {
let _Map = mapData;
instance && _Map.MapData.GlobalMap.removeInteraction(instance);
drawLayer = _Map._layer.creatTempLayer({
layerName: "drawTempPoint",
title: "绘制临时图层选点",
zindex: 10,
});
};
const handlerDarwResult = (drawResult) => {
if (drawResult.coods.length !== 0) {
let myCo = Object.assign({}, drawResult.coods);
data.coordinate = myCo[0];
for (let key in data.coordinate) {
data.coordinate[key].indexOf("") >= 0 &&
(data.coordinate[key] = data.coordinate[key].trim());
}
}
instance = null;
};
const handleOpenDialog = (row, item) => {
item.getRowInfo({ ...row, ...item, ...data.formInline });
};
const uploadRef = ref();
const handleResult = (state, info, item) => {
if (state === "success") {
const { data: resData } = info;
const { address, fileName } = resData;
const _fileName = !fileName
? "附件名称" + resData.split("/")[resData.split("/").length - 1]
: fileName;
if (!fileName) {
data.formInline[item.key] = resData;
data.headerResult.fileName = resData;
} else {
data.formInline.fileName = _fileName;
data.formInline.address = address;
}
item.getRowInfo({ ...resData, ...item, ...data.formInline });
}
ElMessage({
type: state,
message: `导入文件上传${state === "success" ? "成功" : "失败"}`,
});
};
const fileClear = (state, info, item) => {
if (state === "fileClear") {
data.formInline[item.key] = "";
const params = {
fileName: info,
fileType: item.option[0].uploadParames.fileType,
};
item.clearFunc(params);
}
};
const uploadSingleImg = (state, info, item) => {
if (state === "success") {
const { data: resData } = info;
item.getRowInfo(resData);
upImgState.dialogImageUrl = BASEUrl + "/file/" + resData;
const _fileList = data.formInline[item.key] || [];
_fileList.unshift({ url: upImgState.dialogImageUrl });
data.formInline[item.key] = _fileList;
}
ElMessage({
type: state,
message: `单个文件上传${state === "success" ? "成功" : "失败"}`,
});
};
const beforeUploadSingleImg = (rawFile) => {
if (!rawFile) return false;
if (rawFile.type !== "image/jpeg" && rawFile.type !== "image/png") {
ElMessage.error("文件类型须为图片格式!");
return false;
} else if (rawFile.size / 1024 / 1024 > 10) {
ElMessage.error("文件大小须小于10MB!");
return false;
}
return true;
};
const beforeRemoveSingleImg = (state, info, item) => {
if (state === "remove") {
let _fileName = "";
if (!info.response) {
_fileName = info.url.split("/")[info.url.split("/").length - 1];
} else {
_fileName =
info.response.data.split("/")[info.response.data.split("/").length - 1];
}
const _fileType = item.option.uploadParames.fileType;
item.removeImg({
title: "删除图片",
fileName: _fileName,
fileType: _fileType,
});
data.formInline[item.key] = [];
}
return true;
};
const upImgState = reactive({
dialogVisible: false, //预览弹框显示与否
dialogImageUrl: "",
fileList: [],
});
const handlePictureCardPreview = (file) => {
upImgState.dialogImageUrl = file.url || BASEUrl + file.url;
upImgState.dialogVisible = true;
};
const handlechange = (val, limit, isJump) => {
if (isJump) {
let _data = {};
let _val = val;
if (Array.isArray(val)) {
_val = _val[_val.length - 1];
}
_data = data.resTreeData.find((item) => item.pid == _val);
if (!_data) return;
locationPoint(_data.geom);
}
if (!limit) return;
if (limit == "有联动") {
getListData(data.tempObj, {
...data.tempObj.moreparams,
areaIdCondition: val,
});
}
if (limit == "有联动2") {
getListData(data.tempObj, {
organization: val,
});
}
};
const handleSelectchange = (val, item) => {
const _findObj = store.areaList.find((el) => el.pid == val[val.length - 1]);
if (_findObj) {
const isOk =
Array.isArray(_findObj.ferryPortAreaVos) &&
_findObj.ferryPortAreaVos.length > 0;
if (!isOk) {
data.formInline[item.key] = data.formInline[item.key].filter((el) => {
return el !== val[val.length - 1];
});
ElMessage.warning("该区域下暂无渡口!");
}
}
};
const blur = (item, type) => {
if (item.type === "treeLocationSelect") {
zeroSelectData();
}
};
const zeroSelectData = () => {
data.treeLocationSelect = data.storeTrseeData
}
defineExpose({
validateForm,
resetForm,
getFormItems,
getFromData,
getListData,
});
</script>
<style lang="scss" scoped>
.area-from {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
:deep(.el-form-item) {
margin-right: 0px;
// margin-bottom: 20px;
max-width: 50%;
.el-form-item__label {
color: #fff;
font-size: 16px;
padding: 0px;
padding-right: 10px;
min-width: 100px;
display: flex;
justify-content: flex-end;
align-items: center;
line-height: 22px;
}
.el-form-item__error {
white-space: nowrap;
}
.el-input__wrapper {
background: rgba(26, 136, 198, 0.3);
box-shadow: none;
border: 1px solid #62a3ff;
border-radius: unset;
.el-input__inner {
color: #fff;
}
.el-input__inner::placeholder {
color: #d0eeff;
}
.el-input__suffix {
color: #fff;
}
}
.el-select__wrapper {
background-color: #08387e;
box-shadow: 0 0 0 1px #62a3ff inset;
}
.el-select__placeholder {
color: #fff;
}
.el-radio__label {
color: #fff;
}
.el-select .el-input__wrapper.is-focus {
box-shadow: unset !important;
}
.el-input--suffix {
color: #d0eeff !important;
}
.unit {
color: #fff;
margin-left: 5px;
}
.el-radio-group {
min-width: 300px;
}
}
.ship-white-warn {
max-width: 100% !important;
.warn-type {
display: flex;
align-items: center;
:deep(.el-checkbox-group) {
margin-left: 10px;
}
}
}
.select-area {
width: 100%;
>.el-form-item {
width: 100%;
max-width: 100%;
margin-right: 0;
}
:deep(.select-trigger) {
height: 100%;
.el-input {
height: 100%;
}
}
:deep(.el-select__tags .el-tag--info) {
background-color: rgb(16, 159, 204) !important;
color: #ffffff;
height: 30px;
}
:deep(.el-tag .el-tag__close) {
color: #ffffff;
}
}
.area-remark {
// margin-top: 10px;
width: 100%;
>.el-form-item {
width: 100%;
max-width: 100%;
margin-right: 0;
.el-textarea {
// width: 91%;
:deep(.el-textarea__inner) {
background-color: rgba(26, 136, 198, 0.3);
border: 1px solid #62a3ff;
border-radius: unset;
outline: none;
box-shadow: unset;
color: #ffffff;
&::placeholder {
color: #d0eeff;
}
}
}
}
}
.warning-level {
display: flex;
justify-content: space-between;
width: 100%;
}
.warning-distance {
width: 100%;
display: flex;
justify-content: space-between;
:deep(.el-form-item) {
.el-form-item__content {
flex-wrap: nowrap;
}
}
}
.cordinate-container {
display: flex;
align-items: center;
:deep(.el-form-item) {
max-width: 100%;
margin-right: 0;
.el-form-item__content {
flex-wrap: nowrap;
}
}
.wrapper {
// width: 80%;
background: rgba(26, 136, 198, 0.3) !important;
border: 1px solid #62a3ff;
padding: 6px 16px;
display: flex;
// flex-wrap: nowrap;
.row-input-box {
display: flex;
align-items: center;
&:first-child {
margin-right: 22px;
}
.row-name {
margin-right: 8px;
white-space: nowrap;
font-size: 14px;
font-family: Microsoft YaHei-Regular, Microsoft YaHei;
font-weight: 400;
color: #d0eeff;
line-height: 22px;
}
.input-item {
display: flex;
align-items: center;
.inp-unit {
margin: 0 6px;
font-size: 12px;
font-family: Microsoft YaHei-Regular, Microsoft YaHei;
font-weight: 400;
color: #52cbff;
line-height: 19px;
}
.el-input {
// width: 50px;
:deep(.el-input__wrapper) {
border: unset;
border-radius: unset;
background: #074665;
padding: 1px;
>input {
text-align: center;
}
}
}
}
}
}
.box {
cursor: pointer;
margin-left: 5px;
display: inherit;
align-items: inherit;
}
}
.psw_home {
padding-bottom: 15px;
}
.opration-box,
.render-box {
display: flex;
justify-content: center;
span {
margin-right: 5px;
}
img {
width: 16px;
}
}
:deep(.el-input-group__append) {
border-left: 0;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
box-shadow: 0 1px 0 0 #62a3ff inset, 0 -1px 0 0 #62a3ff inset,
-1px 0 0 0 #62a3ff inset;
background-color: #075480;
padding: 0px 10px;
background-color: #095680;
}
.avatar-uploader {
width: 50px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
border: 1px dashed #d9d9d9;
img {
width: 98%;
height: 98%;
object-fit: cover;
}
}
}
</style>
<style lang="scss">
.custom-header {
.el-checkbox {
display: flex;
height: unset;
}
// .el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
// color: #606266 !important;
// }
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after {
// background-color: rgb(47, 255, 127) !important;
width: 25px;
height: 25px;
font-weight: bolder;
font-size: 16px;
}
}
</style>
调用组件AreaFormItem.vue的方式:
//调用组件
<template>
<AreaFormItem
ref="areaForm"
:formItems="formItems"
:formItemsVal="state.formItemsVal"
/>
</template>
<script setup>
import { nextTick, onMounted, reactive, ref, watch } from "vue";
import AreaFormItem from "@/components/Form/AreaFormItem.vue";
const state = reactive({
formItemsVal: {},
});
//备注:state.formItemsVal用于表单回显
const formItems = [
{
type: "datePicker",
label: "签发日期",
key: "dateOfIssue",
rule: [
{
required: true,
message: "签发日期不能为空!",
trigger: "blur",
},
],
},
{
type: "datePicker",
label: "截止日期",
key: "deadline",
rule: [
{
required: true,
message: "截止日期不能为空!",
trigger: "blur",
},
],
},
{
type: "select",
label: "文件类型",
key: "type",
optionsData: [
{
label: "船舶经营许可证",
value: 0,
},
{
label: "船舶管理人许可证",
value: 7,
},
],
defaultVal: 0,
rule: [
{
required: true,
message: "文件类型不能为空!",
trigger: "blur",
},
],
},
{
type: "uploadSingleImg",
label: "渡船证书",
key: "file",
option: {
uploadParames: {
fileType: 0, //渡船相关证书
},
img_bg: "table/upload.png",
info: "upload",
text: "上传",
importUrl: BASEUrl + "/data/file/upload",
},
width: "100%",
getRowInfo: (data) => {
console.log("上传图片的结果", data);
state.formItemsVal.file = data;
},
removeImg: (data) => {
api.fileApi
.deleteFile({ fileName: data.fileName, fileType: data.fileType })
.then((res) => {
if (res.status === 200 && res.data.code === 200) {
ElMessage({
type: "success",
message: "删除成功",
});
}
})
.catch((err) => {
console.log(err);
});
},
rule: [
{
required: true,
message: `${state.pageTheme}不能为空!`,
trigger: "blur",
},
],
},
];
<script>
写到这儿就实现了表单组件的封装,根据调用组件传参的formItems变量的type类型渲染不同的表单组件例如:input、select等组件。。。最终随着项目的进行,表单组件的其他类型:
'location', 'medium','password', 'sexRadio', 'coordinate','radio','textarea','multipleSelect', 'uploadFile','uploadSingleImg',也会封装。
更多推荐


所有评论(0)