背景:
技术框架:
uniapp框架(vue2语法)+uView组件库。
通过form表单实现数据列表的"查询"功能。注意:
1、<u--form>内部嵌套<u-form-item>,<u-form-item>内部嵌套<u--input>表单组件。
2、H5浏览器端,input输入框可以整块点击。但uniapp打包成App,不能整块点击;在平板上运行,可以整块点击。
3、input实现 select下拉列表功能
4、在App上,实现input输入框的整块点击,点击之后出现select弹框区域。
5、Form表单封装组件。接收绑定Form的数据(父传子formItems[]);监听formInline的变化,监听到变化后emit(子传父,)
效果展示:
官网链接:点击跳转官网 form
接下来便是要解决的问题:让整块input输入框都可以点击。。。
一、selet下拉列表的实现
实现思路:
input输入框,通过点击右侧的插槽图标,实现select弹框弹出。实际是两部分组成。。。
javascript
<u--input
:readonly="true"
v-model="inputValue"
:placeholder="'请选择'"
suffixIcon="arrow-down"
>
</u--input>
下拉列表:
javascript
<!-- 下拉列表显示 -->
<u-action-sheet
:show="showOption"
:actions="currentSelectItem.option.optionList"
:title="currentSelectItem.option.title"
:description="currentSelectItem.option.description"
@close="handleCloseOption(false)"
@select="handleSelect"
v-if="currentSelectItem.option"
>
</u-action-sheet>
官网链接:点击跳转官网 action-sheet
二、input框整块点击,打开select弹框
实现思路:
要想实现input框的整块点击。
实现思路是:
1、给当前input外层套一个盒子,在此盒子上绑定@click事件;
2、给input添加鼠标点击样式:style="pointer-events:none"。
none表示:鼠标事件"穿透"该元素并且指定该元素"下面"的任何东西。
给input外层套一层盒子,并绑定事件:
javascript
<template @click='handleOpenOption(item)'>
<u--input
:readonly="true"
style="pointer-events:none"
v-model="inputValue"
:placeholder="'整块点击'"
suffixIcon="arrow-down"
>
</u--input>
</template>
事件:
javascript
methods: {
handleOpenOption(item) {
if (item.type === 'calendar') {
this.currentSelectItem = {
...item
};
this.handleCloseCalendar(true);//打开日期选择弹框
}
if (item.type === 'select') {
this.currentSelectItem = {
...item
};
this.handleCloseOption(true);//打开select弹框
}
},
}
三、封装form表单组件
背景:
理论知识:
父传子:通过props,获取到传递到子组件的formItems[];
子传父:通过watch监听表单组件绑定的数据的变化,使用emit('自定义事件名',传给父组件的数据值)
图片:
formItems封装代码:
javascript
//formItems分装代码
<template>
<view>
<u--form labelPosition="left" :model="formInline" :rules="formRules" ref="uForm" :errorType="errorType">
<u-form-item :required="item.rule.required" labelWidth="230rpx" labelAlign="right" :label="item.label"
:prop="item.key" :borderBottom="false" @tap="handleOpenOption(item)" v-for="item, index in formItems"
:key="index">
<u--input v-model="formInline[item.key]" :placeholder="item.placeholder" v-if="item.type === 'input'"
:customStyle="item.customStyle"></u--input>
<!-- input带后置图标 -->
<u--input v-model="formInline[item.key]" :placeholder="item.placeholder" suffixIcon="search"
suffixIconStyle="color: #333333;font-size: 42rpx" v-if="item.type === 'input2'"
:customStyle="item.customStyle" :clearable="true" @clear="handleClear(item.key)"></u--input>
<!-- 以下点击设置hideKeyboard(),让移动端不弹出键盘 -->
<template @click='handleOpenOption(item)'>
<u--input :readonly="true" style="pointer-events:none" v-model="formInline[item.key].name"
:placeholder="item.placeholder" suffixIcon="arrow-down" v-if="item.type === 'select'"
:customStyle="item.customStyle"></u--input>
</template>
<template @click='handleOpenOption(item)'>
<u--input :readonly="true" style="pointer-events:none" v-model="formInline[item.key]"
:placeholder="item.placeholder" suffixIcon="clock" v-if="item.type === 'calendar'"
:customStyle="item.customStyle" :clearable="true" @clear="handleClear(item.key)" ></u--input>
</template>
</u-form-item>
</u--form>
<!-- 下拉列表显示 -->
<u-action-sheet :show="showOption" :actions="currentSelectItem.option.optionList"
:title="currentSelectItem.option.title" :description="currentSelectItem.option.description"
@close="handleCloseOption(false)" @select="handleSelect" v-if="currentSelectItem.option">
</u-action-sheet>
<!-- 日历显示 -->
<u-calendar :closeOnClickOverlay="true" monthNum="12" :minDate="minDate" :maxDate="maxDate" :show="showCalendar"
mode="range" :allowSameDay="true" @confirm="handleConfirmCalendar"
@close="handleCloseCalendar(false)"></u-calendar>
</view>
</template>
<script>
import {
getLayerData
} from '@/api/index.js'
export default {
name: "FormItem",
data() {
return {
formInline: {},
formRules: {},
showOption: false,
currentSelectItem: {},
showCalendar: false,
readonlyCalendar: true,
minDate: '2023-01-01',
maxDate: '2023-11-07',
};
},
props: {
formItems: Array,
errorType: {
type: String,
default: 'none'
}
},
computed: {},
watch: {
formItems: {
immediate: true,
handler(newval, oldval) {
newval.forEach(item => {
// 动态请求下拉列表
(item.type === 'select' && item.option.optionsSrc) && this.getOptionList(item);
this.getFormRules();
this.getFormInlineDefault();
});
}
},
formInline: {
immediate: true,
deep: true,
handler(newval, oldval) {
this.$emit('searchData', newval);
}
}
},
methods: {
handleConfirmCalendar(e) { //日历选项选择完毕确认
this.formInline[this.currentSelectItem.key] = e[0] + '至' + e[e.length - 1]
this.handleCloseCalendar(false);
// this.readonlyCalendar = !this.readonlyCalendar;
},
handleCloseCalendar(state) { //日历选项关闭
this.showCalendar = state;
},
handleOpenOption(item) {
if (this.readonlyCalendar) {
if (item.type === 'calendar') {
this.currentSelectItem = {
...item
};
this.handleCloseCalendar(true);
}
}
if (item.type === 'select') {
//当前选择框的信息,因为复用了同一个下拉选择弹出层
this.currentSelectItem = {
...item
};
this.handleCloseOption(true);
}
},
handleSelect(e) { //下拉选项选择目标逻辑
this.formInline[this.currentSelectItem.key] = e;
},
handleCloseOption(state) { //下拉选项关闭逻辑
this.showOption = state;
},
async getOptionList(item) {
const {
data: res
} = await getLayerData(item.option.optionsSrc);
const resData = res.data;
item.option.optionList = resData.reduce((cre, pre) => [...cre, {
name: pre[item.option.optionContent.name],
// 下拉选项是对各值还是一个值
value: item.option.optionContent.value === 'objString' ? JSON.stringify(pre) : pre[
item.option.optionContent.value]
}], [...item.option.options]);
this.getFormInlineDefault();
},
getFormInlineDefault() {
this.formInline = this.formItems.reduce((cre, pre) => {
let handlePre = '';
if (pre.defaultVal) {
if (pre.defaultVal instanceof Array) {
// 动态请求的内容作为默认值
if (pre.option.optionList?.length > pre.defaultVal[0]) {
handlePre = pre.option.optionList[pre.defaultVal[0]];
} else {
const emptyTarget = pre.option.optionList?.find(item => item.value === '')
handlePre = emptyTarget || '';
}
} else {
handlePre = pre.defaultVal;
}
} else {
// const emptyTarget = pre.option?.optionList?.find(item => item.value === '')
// handlePre = emptyTarget || '';
handlePre = ''
}
return {
...cre,
[pre.key]: handlePre
}
}, {});
},
getFormRules() {
this.formRules = this.formItems.reduce((cre, pre) => {
return pre.rule && {
...cre,
[pre.key]: pre.rule
}
}, {});
},
handleClear(_key) {
this.formInline[_key] = '';
this.readonlyCalendar = true;
this.readonlySelect = true
},
clickshowOption() {
this.showOption = !this.showOption
},
getCurrentData() {
const d = new Date()
const year = d.getFullYear()
let month = d.getMonth() + 1
month = month < 10 ? `0${month}` : month
const date = d.getDate()
return [`${year}-${month}-${date}`]
},
getMinDate(_yesteMonth) {
const yesteMonth = _yesteMonth || 2
const d = new Date()
const year = d.getFullYear()
let month = d.getMonth() - yesteMonth
month = month < 10 ? `0${month}` : month
const date = d.getDate()
return `${year}-${month}-${date}`
}
},
onLoad() {
this.formInline = {}
},
created() {
this.maxDate = this.getCurrentData()[0]
this.minDate = this.getMinDate(6)
}
}
</script>
<style scoped lang="scss">
/deep/.u-form {
>.u-form-item {
>.u-form-item__body {
padding: 0;
>.u-form-item__body__right {
// background-color: #F6F7FA;
/* border-radius: 12rpx;
border: 1px solid #E3E7EE;
padding: 10rpx; */
}
}
}
}
// /deep/ .u-input__content__clear {
// position: relative;
// }
// /deep/.u-icon__icon {
// position: absolute;
// left: 0px;
// right: 0px;
// top: 0px;
// bottom: 0px;
// justify-content: center;
// }</style>
父组件使用方法:
javascript
//父传子myformItems
//子传父触发事件mysearchData
<FormItem :formItems="myformItems" @searchData="mysearchData"></FormItem>
上面用到的数据和方法:
数据formItems,示例如下:
javascript
[
{
type: "input2",
// label: "船舶名称",
key: "name",
placeholder: "请输入船舶名称",
rule: {
required: false,
},
customStyle: {
"margin-bottom": "20rpx",
},
},
{
type: "input2",
// label: "MMSI",
key: "mmsi",
placeholder: "请输入MMSI",
rule: {
required: false,
},
customStyle: {
"margin-bottom": "20rpx",
},
},
],
mysearchData事件,示例如下:
javascript
methods: {
mysearchData(_data) {
console.log(_data);
this.pages.name = _data.name
this.pages.mmsi = _data.mmsi
this.pages.pageNum = 1
this.pages.pageSize = 7
this.getTableList(this.pages)//查询接口
}
}