介绍:项目使用的是uni-app做的微信小程序;uni-app里原来就用一个组件uni-indexed-list,是用来做通讯录的;可以直接看官方案例也就是hello-uniapp-master下的导航栏;但是官方这个是多选,且没有默认选中功能;故本文是在使用官方组件同时,修改原组件的代码来达到单选和默认选中功能;
解决思路:
1.将uni_modules包下的通讯录组件uni-indexed-list复制一份在公共组件components下;
2.在你自己的页面中,引入这个公共的通讯录组件uni-indexed-list;
3.如果是想要单选用户和具有默认选中某个用户功能,就去修改uni-indexed-list.vue文件(本文只是完成这一需求);针对如果是多选,就不需要改任何代码,可以直接按照官方的demo去做(本文暂未做);
4.修改uni-indexed-list.vue,如果需要单选功能就在onClick方法里,加上以下代码:
如果想要默认选中某个用户,就在setList方法下添加以下代码:
5.官方的demo下通讯录,是自适应高度的;如果想要将右侧的ABC字母高度修改成固定的;需要注意修改uni-indexed-list.vue两个地方;
例如我的是将右侧通讯录单个高度设为18px:
5.2 修改触发滑动或者点击时候,对应的触发对应区域(共有三个地方需要修改 搜索
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
进行修改即可 ):
6.各文件代码:可以直接复制使用,没有调用接口,注意需要修改自己的通讯录数据引入以及自己的通讯录数据格式字段等;筛选输入框需要配合后端返回通讯录数据;保存按钮的事件需要自己写;通讯录组件uni-indexed-list默认是绝对定位的,修改样式需要注意;
6.1自己的页面文件,在这里引入通讯录组件的; address-list.vue:
<template>
<view class="address-list">
<view class="header">
<view class="input-view">
<uni-icons type="search" size="22" color="#666666" />
<!-- 详见uni-app的 input组件 -->
<!-- confirm-type设置键盘右下角按钮的文字 send search next go done,仅在 type="text" 时生效。 -->
<!-- type 有效值 text number idcard digit tel -->
<input type="text" v-model="real_name" confirm-type="search" class="input-first" placeholder="搜索筛选可以借助后端接口" placeholderStyle="color:#ccc;" @input="changeInput" @blur="searchBtn" @confirm="searchBtn">
<!-- #ifndef H5 -->
<uni-icons v-if="real_name" class="input-uni-del" @tap.stop="closeInput(e)" type="closeempty" size="22" color="#ccc" />
<!-- #endif -->
</view>
</view>
<!-- 使用通讯录组件 -->
<uni-indexed-list :options="list" :show-select="true" @click="bindClick" :select_city="select_city" />
<view class="bottom_box">
<button @click="updataCity">保存</button>
</view>
</view>
</template>
<script>
import airport from '@/common/airport.js'
// 注意:一定是引入自己 复制在公共组件components下的通讯录组件(自己修改成单选和默认值)
import uniIndexedList from '@/components/uni-indexed-list/components/uni-indexed-list/uni-indexed-list'
export default {
// 引入组件
components: {
uniIndexedList
},
data () {
return {
list: airport.list,// 引入的通讯录数据---一般是通过后端接口返回的
select_city: '阿里昆莎机场',//默认选中的 -- 一般是某个id 值需要父传子给通讯录组件
real_name: '',//搜索筛选框的字段 -- 为了配合后端做筛选
}
},
methods: {
bindClick (e) {
console.log('点击item,返回数据' + JSON.stringify(e))
},
// 点击确认搜索等事件
searchBtn (e) {
uni.showToast({
title: '筛选需后端配合返回数据',
icon: 'success',
})
},
// 输入事件
changeInput (e) {
console.log(e.target.value)
},
closeInput (e) {
this.real_name = ''
console.log('清空刷新')
},
updataCity () {
uni.showToast({
title: '修改机场成功',
icon: 'success',
mask: true
})
}
}
}
</script>
<style lang="less" scope>
.address-list {
//原生的input-first框
.header {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 17rpx 40rpx;
align-items: center;
border-top-width: 1px;
border-top-color: #f5f5f5;
border-top-style: solid;
background-color: #ffffff;
.input-view {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
flex-direction: row;
height: 70rpx;
line-height: 70rpx;
border: 1px solid #ddd;
border-radius: 35rpx;
padding: 0 10px;
flex: 1;
.input-first {
flex: 1;
padding: 0 5px;
height: 70rpx;
line-height: 70rpx;
font-size: 14px;
background-color: transparent;
}
}
}
// 通讯录 原本就是用的绝对定位
.uni-indexed-list {
width: 98% !important;
top: 126rpx !important;
bottom: 152rpx !important; //影响右侧的XYZ显示
}
/* 解决小程序和app滚动条的问题 */
::-webkit-scrollbar {
display: none;
}
.bottom_box {
width: 100%;
position: fixed !important;
bottom: 0;
background-color: #fff;
z-index: 999 !important;
padding: 30rpx;
box-sizing: border-box;
button {
height: 94rpx;
background-color: #108ee9;
font-family: PingFangSC-Regular;
font-size: 36rpx;
color: #ffffff;
text-align: center;
line-height: 94rpx;
}
}
}
</style>
6.2需要修改的公共罪案components下的通讯录组件uni-indexed-list.vue
<template>
<view class="uni-indexed-list" ref="list" id="list">
<!-- #ifdef APP-NVUE -->
<list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
<cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
<view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
<!-- #endif -->
<indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect" @itemClick="onClick"></indexed-list-item>
<!-- #ifndef APP-NVUE -->
</view>
</scroll-view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
</cell>
</list>
<!-- #endif -->
<view class="uni-indexed-list__menu" :class="touchmove ? 'uni-indexed-list__menu--active' : ''" @touchstart="touchStart" @touchmove.stop.prevent="touchMove" @touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove" @mouseleave.stop="mouseleave">
<view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item">
<text class="uni-indexed-list__menu-text" :class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
</view>
</view>
<view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
<text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
</view>
</view>
</template>
<script>
import indexedListItem from './uni-indexed-list-item.vue'
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
// #ifdef APP-PLUS
function throttle (func, delay) {
var prev = Date.now()
return function () {
var context = this
var args = arguments
var now = Date.now()
if (now - prev >= delay) {
func.apply(context, args)
prev = Date.now()
}
}
}
function touchMove (e) {
let pageY = e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
// #ifndef APP-NVUE
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #endif
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
this.touchmoveIndex = index
// #endif
}
}
const throttleTouchMove = throttle(touchMove, 40)
// #endif
/**
* IndexedList 索引列表
* @description 用于展示索引列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=375
* @property {Boolean} showSelect = [true|false] 展示模式
* @value true 展示模式
* @value false 选择模式
* @property {Object} options 索引列表需要的数据对象
* @event {Function} click 点击列表事件 ,返回当前选择项的事件对象
* @example
*/
export default {
name: 'UniIndexedList',
components: {
indexedListItem
},
props: {
options: {
type: Array,
default () {
return []
}
},
showSelect: {
type: Boolean,
default: false
},
select_city: {
type: String,
default: ''
}
},
data () {
return {
lists: [],
winHeight: 0,
itemHeight: 0,
winOffsetY: 0,
touchmove: false,
touchmoveIndex: -1,
scrollViewId: '',
touchmovable: true,
loaded: false,
isPC: false
}
},
watch: {
options: {
handler: function () {
this.setList()
},
deep: true
}
},
mounted () {
// #ifdef H5
this.isPC = this.IsPC()
// #endif
setTimeout(() => {
this.setList()
}, 50)
setTimeout(() => {
this.loaded = true
}, 300)
},
methods: {
setList () {
let index = 0
this.lists = []
this.options.forEach((value, index) => {
if (value.data.length === 0) {
return
}
let indexBefore = index
let items = value.data.map(item => {
let obj = {}
obj['key'] = value.letter
obj['name'] = item
obj['itemIndex'] = index
index++
obj.checked = item.checked ? item.checked : false
return obj
})
this.lists.push({
title: value.letter,
key: value.letter,
items: items,
itemIndex: indexBefore
})
})
//----------------------自己添加的部分 目的是 将默认选中的机场 便利后就将其checked设置未true 这样显示就会是选中 (一般被选中的给的是id 而不是字符串name)
this.lists.forEach(ele => {
ele.items.forEach(val => {
if (val.name == this.select_city) {
val.checked = true
}
})
})
//----------------------自己添加的部分 目的是 将默认选中的机场 便利后就将其checked设置未true 这样显示就会是选中 (一般被选中的给的是id 而不是字符串name)
// #ifndef APP-NVUE
uni.createSelectorQuery()
.in(this)
.select('#list')
.boundingClientRect()
.exec(ret => {
this.winOffsetY = ret[0].top
this.winHeight = ret[0].height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['list'], (res) => {
this.winOffsetY = res.size.top
this.winHeight = res.size.height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
},
touchStart (e) {
this.touchmove = true
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
// #endif
}
},
touchMove (e) {
// #ifndef APP-PLUS
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
}
// #endif
// #ifdef APP-PLUS
throttleTouchMove.call(this, e)
// #endif
},
touchEnd () {
this.touchmove = false
this.touchmoveIndex = -1
},
/**
* 兼容 PC @tian
*/
mousedown (e) {
if (!this.isPC) return
this.touchStart(e)
},
mousemove (e) {
if (!this.isPC) return
this.touchMove(e)
},
mouseleave (e) {
if (!this.isPC) return
this.touchEnd(e)
},
// #ifdef H5
IsPC () {
var userAgentInfo = navigator.userAgent
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]
var flag = true
for (let v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false
break
}
}
return flag
},
// #endif
onClick (e) {
//----------------------自己添加的部分 目的是 将选择客户B时候 将之前选中的其他客户A等的状态全部清空未false 后面再单独给B设置选中true -- 这样就可以控制单选一人
this.lists.forEach((ele, i) => {
ele.items.forEach((val, j) => {
val.checked = false
})
})
//----------------------自己添加的部分 目的是 将选择客户B时候 将之前选中的其他客户A等的状态全部清空未false 后面再单独给B设置选中true -- 这样就可以控制单选一人
let {
idx,
index
} = e
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
let select = []
if (this.showSelect) {
// 这一步是原来就有的 目的是给当前选择的用户 标记为选中状态true
this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
this.lists.forEach((value, idx) => {
value.items.forEach((item, index) => {
if (item.checked) {
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
select.push(obj)
}
})
})
}
this.$emit('click', {
item: obj,
select: select
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-indexed-list {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-indexed-list__scroll {
flex: 1;
}
.uni-indexed-list__menu {
width: 24px;
background-color: lightgrey;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-indexed-list__menu-item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
align-items: center;
justify-content: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__menu-text {
line-height: 20px;
font-size: 12px;
text-align: center;
color: #aaa;
}
.uni-indexed-list__menu--active {
background-color: rgb(200, 200, 200);
}
.uni-indexed-list__menu-text--active {
color: #007aff;
}
.uni-indexed-list__alert-wrapper {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.uni-indexed-list__alert {
width: 80px;
height: 80px;
border-radius: 80px;
text-align: center;
line-height: 80px;
font-size: 35px;
color: #fff;
background-color: rgba(0, 0, 0, 0.5);
}
</style>