https://vant-contrib.gitee.io/vant/v2/#/zh-CN/field
1、做下拉选择组件
vant 没有elementui那种封装好的组建,需要自己独立维护
1、制作下拉选择项
设置输入项只能为【只读】,【可以点击】 ,追加一个【点击事件】
注意这里value值是label标签值,非实际选中的value值, Visible变量是为了控制下拉列表的弹层展开关闭
<van-field readonly clickable label="所属公司" :value="coName" placeholder="所属公司" :rules="rules.sysArCoId" required @click="coNameVisible = true" />
2、制作下拉列表弹层
弹层绑定Visible变量打开和关闭
picker组件就是实际的el-select组件,
colums属性用来放下拉的数据集合,
value-key就是这个picker组件要展示的label值,
分别设置【取消事件】和【确认事件】
<van-popup v-model="coNameVisible" round position="bottom">
<van-picker title="请选择所属公司" show-toolbar :columns="corpList" value-key="coName" @cancel="coNameVisible = false" @confirm="sysArCoIdConfirm" />
</van-popup>
3、编写【确认事件】的逻辑:
async sysArCoIdConfirm(val) {
/* val 返回数据集合选中的元素,元素是什么类型,val就是什么类型 */
this.form.sysArCoId = val.id
/* 设置好上面的value值之后,还要回显下拉的label值 */
this.coName = this.form.coName = val.coName
/* 然后关闭下拉弹层 */
this.coNameVisible = false
/* 下面这些逻辑是联动其它选项的 */
/* 重置所属部门 */
this.deName = ''
this.deptList = []
this.form.sysArDeId = ''
/* 重新初始化 */
await this.initialAllocatedDepartmentList(val.id)
2、做查询条件,时间范围查询组件
1、组件标签
<div style="display: block;">
<span>申请时间</span>
<span style="position: relative;display: block;">
<van-field v-model="dateRange" style="width: 91vw;" placeholder="请选择申请时间范围" :clearable="true" readonly @click="dateVisible = true" />
<van-icon name="clear" class="search-clear-icon" @click="clearDateRange" />
</span>
2、data属性变量:
dateRange: '',
dateVisible: false,
/* 时间范围设置 */
minDate: new Date(2022, 1, 1),
maxDate: new Date(2099, 12, 31)
3、时间范围选择弹层:
<!-- 时间范围筛选 -->
<van-popup v-model="dateVisible" position="bottom">
<van-calendar v-model="dateVisible" :color="$ui.color" type="range" :min-date="minDate" :max-date="maxDate" :allow-same-day="true" @confirm="addDateRange" />
</van-popup>
4、确认事件的赋值操作:
addDateRange(values) {
this.dateRange = values[0].getFullYear() + '/' + (values[0].getMonth() + 1) + '/' + values[0].getDate() + ' ~ ' + values[1].getFullYear() + '/' + (values[1].getMonth() + 1) + '/' + values[1].getDate()
this.dateVisible = false
this.onRefresh()
3、下拉列表可搜索选项:
预览效果:
参考博客:
https://blog.csdn.net/qq_41426442/article/details/124567038
这里我封装成一个组件:
只是组合了一下,Props参数通过Field和Picker对象传入
包括事件的方法,注意这里方法传入
需要组件声明一个方法提供到Vant组件进行调用,然后再通过Props参数传给这个组件(否则无法调用)
<template>
<!-- 表单项 -->
<van-field readonly clickable :label="field.label" :value="field.value" :placeholder="field.placeholder" :rules="field.rule" required @click="selectVisible = true" />
<!-- 选择器 -->
<van-popup v-model="selectVisible" round position="bottom" closeable>
<van-field v-model="searchInput" :placeholder="picker.placeholder" left-icon="search" @input="pickerSearch" />
<van-picker :title="picker.title" show-toolbar :columns="picker.data" :value-key="picker.valueKey" @cancel="selectVisible = false" @confirm="pickerConfirm" />
</van-popup>
</template>
<script>
export default {
name: 'SearchSelect',
props: {
field: {
type: Object,
required: true,
default() {
return {
label: '选项名称',
placeholder: '选项名称',
value: '',
rule: []
picker: {
type: Object,
required: true,
default() {
return {
title: '选择器标题',
placeholder: '选择器文本',
valueKey: '',
data: [],
inputMethod: (search) => {},
confirmMethod: () => {}
data() {
return {
selectVisible: false,
searchInput: ''
methods: {
pickerSearch(search) {
this.picker.inputMethod(search)
pickerConfirm(val) {
this.picker.confirmMethod(val)
this.selectVisible = false
</script>
使用组件:
1、使用标签,绑定参数对象
2、组件注册:
3、Data属性填写:
直接在属性中将方法作为固定变量
/* 文件所属项目变量 */
projectList: [],
inField: {
label: '文件所属项目',
placeholder: '文件所属项目',
value: '',
rule: [{ required: true, message: '请选择文件所属项目' }]
inPicker: {
title: '请选择文件所属项目',
placeholder: '项目名称',
valueKey: 'inName',
data: [],
inputMethod: (search) => {
this.inPicker.data = this.projectList.filter(x => x.inName.indexOf(search) > -1)
confirmMethod: (val) => {
this.form.salPrInId = val.id
this.inField.value = val.inName
4、数据属性问题:
可以看inputMethod的写法,需要一份原数据和展示数据
这里贴上数据获取的接口:
const { data: projectList } = await getInfoList({ inApprState: '1' })
this.projectList = projectList
this.inPicker.data = projectList
4、多选下拉列表:
组件标签:
<!-- 表单组件 -->
<van-field readonly clickable label="印章类型" :value="sealTypeName" placeholder="印章类型" :rules="rules.afSealType" required @click="sealTypeVisible=true" />
<!-- 多选选择器 -->
<van-popup v-model="sealTypeVisible" round position="bottom" closeable close-icon="close" style="height: 40vh" @close="closeSealTypeSelect">
<van-checkbox-group ref="checkboxGroup" v-model="checkedValue" style="margin-top: 60px" @change="change">
<van-cell-group>
<van-cell v-for="(item, idx) in sealTypeList" :key="`sealType${idx}`" clickable @click="cbxToggle(idx)">
<template #title>
<span :class="item.checked ? 'custom-title' : ''">{{ item.diName }}</span>
</template>
<template #right-icon>
<van-checkbox ref="checkboxes" v-model="item.checked" :name="item" />
</template>
</van-cell>
</van-cell-group>
</van-checkbox-group>
</van-popup>
data变量:
/* 印章类型变量 */
sealTypeName: '',
sealTypeVisible: false,
sealTypeList: [],
checkedValue: []
方法变量:
/* 关闭下拉时设置选中的下拉项和回显翻译值 */
closeSealTypeSelect() {
this.sealTypeName = this.checkedValue.map(el => el.diName).toString()
this.form.afSealType = this.checkedValue.map(el => el.diCode).toString()
this.sealTypeVisible = false
/* 监听Change事件变化 */
change() {},
/* 复选框勾选切换事件 */
cbxToggle(index) {
this.$refs.checkboxes[index].toggle()
this.sealTypeList[index].checked = !this.sealTypeList[index].checked
文本跟随选中时颜色变化:
<style scoped>
.custom-title {
color: #1890FF
</style>
5、行政区域联动下拉选择
行政区域选择,我看到有van-cascader做下拉,但是同事用的还是picker来做,查看文档发现
下级的联动是读取children属性实现,但是我返回的数据不是children属性,是treeNodeChildren
查看文档之后发现没有专门声明children指定的配置参数,可恶啊,那我这里只能想到替换js对象属性来完成了
参考方法:
https://blog.csdn.net/qq_40055200/article/details/127413479
我的方法:
找到treeNodeChilren属性,然后赋值给新的属性
第二步,删掉原属性
第三步,判断新赋值属性是否存在子集合,有则递归方法继续替换
methods: {
changeTreePropRecursive(children) {
children.forEach(el => {
el['children'] = el['treeNodeChildren']
delete el['treeNodeChildren']
if (el['children'] && el['children'].length > 0) this.changeTreePropRecursive(el['children'])
组件标签使用:
<van-field readonly clickable label="客户地区" :value="cuCityName" placeholder="客户地区" @click="cuCityVisible=true" />
<van-popup v-model="cuCityVisible" round position="bottom">
<van-picker title="请选择客户地区" show-toolbar :columns="cuCityList" value-key="arName" @cancel="cuCityVisible = false" @confirm="cuCityConfirm" />
</van-popup>
数据变量:
/* 客户地区变量 */
cuCityName: '',
cuCityVisible: false,
cuCityList: [],
确认点击事件:
cuCityConfirm(val, idx) {
this.form.cuCityCode = this.cuCityList[idx[0]].children[idx[1]].children[idx[2]].arCode
this.cuCityName = val[0] + '-' + val[1] + '-' + val[2]
this.cuCityVisible = false
区域树加载的数据:
async initArea() {
const { data: cuCityList } = await getCachedAreaTree()
this.changeTreePropRecursive(cuCityList)
this.cuCityList = cuCityList
移动端表格组件的解决方案
Vxe-Table 官方文档地址:
https://vxetable.cn/v3/#/table/start/install
安装教程见文档:
https://blog.csdn.net/qq_40323256/article/details/127717133
发现在单独操作数据时,表格会不更新渲染
解决办法是手动调用重新加载方法:
this.$refs.xTable.reloadData(this.form.cuBankList)
xTable是ref属性的值:
<vxe-table
ref="xTable"
class="table-scrollbar"
border
resizable
show-overflow
size="mini"
show-footer
width="1200"
:row-config="{ isHover: true }"
:data="form.cuBankList"
<vxe-column type="seq" width="60" title="序号" align="center" show-overflow />
<vxe-column field="cbAcNumber" width="200px" title="账户号码" show-overflow />
<vxe-column field="cbAcName" width="160px" title="账户名称" show-overflow />
<vxe-column field="cbBaName" width="160px" title="银行名称" show-overflow />
<vxe-column field="cbBaSubbranch" width="160px" title="支行名称" show-overflow />
<vxe-column field="cbBaProvince" width="160px" title="开户行省" show-overflow />
<vxe-column field="cbBaCity" width="160px" title="开户行所在市" show-overflow />
<vxe-column field="cbAcCurrency" width="160px" title="币种" :formatter="formatterCbAcCurrency" />
<vxe-column field="sealupState" width="120px" align="center" title="封存状态" :formatter="formatterSealupState" />
<vxe-column title="操作" min-width="100" show-overflow align="center">
<template #default="{ row }">
<vxe-button type="text" icon="vxe-icon-edit" @click="openEditPopup(row)" />
<vxe-button type="text" icon="vxe-icon-delete" @click="removeDetail(row)" />
</template>
</vxe-column>
</vxe-table>
2023年03月09日 更新:
1、Element 和 Vant 表单 暂存功能 数据兼容问题:
暂存功能前提情要:
https://www.cnblogs.com/mindzone/p/16802692.html
Element负责PC端页面,Vant负责钉钉H5页面
两个前端项目,同一个业务功能
普通输入的表单项是一致的,但是Select是不一样的
el-select只需要提供数值本身即可,但是van-picker这边是和van-field组合使用
回显需要提供label值给van-field,暂存的数据通用,这会出现一个问题:
在PC端暂存的数据,在手机端回显暂存的时候,label值缺失:
解决思路是在PC端暂存的时候,把手机端需要的label值放进去
先来看最基础的单选Select标签:
element官方文档上@change函数返回的只提供value值
<el-form-item label="所属公司" prop="sysArCoId" :style="commonStyle">
<el-select ref="sysArCoIdSelect" v-model="form.sysArCoId" :style="inputStyle" size="mini" clearable placeholder="请选择" @change="selectCompanyChange">
<el-option v-for="(item, idx) in companyList" :key="`sysArCoId${idx}`" :label="`${item.coName} | ${item.coCode}`" :value="item.id" />
</el-select>
</el-form-item>
给vant-field装填label值需要从element这边获取
有两种获取方式:
第一种使用@click.native
参考CSDN博客:
https://blog.csdn.net/m0_38038870/article/details/123418588
点击时触发事件,直接获取选中的option,然后把值放到表单对象里面
<el-option v-for="(item, idx) in projectList" :key="`project${idx}`" :label="`[${item.inCode} / ${item.inName}]`" :value="item.id" @click.native="sc.row.inName = item.inName" />
第二种使用组件内置的属性获取:
参考简述文章:
http://events.jianshu.io/p/a4b1920ddd1d
通过select组件内置的属性进行获取
/* 存储下拉label值(给钉钉回显) */
this.form.coName = this.$refs['sysArCoIdSelect'].selectedLabel
/* 这里label值不是一致的,需要文本处理 */
this.form.coName = this.form.coName.substring(0, this.form.coName.lastIndexOf('|') - 1)
多选Select标签回显:
多选显示的是一组label值,上面那种@click.native的办法就不适用了
还是通过内置属性获取,使用map方法提取label值转字符即可
/* 多选下拉,转换label值 */
this.form.orWaCateName = this.$refs['orWaCateSelect'].selected.map(comp => comp.currentLabel).toString()
多选Select的类型不一致:
el-select多选,是存储一个数组类型,而vant-picker这里是存储一个字符串
暂存接口并没有像业务接口一样提交之前转换成字符串存入
vant手机端和业务字段不需要任何转换处理,主要是el-select这边的处理
从el-select暂存的是:["BA1001", "BA1002", ...]
从van-picker暂存的是:"BA1001, BA1002, ..."
给element这边就判断类型是不是h5暂存的,如果是才转换
这里发现多选为空的情况下暂存,空字符使用分割数组方法会填充一个空串元素
为了去除这个无效元素,我后面加了filter过滤处理
/* 从钉钉端和PC端暂存的类型不一致区分处理 */
const isFromH5 = typeof dataParam.orWaCate === 'string'
if (isFromH5) {
dataParam.orWaCate = dataParam.orWaCate.split(',').filter(x => x !== '')
给vant这边就反过来:
/* 从钉钉端和PC端暂存的类型不一致区分处理 */
const isFromH5 = typeof this.form.afSealType === 'string'
if (!isFromH5) {
this.form.afSealType = this.form.afSealType.toString()
2023年04月19日更新:
解决钉钉H5路由跳转的问题
钉钉微应用H5 分为【安卓,苹果,PC】PC的先不考虑,主要是在安卓和苹果上的跳转处理
先看文档说明:
https://open.dingtalk.com/document/orgapp/return-to-previous-page
问题主要出现在安卓的跳转,发现按照官方文档的说明使用后并不能后退,而是直接退到钉钉页面了
再查看window.history对象之后发现,h5页面的history对象内容为空,这就说明钉钉是不会记录H5的跳转
但是在苹果IOS的钉钉上面并没有发现这个问题,正常使用,所以对IOS的处理是不需要干预的
如何判断移动端是IOS还是安卓?
方式一、通过读取浏览器信息,判断字符内容
const u = navigator.userAgent
const isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
方式二、使用dingding的JSAPI变量
import ding from 'dingtalk-jsapi'
if (ding.ios) {
// 是ios ...
解决安卓回退的问题:
一、在跳转之前,先回退需要准备的参数准备好:
因为要回退到本页面,那下一个页面就需要知道本页面的path,和重新跳转到本页面需要的相关参数
openSellHistoryPopup() {
this.$router.push({ path: '/salApSellHistory', query: { ... this.$route.query, path: this.$route.path }})
二、在跳转的页面挂载dom之后,对钉钉的后退按钮绑定事件方法
因为是取dom操作,所以要等挂载完成后再绑定,先把当前页面刷新一遍后,再跳回去
mounted() {
const u = navigator.userAgent
const that = this
const isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
if (!isiOS) {
document.addEventListener('backbutton', function() {
const { path } = that.$route.query
window.location.reload()
that.$router.push({ path, query: { ... that.$route.query }, replace: true })
可以先阻止钉钉自己的后退事件
document.addEventListener('backbutton', function(e) {
e.preventDefault()
const { path } = that.$route.query
window.location.reload()
that.$router.push({ path, query: { ... that.$route.query }, replace: true })
解决苹果IOS页面可拖动的BUG:
需要安装一个inobounce的依赖:
"inobounce": "^0.2.1"
导入inobounce,在挂载时调用开启方法,在页面实例销毁前调用关闭方法
import inobounce from 'inobounce'
因为只针对苹果,所以先判断下是不是IOS
mounted() {
if (ding.ios) inobounce.enable()
beforeDestroy() {
if (ding.ios) inobounce.disable()
然后页面根元素必须使用自动滚动样式:
overflow-y: auto;
<template>
<div style="background:#f5f5f5; width: 100vw; height: 100vh;!important; overflow-y: auto;">
<!-- 页面内容 -->
</div>
</template>