a7835847 by 华明祺

Merge branch 'master' of https://code.itechtop.cn/yangyang/ztx_wx_admin

# Conflicts:
#	pages/index/index.vue
2 parents 7145d1c6 c49a1142
Showing 54 changed files with 4327 additions and 195 deletions
......@@ -253,8 +253,41 @@
"navigationBarTitleText": "注册",
"enablePullDownRefresh": false
}
}, {
"path": "pages/index/msgList",
"style": {
"navigationBarTitleText": "待办列表",
"enablePullDownRefresh": false
}
}, {
"path": "pages/personalVip/addVip",
"style": {
"navigationBarTitleText": "添加会员",
"enablePullDownRefresh": false,
"backgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
}
],
,{
"path" : "pages/personalVip/renew",
"style" :
{
"navigationBarTitleText": "会员缴费",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/personalVip/vipList",
"style" :
{
"navigationBarTitleText": "会员列表",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white",
......
<template>
<view class="page">
<view>
<view class="welcome">您好!<br />欢迎使用中跆协会员管理系统</view>
<view class="flexbox">
<view>添加会员</view>
<view>会员缴费</view>
<view>缴费审核</view>
</view>
</view>
<uni-section padding>
<uni-grid :column="4" :show-border="false" :square="false" @change="change">
<uni-grid-item v-for="(item ,index) in list" :index="index" :key="index">
<view class="grid-item-box">
<image class="image" :src="item.url" mode="aspectFill" />
<text class="text">{{item.text}}</text>
<view v-if="item.badge" class="grid-dot">
<uni-badge :text="item.badge" :type="item.type" />
</view>
</view>
</uni-grid-item>
</uni-grid>
</uni-section>
<uni-section title="待办提醒" padding>
<template v-slot:right>
<text @click="goMsgList" class="more">更多></text>
</template>
<view class="msglist">
<view class="msgitem" v-for="n in 4">
<text class="dot" :class="n.isRead?'done':''"></text>
<view class="tt esp">你有一条会员缴费等待审批,点击前去处理!</view>
<view class="date">2023-09-23</view>
</view>
</view>
</uni-section>
</view>
<view class="page">
<view>
<view class="welcome">您好!<br />欢迎使用中跆协会员管理系统</view>
<view class="flexbox">
<view @click="goAddVip">添加会员</view>
<view @click="goRenew">会员缴费</view>
<view>缴费审核</view>
</view>
</view>
<uni-section padding>
<uni-grid :column="4" :show-border="false" :square="false" @change="change">
<uni-grid-item v-for="(item ,index) in list" :index="index" :key="index">
<view class="grid-item-box">
<image class="image" :src="item.url" mode="aspectFill" />
<text class="text">{{item.text}}</text>
<view v-if="item.badge" class="grid-dot">
<uni-badge :text="item.badge" :type="item.type" />
</view>
</view>
</uni-grid-item>
</uni-grid>
</uni-section>
<uni-section title="待办提醒" padding>
<template v-slot:right>
<text @click="goMsgList" class="more">更多></text>
</template>
<view class="msglist">
<view class="msgitem" v-for="n in 4">
<text class="dot" :class="n.isRead?'done':''"></text>
<view class="tt esp">你有一条会员缴费等待审批,点击前去处理!</view>
<view class="date">2023-09-23</view>
</view>
</view>
</uni-section>
</view>
</template>
<script setup>
import * as api from '@/common/api.js';
import * as loginServer from '@/common/login.js';
import {
getWxUser,
getWxUserPhone
} from '@/common/login.js';
import {
onLoad,
onShow,
onReady,
onShareAppMessage,
onShareTimeline,
onPullDownRefresh
} from '@dcloudio/uni-app';
import {
ref,
getCurrentInstance
} from 'vue';
const {
proxy
} = getCurrentInstance()
const app = getApp();
let proId;
let goPath;
const svId = ref(null);
const list = ref([{
url: '/static/c1.png',
text: '会员调动',
badge: '0',
type: "primary"
},
{
url: '/static/c2.png',
text: '调动审核',
badge: '1',
type: "success"
},
{
url: '/static/c3.png',
text: '级位考试申请',
badge: '99',
type: "warning"
},
{
url: '/static/c4.png',
text: '级位考试审核',
badge: '2',
type: "error"
},
{
url: '/static/c5.png',
text: '段位考试申请'
},
{
url: '/static/c6.png',
text: '段位考试审核'
},
{
url: '/static/c7.png',
text: '级位证书发送'
},
{
url: '/static/c8.png',
text: '段位证书发送'
}
]);
onShow(() => {
if (app.globalData.isLogin) {
init();
} else {
app.firstLoadCallback = () => {
init();
};
}
})
onLoad(option => {
if (option.scene) {
proId = decodeURIComponent(option.scene);
} else {
proId = option.proId;
}
});
function gologin(){
let path = '/pages/index/login';
uni.navigateTo({
url: path
});
}
function goCenter() {
let path = '/pages/usercenter/usercenter';
if (checkUserPhone(path)) {
uni.navigateTo({
url: path
});
}
}
function init() {
uni.showToast({
title:uni.getStorageSync('userType')
})
}
function goMsgList(){
}
function goItem(item) {
if (item.path) {
let path = item.path
// if (checkUserPhone(path)) {
uni.navigateTo({
url: item.path
});
// }
} else {
uni.showToast({
title: `暂未开放`,
icon: 'none'
});
}
}
import * as api from '@/common/api.js';
import * as loginServer from '@/common/login.js';
import {
getWxUser,
getWxUserPhone
} from '@/common/login.js';
import {
onLoad,
onShow,
onReady,
onShareAppMessage,
onShareTimeline,
onPullDownRefresh
} from '@dcloudio/uni-app';
import {
ref,
getCurrentInstance
} from 'vue';
const {
proxy
} = getCurrentInstance()
const app = getApp();
let proId;
let goPath;
const svId = ref(null);
const list = ref([{
url: '/static/c1.png',
text: '会员调动',
badge: '0',
type: "primary"
},
{
url: '/static/c2.png',
text: '调动审核',
badge: '1',
type: "success"
},
{
url: '/static/c3.png',
text: '级位考试申请',
badge: '99',
type: "warning"
},
{
url: '/static/c4.png',
text: '级位考试审核',
badge: '2',
type: "error"
},
{
url: '/static/c5.png',
text: '段位考试申请'
},
{
url: '/static/c6.png',
text: '段位考试审核'
},
{
url: '/static/c7.png',
text: '级位证书发送'
},
{
url: '/static/c8.png',
text: '段位证书发送'
}
]);
onShow(() => {
if (app.globalData.isLogin) {
init();
} else {
app.firstLoadCallback = () => {
init();
};
}
})
onLoad(option => {
if (option.scene) {
proId = decodeURIComponent(option.scene);
} else {
proId = option.proId;
}
});
function gologin() {
let path = '/pages/index/login';
uni.navigateTo({
url: path
});
}
function goCenter() {
let path = '/pages/usercenter/usercenter';
uni.navigateTo({
url: path
});
}
function goAddVip() {
let path = '/pages/personalVip/addVip';
uni.navigateTo({
url: path
});
}
function goRenew() {
let path = '/pages/personalVip/renew';
uni.navigateTo({
url: path
});
}
function init() {
}
function goMsgList() {
let path = '/pages/index/msgList';
uni.navigateTo({
url: path
});
}
function goItem(item) {
if (item.path) {
let path = item.path
// if (checkUserPhone(path)) {
uni.navigateTo({
url: item.path
});
// }
} else {
uni.showToast({
title: `暂未开放`,
icon: 'none'
});
}
}
</script>
<style scope lang="scss">
.welcome{padding: 55rpx;
line-height: 55rpx;
font-size: 36rpx;}
<style scope lang="scss">
.welcome {
padding: 55rpx;
line-height: 55rpx;
font-size: 36rpx;
}
.flexbox {
display: flex;
justify-content: space-around;
......@@ -213,27 +230,14 @@ font-size: 36rpx;}
position: absolute;
top: 5px;
right: 15px;
}
:deep(.uni-section){
background-color: transparent;
}
:deep(.uni-section .uni-section-header__content){font-size: 36rpx;color: #29343C;}
.more{color: #929AA0;font-size: 24rpx;}
.msglist{
.msgitem{background: #FFFFFF;border-radius: 10rpx;margin-bottom: 25rpx;padding: 30rpx 20rpx 30rpx 50rpx;
position: relative;
.dot{width: 12rpx;display: block;position: absolute;left: 20rpx;top: 40rpx;
height: 12rpx;
background: #C40F18;
border-radius: 50%;}
.dot.done{
background: #C7C7CD;}
.tt{
font-size: 28rpx;
color: #29343C;}
.date{
color: #929AA0;
font-size: 24rpx;}
}
}
:deep(.uni-section) {
background-color: transparent;
}
:deep(.uni-section .uni-section-header__content) {
font-size: 36rpx;
color: #29343C;
}
</style>
\ No newline at end of file
......
<template>
<view>
<z-paging ref="paging" v-model="dataList" @query="queryList">
<!-- z-paging默认铺满全屏,此时页面所有view都应放在z-paging标签内,否则会被盖住 -->
<!-- 需要固定在页面顶部的view请通过slot="top"插入,包括自定义的导航栏 -->
<uni-section title="待办提醒" padding>
<view class="msglist">
<view class="msgitem" v-for="n in dataList">
<text class="dot" :class="n.isRead?'done':''"></text>
<view class="tt esp">你有一条会员缴费等待审批,点击前去处理!</view>
<view class="date">2023-09-23</view>
</view>
</view>
</uni-section>
</z-paging>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
const dataList = ref([])
const paging = ref(null)
const total = ref(50)
const current = ref(2)
function queryList(pageNo, pageSize){
paging.value.complete([{},{},{},{},{},{},{},{},{}]);
}
</script>
<style lang="scss" scoped>
:deep(.uni-section) {
background-color: transparent;
}
:deep(.uni-section .uni-section-header__content) {
font-size: 36rpx;
color: #29343C;
}
</style>
\ No newline at end of file
<template>
<view>
<uni-segmented-control class="whitebg" :current="current" :values="items" @clickItem="onClickItem"
styleType="text" activeColor="#C40F18"></uni-segmented-control>
<view class="hasfixedbottom">
<view>
<uni-forms ref="baseForm" :border="true" :modelValue="baseFormData" label-width="80">
<view class="nolineform">
<uni-forms-item label="姓名" required name="realName" v-show="current === 0">
<uni-easyinput :styles="inputstyle" :placeholderStyle="placeholderStyle"
v-model="baseFormData.realName" placeholder="请输入姓名" />
</uni-forms-item>
<uni-forms-item label="证件类型" required name="idcType">
<uni-data-select v-model="baseFormData.idcType" :localdata="idcTypeList"
@change="changeidcType"></uni-data-select>
</uni-forms-item>
<uni-forms-item label="证件照" required name="picUrl" v-show="current === 1">
<view class="upCard">
<uni-file-picker :class="baseFormData.idcFrontImg?'':'op0'"
v-model="baseFormData.idcFrontImgObj" @delete="delimgFont" return-type="object"
limit="1" @select="upIdCardImgFront"
:image-styles="imageStylesZJ"></uni-file-picker>
<image v-if="!baseFormData.idcFrontImg" class="sfz" src="@/static/login/sfz.png">
</image>
</view>
</uni-forms-item>
<uni-forms-item label="姓名" required name="realName" v-show="current === 1">
<uni-easyinput :styles="inputstyle" :placeholderStyle="placeholderStyle"
v-model="baseFormData.realName" placeholder="请输入姓名" />
</uni-forms-item>
<uni-forms-item label="证件号码" required name="idcCode">
<uni-easyinput :styles="inputstyle" :placeholderStyle="placeholderStyle"
v-model="baseFormData.idcCode" @blur="giveBirthDay" placeholder="请输入证件号码" />
</uni-forms-item>
<uni-forms-item label="性别" required name="sex">
<uni-data-checkbox v-model="baseFormData.sex" @change="changeSex" :localdata="sexs" />
</uni-forms-item>
<uni-forms-item label="出生日期" required name="birth">
<uni-datetime-picker type="date" placeholder="YYYY-MM-DD" :border='false'
:clear-icon="false" v-model="baseFormData.birth" />
</uni-forms-item>
<uni-forms-item label="联系方式" name="phone">
<uni-easyinput :styles="inputstyle" :placeholderStyle="placeholderStyle"
v-model="baseFormData.phone" placeholder="请输入联系方式" />
</uni-forms-item>
<uni-forms-item label="所在地区">
<uni-data-picker class="fixUniFormItemStyle" v-model="baseFormData.regionId"
:localdata="regionsList" popup-title="请选择所在地区"></uni-data-picker>
</uni-forms-item>
<uni-forms-item label="详细地址"><uni-easyinput :styles="inputstyle"
:placeholderStyle="placeholderStyle" v-model="baseFormData.address"
placeholder="请输入详细地址" /></uni-forms-item>
<uni-forms-item label="头像" required name="picUrl">
<uni-file-picker v-model="baseFormData.idcFrontImgObj" @delete="delimgFont"
return-type="object" limit="1" @select="upIdCardImgFront"
:image-styles="imageStylesTx"></uni-file-picker>
</uni-forms-item>
</view>
</uni-forms>
</view>
<view class="agreeline">
<image @click="changeAgree(agree)" v-if="agree" src="@/static/login/xz_dwn@2x.png"></image>
<image v-else src="@/static/login/xz2@2x.png"></image>
<view>我已阅读<text @click="openpopup">《入会须知》</text></view>
</view>
</view>
<view class="fixedBottom"><button class="btn-red" @click="goSubmit">确 定</button></view>
<!-- 会员须知 -->
<uni-popup ref="popup" type="bottom" background-color="#fff" animation>
<view class="tt">入会须知</view>
<view class="popBody">
______欢迎您申请成为中国跆拳道协会(以下简称中国跆协)会员,请确保本次申请是经过您本人或监护人授权同意后的自愿行为,请您务必仔细阅读本入会须知。
<br />
一、中国跆协会员分为个人会员和单位会员。
<br />
二、成为本协会会员条件:遵守中国跆协章程和协会各项规章制度及相关决议,按期交纳会费,积极支持和参与中国跆拳道事业发展的社会各届人士或地方跆拳道协会、俱乐部、培训机构等,均可自愿申请成为中国跆协会员。
<br />
三、个人会员为在中国工作和生活的跆拳道爱好者,16 周岁以下应有监护人协助申请,会员须为中国公民。
<br />
四、会员入会需向所在区域内中国跆协单位会员提出入会申请,并按程序报中国跆协批准,按规定交纳会费。
<br />
五、会员享有《中国跆拳道协会会员管理办法》规定的会员权利。
<br />
六、会员应履行《中国跆拳道协会会员管理办法》规定的会员义务。
<br />
七、凡中国跆协会员,须按照《中国跆拳道协会会员会费标准(2021 版)》按时交纳年度会费。
<br />
八、会员行为违反《中国跆拳道协会会员管理办法》中规定的,按照相关处罚规定进行处理。
<br />
九、会员有退会的自由。会员自愿退会应至少提前 3 个月以书面形式通知本协会,并在此之前,妥善解决与本协会及其他会员之间的财务等问题,方可退会。
<br />
十、其它会员相关内容请查看《中国跆拳道协会会员管理办法》。
<button @click="closepopup" class="btn-red">我已阅读</button>
</view>
</uni-popup>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
const current = ref(0)
const popup = ref(null)
const agree = ref(true)
const regionsList = ref([])
const baseFormData = ref({
sex: '0'
})
const baseFormData2 = ref({})
const items = ref(['手动录入', '自动录入'])
const idcTypeList = ref([{
value: '0',
text: "身份证"
},
{
value: '1',
text: "护照"
},
{
value: '2',
text: "其他"
},
])
const sexs = ref([{
text: '女',
value: '0'
}, {
text: '男',
value: '1'
}])
const placeholderStyle = ref('text-align: right;font-size:30rpx')
const inputstyle = ref({
borderColor: '#fff',
fontSize:'30rpx'
})
const imageStylesTx = ref({
width: '210rpx',
height: '280rpx',
background: {
color: '#F4F6FA'
},
border: {
radius: '2px'
}
});
const imageStylesZJ = ref({
width: '500rpx',
height: '316rpx'
});
function onClickItem(e) {
if (current.value != e.currentIndex) {
current.value = e.currentIndex
}
}
function changeAgree(item) {
item = !item
}
function upIdCardImgFront(e) {
// api.uploadImg(e).then(data => {
// baseFormData.value.idcFrontImg = data.data;
// });
baseFormData.value.idcFrontImg = e
}
function delimgFont(n) {
baseFormData.value.idcFrontImg = '';
}
function giveBirthDay() {
// 判断身份证正确性/赋值生日
if (baseFormData.value.idcType == 0) {
if (!(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/.test(baseFormData.value.idcCode))) {
uni.showToast({
title: '请输入正确的身份证号码',
duration: 2000,
icon: 'none'
})
} else {
let tmpStr = "";
if (baseFormData.value.idcCode.length == 15) {
tmpStr = baseFormData.value.idcCode.substring(6, 12);
tmpStr = "19" + tmpStr;
tmpStr = tmpStr.substring(0, 4) + "-" + tmpStr.substring(4, 6) + "-" + tmpStr.substring(6)
} else {
tmpStr = baseFormData.value.idcCode.substring(6, 14);
tmpStr = tmpStr.substring(0, 4) + "-" + tmpStr.substring(4, 6) + "-" + tmpStr.substring(6)
}
baseFormData.value.birth = tmpStr
const res = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/
if (baseFormData.value.idcCode && res.test(baseFormData.value.idcCode)) {
const genderCode = baseFormData.value.idcCode.charAt(16)
if (parseInt(genderCode) % 2 == 0) {
baseFormData.value.sex = '0'
} else {
baseFormData.value.sex = '1'
}
}
}
}
}
function changeSex(e) {
console.log("性别:", e.detail.value)
}
function changeidcType(e) {
console.log("证件:", e)
if (e) {
baseFormData.value.idcTypeStr = idcTypeList[e].text
}
}
function openpopup() {
popup.value.open()
}
function closepopup() {
popup.value.close()
}
function goSubmit() {
if(!agree.value){
uni.showToast({
title: '请阅知入会须知',
duration: 2000
});
}
}
</script>
<style lang="scss" scoped>
:deep(.segmented-control){height:100rpx;}
:deep(.segmented-control__text){line-height: 2;font-size: 30rpx;}
.tt{text-align: center;font-size: 30rpx;padding: 40rpx 0 0;}
.popBody {
font-size: 28rpx;
line-height: 1.5;
font-family: 华文仿宋;
height: 80vh;
overflow: auto;padding: 30rpx;
.btn-red{margin: 50rpx 0 30rpx;}
}
.agreeline {
padding: 20rpx 40rpx;
box-sizing: border-box;
display: flex;
font-size: 30rpx;
text {
color: #014A9F;
}
image {
width: 40rpx;
height: 40rpx;
margin-right: 20rpx;
}
}
.upCard {
position: relative;
width: 500rpx;
height: 316rpx;
.uni-file-picker {
position: absolute;
z-index: 1;
}
.sfz {
width: 500rpx;
height: 316rpx;
}
}
.op0 {
opacity: 0;
}
</style>
\ No newline at end of file
<template>
<view>
<view class="searchbar">
<uni-easyinput placeholderStyle="font-size:30rpx" :input-border="false" prefixIcon="search" v-model="query.name" placeholder="搜索姓名或证件号码" @iconClick="iconClick">
</uni-easyinput>
<view class="invertedbtn-red" @click="goVipList">+ 添加会员</view>
</view>
<uni-swipe-action>
<uni-swipe-action-item class="personitem" v-for="n in choseList">
<view class="content-box">
<view class="flexbox">
<view class="colorful">{{n.name.slice(0,1)}}</view>
{{n.name}}
</view>
<view class="flexbox">
<uni-number-box :min="1" v-model="n.year" :max="5"></uni-number-box>
</view>
</view>
<template v-slot:right>
<view class="slot-button">
<view>
<uni-icons type="trash-filled" color="#fff" size="20"></uni-icons>
<text class="slot-button-text">删除</text>
</view>
</view>
</template>
</uni-swipe-action-item>
</uni-swipe-action>
<view class="nodata" v-if="choseList.length==0">
<image mode="aspectFit" src="/static/nodata.png"></image>
<button class="btn-red" @click="goVipList">+ 添加会员</button>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
const query = ref({})
const choseList = ref([{name:'张三',year:'1'},{name:'李三',year:'1'}])
const yearlist = ref([{text:'一年',value:'1'},{text:'2年',value:'2'}])
function goVipList(){
let path = '/pages/personalVip/vipList';
uni.navigateTo({
url: path
});
}
function changeYear(){
}
</script>
<style scoped lang="scss">
.searchbar{display: flex;align-items: center;padding: 25rpx;box-sizing: border-box;
.invertedbtn-red{margin-left: 15rpx;font-size: 30rpx;
padding: 16rpx 20rpx;box-sizing: border-box;border-radius: 50rpx;background-color: #fff;}
:deep(.uni-easyinput .uni-easyinput__content){border-radius: 35rpx;border: none;
height: 70rpx;}
:deep(.uni-easyinput__content-input){
font-size: 26rpx;}
}
.slot-button {display: flex;align-items: center;
padding: 0 20px;text-align: center;
background-color: #E60012;
}
.slot-button-text {
color: #ffffff;display: block;
font-size: 14px;
}
.personitem{background: #fff;box-sizing: border-box;
margin-bottom: 30rpx;
.content-box{display: flex;align-items: center;padding:16rpx;border-radius: 15rpx;
justify-content: space-between;
.noborder{border: none;
:deep(.uni-select){border: none;text-align: right;}
}
}
.flexbox{align-items: center;}
&:nth-child(3n) .colorful{background: #014A9F;}
&:nth-child(3n+1) .colorful{background: #AD181F;}
&:nth-child(3n+2) .colorful{background: #D3B267;}
}
.colorful{width: 100rpx;margin-right: 14rpx;
height: 100rpx;line-height: 100rpx;
font-size: 44rpx;
color: #fff;text-align: center;
border-radius: 50%;}
</style>
<template>
<view>
<view class="searchbar">
<uni-easyinput placeholderStyle="font-size:30rpx" :input-border="false" prefixIcon="search"
v-model="query.name" placeholder="搜索姓名或证件号码" @iconClick="iconClick">
</uni-easyinput>
</view>
<view>会员列表</view>
<view class="indexboxre">
<uni-indexed-list :options="list" :showSelect="true" @click="bindClick"></uni-indexed-list>
</view>
<view class="fixedBottom">
<button class="btn-red" @click="goAddRenew">添 加</button>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
const query = ref({})
const list = ref([{
"letter": "A",
"data": [
"阿克苏机场",
"阿拉山口机场",
"阿勒泰机场",
"阿里昆莎机场",
"安庆天柱山机场",
"澳门国际机场"
]
}, {
"letter": "B",
"data": [
"保山机场",
"包头机场",
"北海福成机场",
"北京南苑机场",
"北京首都国际机场"
]
}, {
"letter": "C",
"data": [
"保山机场",
"包头机场",
"北海福成机场",
"北京南苑机场",
"北京首都国际机场"
]
}, {
"letter": "D",
"data": [
"保山机场",
"北京首都国际机场"
]
}, {
"letter": "G",
"data": [
"保山机场",
"北京首都国际机场"
]
}, {
"letter": "Z",
"data": [
"保山机场",
"北京首都国际机场"
]
}])
function bindClick(e) {
console.log('点击item,返回数据' + JSON.stringify(e))
}
function goAddRenew(){
uni.navigateBack()
}
</script>
<style scoped lang="scss">
.indexboxre{position: relative;height:calc(100vh - 300rpx);}
.searchbar{display: flex;align-items: center;padding: 25rpx;box-sizing: border-box;
:deep(.uni-easyinput .uni-easyinput__content){border-radius: 35rpx;border: none;
height: 70rpx;}
:deep(.uni-easyinput__content-input){
font-size: 26rpx;}
}
</style>
\ No newline at end of file

17.7 KB | W: | H:

73.1 KB | W: | H:

static/nodata.png
static/nodata.png
static/nodata.png
static/nodata.png
  • 2-up
  • Swipe
  • Onion skin
## 1.2.1(2021-11-22)
- 修复 vue3中某些scss变量无法找到的问题
## 1.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-indexed-list](https://uniapp.dcloud.io/component/uniui/uni-indexed-list)
## 1.1.0(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.11(2021-05-12)
- 新增 组件示例地址
## 1.0.10(2021-04-21)
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
## 1.0.9(2021-02-05)
- 优化 组件引用关系,通过uni_modules引用组件
## 1.0.8(2021-02-05)
- 调整为uni_modules目录规范
- 新增 支持 PC 端
<template>
<view>
<!-- <view v-if="loaded || list.itemIndex < 15" class="uni-indexed-list__title-wrapper">
<text v-if="list.items && list.items.length > 0" class="uni-indexed-list__title">{{ list.key }}</text>
</view> -->
<view v-if="(loaded || list.itemIndex < 15) && list.items && list.items.length > 0" class="uni-indexed-list__list">
<view v-for="(item, index) in list.items" :key="index" class="uni-indexed-list__item" hover-class="uni-indexed-list__item--hover">
<view class="uni-indexed-list__item-container" @click="onClick(idx, index)">
<view class="uni-indexed-list__item-border" :class="{'uni-indexed-list__item-border--last':index===list.items.length-1}">
<view v-if="showSelect" style="margin-right: 20rpx;">
<uni-icons :type="item.checked ? 'checkbox-filled' : 'circle'" :color="item.checked ? '#AD181F' : '#C0C0C0'" size="24" />
</view>
<text class="uni-indexed-list__item-content">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UniIndexedList',
emits:['itemClick'],
props: {
loaded: {
type: Boolean,
default: false
},
idx: {
type: Number,
default: 0
},
list: {
type: Object,
default () {
return {}
}
},
showSelect: {
type: Boolean,
default: false
}
},
methods: {
onClick(idx, index) {
this.$emit("itemClick", {
idx,
index
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-indexed-list__list {
// background-color: $uni-bg-color;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-indexed-list__item {
font-size: 14px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: space-between;
align-items: center;
background-color: #fff;
margin-bottom: 26rpx;
}
.uni-indexed-list__item-container {
padding-left: 15px;
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__item-border {
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 50px;
padding: 25px;
padding-left: 0;
// border-bottom-style: solid;
// border-bottom-width: 1px;
// border-bottom-color: #DEDEDE;
}
.uni-indexed-list__item-border--last {
border-bottom-width: 0px;
}
.uni-indexed-list__item-content {
flex: 1;
font-size: 14px;
color: #191919;
}
.uni-indexed-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-indexed-list__title-wrapper {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
background-color: #f7f7f7;
}
.uni-indexed-list__title {
padding: 6px 12px;
line-height: 24px;
font-size: 16px;
font-weight: 500;
}
</style>
<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" @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"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''">
<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 <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
*/
export default {
name: 'UniIndexedList',
components: {
indexedListItem
},
emits: ['click'],
props: {
options: {
type: Array,
default () {
return []
}
},
showSelect: {
type: Boolean,
default: false
}
},
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
})
})
// #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) {
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) {
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;box-shadow: 0 0 2px #333;z-index: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
background: #FFFFFF;
border-radius: 15rpx;
// height: fit-content;
}
.uni-indexed-list__menu-item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
align-items: center;
justify-content: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
margin: 15px 0;
}
.uni-indexed-list__menu-text {
font-size: 12px;
text-align: center;
color: #434343;
}
.uni-indexed-list__menu--active {
// background-color: rgb(200, 200, 200);
}
.uni-indexed-list__menu--active {}
.uni-indexed-list__menu-text--active {
border-radius: 16px;
width: 16px;
height: 16px;
line-height: 16px;
color: #AD181F;
}
.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>
{
"id": "uni-indexed-list",
"displayName": "uni-indexed-list 索引列表",
"version": "1.2.1",
"description": "索引列表组件,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景",
"keywords": [
"uni-ui",
"索引列表",
"索引",
"列表"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}
## IndexedList 索引列表
> **组件名:uni-indexed-list**
> 代码块: `uIndexedList`
用于展示索引列表。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-indexed-list)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
## 2.5.9(2023-09-25)
1.`新增` `doInsertVirtualListItem`方法,支持在非固定高度虚拟列表新插入数据。
2.`新增` `refreshToPage`方法,支持刷新列表数据至指定页。
3.`新增` 请求失败Promise在reject时添加具体错误信息,组件内部调用reload时添加catch处理。
4.`修复` 滑动切换选项卡+吸顶模式在抖音小程序中下拉刷新状态无法结束的问题。
5.`修复` `slot="left"`&`slot="right"`可能出现的高度过高超出页面的问题。
6.`修复` nvue中可能出现的切换空数据图后切换回列表数据时底部加载更多在列表顶部的问题。
7.`修复` 虚拟列表固定高度cell超出屏幕高度时,底部有异常空白的问题。
8.`修复` i18n国际化可能导致的下拉刷新view未能正常显示的问题。
9.`修复` 设置`show-refresher-when-reload``refresher-threshold`且自定义插入下拉刷新view后可能出现的闪现旧的view的问题。
10.`修复` 基于z-paging封装个性化分页组件demo在vue3中列表不展示的问题。
11.`修复` `didUpdateVirtualListCell``didDeleteVirtualListCell`高度缓存计算不正确的问题。
12.`修复` `empty-view-super-style`无效的问题。
13.`修复` vue3+ios中长时间进入后台打开后可能出现的下拉刷新展示位置向下偏移的问题。
14.`修复` 在安卓+nvue中,z-paging高度比较低时,空数据图被裁剪的问题。
15.`优化` `endRefresh`触发时,也终止系统默认下拉刷新状态。
16.`优化` slot插入的上下view禁止touchmove事件冒泡。
17.`优化` `addDataFromTop`方法传入的数组倒序处理。
18.`优化` 列表滚动性能,减少js层和wxs层信息交互次数。
19.`优化` 从列表滚动过渡到下拉刷新时的细节处理以解决由此可能引发的下拉刷新抖动问题。
20.`优化` 聊天记录模式demo细节。
<!-- z-paging -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群:790460711 -->
<!-- z-paging-cell,用于在nvue中使用cell包裹,vue中使用view包裹 -->
<template>
<!-- #ifdef APP-NVUE -->
<cell :style="[cellStyle]">
<slot />
</cell>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<view :style="[cellStyle]">
<slot />
</view>
<!-- #endif -->
</template>
<script>
export default {
name: "z-paging-cell",
props: {
//cellStyle
cellStyle: {
type: Object,
default: function() {
return {}
}
}
}
}
</script>
<!-- z-paging -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群:790460711 -->
<!-- 空数据占位view,此组件支持easycom规范,可以在项目中直接引用 -->
<template>
<view :class="{'zp-container':true,'zp-container-fixed':emptyViewFixed}" :style="[finalEmptyViewStyle]" @click="emptyViewClick">
<view class="zp-main">
<image v-if="!emptyViewImg.length" class="zp-main-image" :style="[emptyViewImgStyle]" :src="emptyImg" />
<image v-else class="zp-main-image" mode="aspectFit" :style="[emptyViewImgStyle]" :src="emptyViewImg" />
<text class="zp-main-title" :style="[emptyViewTitleStyle]">{{emptyViewText}}</text>
<text v-if="showEmptyViewReload" class="zp-main-error-btn" :style="[emptyViewReloadStyle]" @click.stop="reloadClick">{{emptyViewReloadText}}</text>
</view>
</view>
</template>
<script>
import zStatic from '../z-paging/js/z-paging-static'
export default {
name: "z-paging-empty-view",
data() {
return {
};
},
props: {
//空数据描述文字
emptyViewText: {
type: String,
default: '没有数据哦~'
},
//空数据图片
emptyViewImg: {
type: String,
default: ''
},
//是否显示空数据图重新加载按钮
showEmptyViewReload: {
type: Boolean,
default: false
},
//空数据点击重新加载文字
emptyViewReloadText: {
type: String,
default: '重新加载'
},
//是否是加载失败
isLoadFailed: {
type: Boolean,
default: false
},
//空数据图样式
emptyViewStyle: {
type: Object,
default: function() {
return {}
}
},
//空数据图img样式
emptyViewImgStyle: {
type: Object,
default: function() {
return {}
}
},
//空数据图描述文字样式
emptyViewTitleStyle: {
type: Object,
default: function() {
return {}
}
},
//空数据图重新加载按钮样式
emptyViewReloadStyle: {
type: Object,
default: function() {
return {}
}
},
//空数据图z-index
emptyViewZIndex: {
type: Number,
default: 9
},
//空数据图片是否使用fixed布局并铺满z-paging
emptyViewFixed: {
type: Boolean,
default: true
}
},
computed: {
emptyImg() {
return this.isLoadFailed ? zStatic.base64Error : zStatic.base64Empty;
},
finalEmptyViewStyle(){
this.emptyViewStyle['z-index'] = this.emptyViewZIndex;
return this.emptyViewStyle;
}
},
methods: {
reloadClick() {
this.$emit('reload');
},
emptyViewClick() {
this.$emit('viewClick');
}
}
}
</script>
<style scoped>
.zp-container{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.zp-container-fixed {
/* #ifndef APP-NVUE */
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
.zp-main{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
padding: 50rpx 0rpx;
}
.zp-main-image {
width: 200rpx;
height: 200rpx;
}
.zp-main-title {
font-size: 26rpx;
color: #aaaaaa;
text-align: center;
margin-top: 10rpx;
}
.zp-main-error-btn {
font-size: 26rpx;
padding: 8rpx 24rpx;
border: solid 1px #dddddd;
border-radius: 6rpx;
color: #aaaaaa;
margin-top: 50rpx;
}
</style>
<!-- z-paging -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群:790460711 -->
<!-- 滑动切换选项卡swiper-item,此组件支持easycom规范,可以在项目中直接引用 -->
<template>
<view class="zp-swiper-item-container">
<z-paging ref="paging" :fixed="false"
:auto="false" :useVirtualList="useVirtualList" :useInnerList="useInnerList" :cellKeyName="cellKeyName" :innerListStyle="innerListStyle"
:preloadPage="preloadPage" :cellHeightMode="cellHeightMode" :virtualScrollFps="virtualScrollFps" :virtualListCol="virtualListCol"
@query="_queryList" @listChange="_updateList" style="height: 100%;">
<slot />
<template #header>
<slot name="header"/>
</template>
<template #cell="{item,index}">
<slot name="cell" :item="item" :index="index"/>
</template>
<template #footer>
<slot name="footer"/>
</template>
</z-paging>
</view>
</template>
<script>
import zPaging from '../z-paging/z-paging'
export default {
name: "z-paging-swiper-item",
components: { zPaging },
data() {
return {
firstLoaded: false
}
},
props: {
//当前组件的index,也就是当前组件是swiper中的第几个
tabIndex: {
type: Number,
default: function() {
return 0
}
},
//当前swiper切换到第几个index
currentIndex: {
type: Number,
default: function() {
return 0
}
},
//是否使用虚拟列表,默认为否
useVirtualList: {
type: Boolean,
default: false
},
//是否在z-paging内部循环渲染列表(内置列表),默认为否。若use-virtual-list为true,则此项恒为true
useInnerList: {
type: Boolean,
default: false
},
//内置列表cell的key名称,仅nvue有效,在nvue中开启use-inner-list时必须填此项
cellKeyName: {
type: String,
default: ''
},
//innerList样式
innerListStyle: {
type: Object,
default: function() {
return {};
}
},
//预加载的列表可视范围(列表高度)页数,默认为12,即预加载当前页及上下各12页的cell。此数值越大,则虚拟列表中加载的dom越多,内存消耗越大(会维持在一个稳定值),但增加预加载页面数量可缓解快速滚动短暂白屏问题
preloadPage: {
type: [Number, String],
default: 12
},
//虚拟列表cell高度模式,默认为fixed,也就是每个cell高度完全相同,将以第一个cell高度为准进行计算。可选值【dynamic】,即代表高度是动态非固定的,【dynamic】性能低于【fixed】。
cellHeightMode: {
type: String,
default: 'fixed'
},
//虚拟列表列数,默认为1。常用于每行有多列的情况,例如每行有2列数据,需要将此值设置为2
virtualListCol: {
type: [Number, String],
default: 1
},
//虚拟列表scroll取样帧率,默认为60,过高可能出现卡顿等问题
virtualScrollFps: {
type: [Number, String],
default: 60
},
},
watch: {
currentIndex: {
handler(newVal, oldVal) {
if (newVal === this.tabIndex) {
//懒加载,当滑动到当前的item时,才去加载
if (!this.firstLoaded) {
this.$nextTick(()=>{
let delay = 5;
// #ifdef MP-TOUTIAO
delay = 100;
// #endif
setTimeout(() => {
this.$refs.paging.reload().catch(() => {});
}, delay);
})
}
}
},
immediate: true
}
},
methods: {
reload(data) {
return this.$refs.paging.reload(data);
},
complete(data) {
this.firstLoaded = true;
return this.$refs.paging.complete(data);
},
_queryList(pageNo, pageSize, from) {
this.$emit('query', pageNo, pageSize, from);
},
_updateList(list) {
this.$emit('updateList', list);
}
}
}
</script>
<style scoped>
.zp-swiper-item-container {
/* #ifndef APP-NVUE */
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
</style>
<!-- z-paging -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群:790460711 -->
<!-- 滑动切换选项卡swiper容器,此组件支持easycom规范,可以在项目中直接引用 -->
<template>
<view :class="fixed?'zp-swiper-container zp-swiper-container-fixed':'zp-swiper-container'" :style="[finalSwiperStyle]">
<!-- #ifndef APP-PLUS -->
<view v-if="cssSafeAreaInsetBottom===-1" class="zp-safe-area-inset-bottom"></view>
<!-- #endif -->
<slot v-if="zSlots.top" name="top" />
<view class="zp-swiper-super">
<view v-if="zSlots.left" :class="{'zp-swiper-left':true,'zp-absoulte':isOldWebView}">
<slot name="left" />
</view>
<view :class="{'zp-swiper':true,'zp-absoulte':isOldWebView}" :style="[swiperContentStyle]">
<slot />
</view>
<view v-if="zSlots.right" :class="{'zp-swiper-right':true,'zp-absoulte zp-right':isOldWebView}">
<slot name="right" />
</view>
</view>
<slot v-if="zSlots.bottom" name="bottom" />
</view>
</template>
<script>
import commonLayoutModule from '../z-paging/js/modules/common-layout'
export default {
name: "z-paging-swiper",
mixins: [commonLayoutModule],
data() {
return {
swiperContentStyle: {}
};
},
props: {
//是否使用fixed布局,默认为是
fixed: {
type: Boolean,
default: true
},
//是否开启底部安全区域适配
safeAreaInsetBottom: {
type: Boolean,
default: false
},
//z-paging-swiper样式
swiperStyle: {
type: Object,
default: function() {
return {};
},
}
},
mounted() {
this.$nextTick(() => {
this.systemInfo = uni.getSystemInfoSync();
})
// #ifndef APP-PLUS
this._getCssSafeAreaInsetBottom();
// #endif
this.updateLeftAndRightWidth();
this.swiperContentStyle = { 'flex': '1' };
// #ifndef APP-NVUE
this.swiperContentStyle = { width: '100%',height: '100%' };
// #endif
},
computed: {
finalSwiperStyle() {
const swiperStyle = this.swiperStyle;
if (!this.systemInfo) return swiperStyle;
const windowTop = this.windowTop;
const windowBottom = this.systemInfo.windowBottom;
if (this.fixed) {
if (windowTop && !swiperStyle.top) {
swiperStyle.top = windowTop + 'px';
}
if (!swiperStyle.bottom) {
let bottom = windowBottom || 0;
bottom += this.safeAreaInsetBottom ? this.safeAreaBottom : 0;
if (bottom > 0) {
swiperStyle.bottom = bottom + 'px';
}
}
}
return swiperStyle;
}
},
methods: {
//更新slot="left"和slot="right"宽度,当slot="left"或slot="right"宽度动态改变时调用
updateLeftAndRightWidth() {
if (!this.isOldWebView) return;
this.$nextTick(() => this._updateLeftAndRightWidth(this.swiperContentStyle, 'zp-swiper'));
}
}
}
</script>
<style scoped>
.zp-swiper-container {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
flex: 1;
}
.zp-swiper-container-fixed {
position: fixed;
/* #ifndef APP-NVUE */
height: auto;
width: auto;
/* #endif */
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.zp-safe-area-inset-bottom {
position: absolute;
/* #ifndef APP-PLUS */
height: env(safe-area-inset-bottom);
/* #endif */
}
.zp-swiper-super {
flex: 1;
overflow: hidden;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.zp-swiper-left,.zp-swiper-right{
/* #ifndef APP-NVUE */
height: 100%;
/* #endif */
}
.zp-swiper {
flex: 1;
/* #ifndef APP-NVUE */
height: 100%;
width: 100%;
/* #endif */
}
.zp-absoulte {
/* #ifndef APP-NVUE */
position: absolute;
top: 0;
width: auto;
/* #endif */
}
.zp-right{
right: 0;
}
.zp-swiper-item {
height: 100%;
}
</style>
<!-- [z-paging]上拉加载更多view -->
<template>
<view class="zp-l-container" :style="[c.customStyle]" @click="doClick">
<template v-if="!c.hideContent">
<text v-if="c.showNoMoreLine&&finalStatus===M.NoMore" class="zp-l-line" :style="[{backgroundColor:zTheme.line[ts]},c.noMoreLineCustomStyle]" />
<!-- #ifndef APP-NVUE -->
<image v-if="finalStatus===M.Loading&&!!c.loadingIconCustomImage"
:src="c.loadingIconCustomImage" :style="[c.iconCustomStyle]" :class="{'zp-l-line-loading-custom-image':true,'zp-l-line-loading-custom-image-animated':c.loadingAnimated}" />
<image v-if="finalStatus===M.Loading&&finalLoadingIconType==='flower'&&!c.loadingIconCustomImage.length"
class="zp-line-loading-image" :style="[c.iconCustomStyle]" :src="zTheme.flower[ts]" />
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view>
<loading-indicator v-if="finalStatus===M.Loading&&finalLoadingIconType!=='circle'" class="zp-line-loading-image" :style="[{color:zTheme.indicator[ts]}]" :animating="true" />
</view>
<!-- #endif -->
<text v-if="finalStatus===M.Loading&&finalLoadingIconType==='circle'&&!c.loadingIconCustomImage.length"
class="zp-l-circle-loading-view" :style="[{borderColor:zTheme.circleBorder[ts],borderTopColor:zTheme.circleBorderTop[ts]},c.iconCustomStyle]" />
<text class="zp-l-text" :style="[{color:zTheme.title[ts]},c.titleCustomStyle]">{{ownLoadingMoreText}}</text>
<text v-if="c.showNoMoreLine&&finalStatus===M.NoMore" class="zp-l-line" :style="[{backgroundColor:zTheme.line[ts]},c.noMoreLineCustomStyle]" />
</template>
</view>
</template>
<script>
import zStatic from '../js/z-paging-static'
import Enum from '../js/z-paging-enum'
export default {
name: 'z-paging-load-more',
data() {
return {
M: Enum.More,
zTheme: {
title: { white: '#efefef', black: '#a4a4a4' },
line: { white: '#efefef', black: '#eeeeee' },
circleBorder: { white: '#aaaaaa', black: '#c8c8c8' },
circleBorderTop: { white: '#ffffff', black: '#444444' },
flower: { white: zStatic.base64FlowerWhite, black: zStatic.base64Flower },
indicator: { white: '#eeeeee', black: '#777777' }
}
};
},
props: ['zConfig'],
computed: {
ts() {
return this.c.defaultThemeStyle;
},
c() {
return this.zConfig || {};
},
ownLoadingMoreText() {
const statusTextArr = [this.c.defaultText,this.c.loadingText,this.c.noMoreText,this.c.failText];
return statusTextArr[this.finalStatus];
},
finalStatus() {
if (this.c.defaultAsLoading && this.c.status === this.M.Default) return this.M.Loading;
return this.c.status;
},
finalLoadingIconType() {
// #ifdef APP-NVUE
return 'flower';
// #endif
return this.c.loadingIconType;
}
},
methods: {
doClick() {
this.$emit('doClick');
}
}
}
</script>
<style scoped>
@import "../css/z-paging-static.css";
.zp-l-container {
height: 80rpx;
font-size: 27rpx;
/* #ifndef APP-NVUE */
clear: both;
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.zp-l-line-loading-custom-image {
color: #a4a4a4;
margin-right: 8rpx;
width: 28rpx;
height: 28rpx;
}
.zp-l-line-loading-custom-image-animated{
/* #ifndef APP-NVUE */
animation: loading-circle 1s linear infinite;
/* #endif */
}
.zp-l-circle-loading-view {
margin-right: 8rpx;
width: 23rpx;
height: 23rpx;
border: 3rpx solid #dddddd;
border-radius: 50%;
/* #ifndef APP-NVUE */
animation: loading-circle 1s linear infinite;
/* #endif */
/* #ifdef APP-NVUE */
width: 30rpx;
height: 30rpx;
/* #endif */
}
.zp-l-text {
/* #ifdef APP-NVUE */
font-size: 30rpx;
margin: 0rpx 10rpx;
/* #endif */
}
.zp-l-line {
height: 1px;
width: 100rpx;
margin: 0rpx 10rpx;
}
/* #ifndef APP-NVUE */
@keyframes loading-circle {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
/* #endif */
</style>
<!-- [z-paging]下拉刷新view -->
<template>
<view style="height: 100%;">
<view :class="showUpdateTime?'zp-r-container zp-r-container-padding':'zp-r-container'">
<view class="zp-r-left">
<image v-if="status!==R.Loading" :class="leftImageClass" :style="[leftImageStyle,imgStyle]" :src="leftImageSrc" />
<!-- #ifndef APP-NVUE -->
<image v-else :class="{'zp-line-loading-image':refreshingAnimated,'zp-r-left-image':true}" :style="[leftImageStyle,imgStyle]" :src="leftImageSrc" />
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view v-else :style="[{'margin-right':showUpdateTime?'18rpx':'12rpx'}]">
<loading-indicator :class="isIos?'zp-loading-image-ios':'zp-loading-image-android'"
:style="[{color:zTheme.indicator[ts]},imgStyle]" :animating="true" />
</view>
<!-- #endif -->
</view>
<view class="zp-r-right">
<text class="zp-r-right-text" :style="[rightTextStyle,titleStyle]">{{currentTitle}}</text>
<text v-if="showUpdateTime&&refresherTimeText.length" class="zp-r-right-text zp-r-right-time-text" :style="[rightTextStyle,updateTimeStyle]">
{{refresherTimeText}}
</text>
</view>
</view>
</view>
</template>
<script>
import zStatic from '../js/z-paging-static'
import u from '../js/z-paging-utils'
import Enum from '../js/z-paging-enum'
export default {
name: 'z-paging-refresh',
data() {
return {
R: Enum.Refresher,
isIos: uni.getSystemInfoSync().platform === 'ios',
refresherTimeText: '',
zTheme: {
title: { white: '#efefef', black: '#555555' },
arrow: { white: zStatic.base64ArrowWhite, black: zStatic.base64Arrow },
flower: { white: zStatic.base64FlowerWhite, black: zStatic.base64Flower },
success: { white: zStatic.base64SuccessWhite, black: zStatic.base64Success },
indicator: { white: '#eeeeee', black: '#777777' }
}
};
},
props: ['status', 'defaultThemeStyle', 'defaultText', 'pullingText', 'refreshingText', 'completeText', 'defaultImg', 'pullingImg',
'refreshingImg', 'completeImg', 'refreshingAnimated', 'showUpdateTime', 'updateTimeKey', 'imgStyle', 'titleStyle', 'updateTimeStyle', 'updateTimeTextMap'
],
computed: {
ts() {
return this.defaultThemeStyle;
},
statusTextArr() {
this.updateTime();
return [this.defaultText,this.pullingText,this.refreshingText,this.completeText];
},
currentTitle() {
return this.statusTextArr[this.status] || this.defaultText;
},
leftImageClass() {
if (this.status === this.R.Complete) return 'zp-r-left-image-pre-size';
return `zp-r-left-image zp-r-left-image-pre-size ${this.status === this.R.Default ? 'zp-r-arrow-down' : 'zp-r-arrow-top'}`;
},
leftImageStyle() {
const showUpdateTime = this.showUpdateTime;
const size = showUpdateTime ? '36rpx' : '30rpx';
return {width: size,height: size,'margin-right': showUpdateTime ? '20rpx' : '9rpx'};
},
leftImageSrc() {
const R = this.R;
const status = this.status;
if (status === R.Default) {
if (!!this.defaultImg) return this.defaultImg;
return this.zTheme.arrow[this.ts];
} else if (status === R.ReleaseToRefresh) {
if (!!this.pullingImg) return this.pullingImg;
if (!!this.defaultImg) return this.defaultImg;
return this.zTheme.arrow[this.ts];
} else if (status === R.Loading) {
if (!!this.refreshingImg) return this.refreshingImg;
return this.zTheme.flower[this.ts];;
} else if (status === R.Complete) {
if (!!this.completeImg) return this.completeImg;
return this.zTheme.success[this.ts];;
}
return '';
},
rightTextStyle() {
let stl = {};
// #ifdef APP-NVUE
const textHeight = this.showUpdateTime ? '40rpx' : '80rpx';
stl = {'height': textHeight, 'line-height': textHeight}
// #endif
stl['color'] = this.zTheme.title[this.ts];
return stl;
}
},
methods: {
updateTime() {
if (this.showUpdateTime) {
this.refresherTimeText = u.getRefesrherFormatTimeByKey(this.updateTimeKey, this.updateTimeTextMap);
}
}
}
}
</script>
<style scoped>
@import "../css/z-paging-static.css";
.zp-r-container {
/* #ifndef APP-NVUE */
display: flex;
height: 100%;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
}
.zp-r-container-padding {
/* #ifdef APP-NVUE */
padding: 15rpx 0rpx;
/* #endif */
}
.zp-r-left {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
overflow: hidden;
/* #ifdef MP-ALIPAY */
margin-top: -4rpx;
/* #endif */
}
.zp-r-left-image {
transition-duration: .2s;
transition-property: transform;
color: #666666;
}
.zp-r-left-image-pre-size{
/* #ifndef APP-NVUE */
width: 30rpx;
height: 30rpx;
overflow: hidden;
/* #endif */
}
.zp-r-arrow-top {
transform: rotate(0deg);
}
.zp-r-arrow-down {
transform: rotate(180deg);
}
.zp-r-right {
font-size: 27rpx;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
justify-content: center;
}
.zp-r-right-text {
/* #ifdef APP-NVUE */
font-size: 28rpx;
/* #endif */
}
.zp-r-right-time-text {
margin-top: 10rpx;
font-size: 24rpx;
}
</style>
// z-paging全局配置文件,注意避免更新时此文件被覆盖,若被覆盖,可在此文件中右键->点击本地历史记录,找回覆盖前的配置
export default {}
\ No newline at end of file
/* [z-paging]公共css*/
.z-paging-content {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
height: 100%;
overflow: hidden;
/* #endif */
flex-direction: column;
}
.z-paging-content-fixed, .zp-loading-fixed {
position: fixed;
/* #ifndef APP-NVUE */
height: auto;
width: auto;
/* #endif */
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.zp-page-top,.zp-page-bottom {
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
position: fixed;
left: 0;
right: 0;
z-index: 999;
}
.zp-page-left,.zp-page-right{
/* #ifndef APP-NVUE */
height: 100%;
/* #endif */
}
.zp-scroll-view-super {
flex: 1;
overflow: hidden;
position: relative;
}
.zp-view-super{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.zp-custom-refresher-container {
overflow: hidden;
}
.zp-scroll-view-container,.zp-scroll-view {
position: relative;
/* #ifndef APP-NVUE */
height: 100%;
width: 100%;
/* #endif */
}
.zp-absoulte{
/* #ifndef APP-NVUE */
position: absolute;
top: 0;
width: auto;
/* #endif */
}
.zp-right{
right: 0;
}
.zp-scroll-view-absolute {
position: absolute;
top: 0;
left: 0;
}
/* #ifndef APP-NVUE */
.zp-scroll-view-hide-scrollbar ::-webkit-scrollbar {
display: none;
-webkit-appearance: none;
width: 0 !important;
height: 0 !important;
background: transparent;
}
/* #endif */
.zp-paging-touch-view {
width: 100%;
height: 100%;
position: relative;
}
.zp-fixed-bac-view {
position: absolute;
width: 100%;
top: 0;
left: 0;
height: 200px;
}
.zp-paging-main {
height: 100%;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.zp-paging-container {
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.zp-chat-record-loading-container {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
/* #ifdef APP-NVUE */
width: 750rpx;
/* #endif */
align-items: center;
justify-content: center;
height: 60rpx;
font-size: 26rpx;
}
.zp-chat-record-loading-custom-image {
width: 35rpx;
height: 35rpx;
/* #ifndef APP-NVUE */
animation: loading-flower 1s linear infinite;
/* #endif */
}
.zp-custom-refresher-container {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
}
.zp-back-to-top {
width: 76rpx;
height: 76rpx;
z-index: 999;
position: absolute;
bottom: 0rpx;
right: 25rpx;
transition-duration: .3s;
transition-property: opacity;
}
.zp-back-to-top-show {
opacity: 1;
}
.zp-back-to-top-hide {
opacity: 0;
}
.zp-back-to-top-img {
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
z-index: 999;
}
.zp-empty-view {
/* #ifdef APP-NVUE */
height: 100%;
/* #endif */
flex: 1;
}
.zp-empty-view-center {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
justify-content: center;
}
.zp-loading-fixed {
z-index: 9999;
}
.zp-safe-area-inset-bottom {
position: absolute;
/* #ifndef APP-PLUS */
height: env(safe-area-inset-bottom);
/* #endif */
}
.zp-n-refresh-container {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
width: 750rpx;
}
.zp-n-list-container{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
}
/* [z-paging]公用的静态css资源 */
.zp-line-loading-image {
margin-right: 8rpx;
width: 28rpx;
height: 28rpx;
/* #ifndef APP-NVUE */
animation: loading-flower 1s steps(12) infinite;
/* #endif */
color: #666666;
}
.zp-loading-image-ios{
width: 20px;
height: 20px;
}
.zp-loading-image-android{
width: 32rpx;
height: 32rpx;
}
/* #ifndef APP-NVUE */
@keyframes loading-flower {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn);
}
}
/* #endif */
{
"zp.refresher.default": "Pull down to refresh",
"zp.refresher.pulling": "Release to refresh",
"zp.refresher.refreshing": "Refreshing...",
"zp.refresher.complete": "Refresh succeeded",
"zp.loadingMore.default": "Click to load more",
"zp.loadingMore.loading": "Loading...",
"zp.loadingMore.noMore": "No more data",
"zp.loadingMore.fail": "Load failed,click to reload",
"zp.emptyView.title": "No data",
"zp.emptyView.reload": "Reload",
"zp.emptyView.error": "Sorry,load failed",
"zp.refresherUpdateTime.title": "Last update: ",
"zp.refresherUpdateTime.none": "None",
"zp.refresherUpdateTime.today": "Today",
"zp.refresherUpdateTime.yesterday": "Yesterday",
"zp.systemLoading.title": "Loading..."
}
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}
{
"zp.refresher.default": "继续下拉刷新",
"zp.refresher.pulling": "松开立即刷新",
"zp.refresher.refreshing": "正在刷新...",
"zp.refresher.complete": "刷新成功",
"zp.loadingMore.default": "点击加载更多",
"zp.loadingMore.loading": "正在加载...",
"zp.loadingMore.noMore": "没有更多了",
"zp.loadingMore.fail": "加载失败,点击重新加载",
"zp.emptyView.title": "没有数据哦~",
"zp.emptyView.reload": "重新加载",
"zp.emptyView.error": "很抱歉,加载失败",
"zp.refresherUpdateTime.title": "最后更新:",
"zp.refresherUpdateTime.none": "无",
"zp.refresherUpdateTime.today": "今天",
"zp.refresherUpdateTime.yesterday": "昨天",
"zp.systemLoading.title": "加载中..."
}
{
"zp.refresher.default": "繼續下拉重繪",
"zp.refresher.pulling": "鬆開立即重繪",
"zp.refresher.refreshing": "正在重繪...",
"zp.refresher.complete": "重繪成功",
"zp.loadingMore.default": "點擊加載更多",
"zp.loadingMore.loading": "正在加載...",
"zp.loadingMore.noMore": "沒有更多了",
"zp.loadingMore.fail": "加載失敗,點擊重新加載",
"zp.emptyView.title": "沒有數據哦~",
"zp.emptyView.reload": "重新加載",
"zp.emptyView.error": "很抱歉,加載失敗",
"zp.refresherUpdateTime.title": "最後更新:",
"zp.refresherUpdateTime.none": "無",
"zp.refresherUpdateTime.today": "今天",
"zp.refresherUpdateTime.yesterday": "昨天",
"zp.systemLoading.title": "加載中..."
}
// [z-paging]useZPaging hooks
import { onPageScroll, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
function useZPaging(paging) {
const cPaging = !!paging ? paging.value || paging : null;
onPullDownRefresh(() => {
if (!cPaging) return;
cPaging.value.reload().catch(() => {});
})
onPageScroll(e => {
if (!cPaging) return;
cPaging.value.updatePageScrollTop(e.scrollTop);
e.scrollTop < 10 && cPaging.value.doChatRecordLoadMore();
})
onReachBottom(() => {
if (!cPaging) return;
cPaging.value.pageReachBottom();
})
}
export default useZPaging
\ No newline at end of file
// [z-paging]useZPagingComp hooks
function useZPagingComp(paging) {
const cPaging = !!paging ? paging.value || paging : null;
const reload = () => {
if (!cPaging) return;
cPaging.value.reload().catch(() => {});
}
const updatePageScrollTop = scrollTop => {
if (!cPaging) return;
cPaging.value.updatePageScrollTop(scrollTop);
}
const doChatRecordLoadMore = () => {
if (!cPaging) return;
cPaging.value.doChatRecordLoadMore();
}
const pageReachBottom = () => {
if (!cPaging) return;
cPaging.value.pageReachBottom();
}
return { reload, updatePageScrollTop, doChatRecordLoadMore, pageReachBottom };
}
export default useZPagingComp
\ No newline at end of file
// [z-paging]点击返回顶部view模块
import u from '.././z-paging-utils'
export default {
props: {
//自动显示点击返回顶部按钮,默认为否
autoShowBackToTop: {
type: Boolean,
default: u.gc('autoShowBackToTop', false)
},
//点击返回顶部按钮显示/隐藏的阈值(滚动距离),单位为px,默认为400rpx
backToTopThreshold: {
type: [Number, String],
default: u.gc('backToTopThreshold', '400rpx')
},
//点击返回顶部按钮的自定义图片地址,默认使用z-paging内置的图片
backToTopImg: {
type: String,
default: u.gc('backToTopImg', '')
},
//点击返回顶部按钮返回到顶部时是否展示过渡动画,默认为是
backToTopWithAnimate: {
type: Boolean,
default: u.gc('backToTopWithAnimate', true)
},
//点击返回顶部按钮与底部的距离,注意添加单位px或rpx,默认为160rpx
backToTopBottom: {
type: [Number, String],
default: u.gc('backToTopBottom', '160rpx')
},
//点击返回顶部按钮的自定义样式
backToTopStyle: {
type: Object,
default: function() {
return u.gc('backToTopStyle', {});
},
},
//iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只支持竖向,默认为是
enableBackToTop: {
type: Boolean,
default: u.gc('enableBackToTop', true)
},
},
data() {
return {
backToTopClass: 'zp-back-to-top zp-back-to-top-hide',
lastBackToTopShowTime: 0,
showBackToTopClass: false,
}
},
computed: {
finalEnableBackToTop() {
return this.usePageScroll ? false : this.enableBackToTop;
},
finalBackToTopThreshold() {
return u.convertToPx(this.backToTopThreshold);
},
finalBackToTopStyle() {
const backToTopStyle = this.backToTopStyle;
if (!backToTopStyle.bottom) {
backToTopStyle.bottom = this.windowBottom + u.convertToPx(this.backToTopBottom) + 'px';
}
if(!backToTopStyle.position){
backToTopStyle.position = this.usePageScroll ? 'fixed': 'absolute';
}
return backToTopStyle;
},
},
methods: {
//点击返回顶部
_backToTopClick() {
!this.backToTopWithAnimate && this._checkShouldShowBackToTop(0);
this.scrollToTop(this.backToTopWithAnimate);
},
//判断是否要显示返回顶部按钮
_checkShouldShowBackToTop(scrollTop) {
if (!this.autoShowBackToTop) {
this.showBackToTopClass = false;
return;
}
if (scrollTop > this.finalBackToTopThreshold) {
if (!this.showBackToTopClass) {
this.showBackToTopClass = true;
this.lastBackToTopShowTime = new Date().getTime();
u.delay(() => {
this.backToTopClass = 'zp-back-to-top zp-back-to-top-show';
}, 300)
}
} else {
if (this.showBackToTopClass) {
this.backToTopClass = 'zp-back-to-top zp-back-to-top-hide';
u.delay(() => {
this.showBackToTopClass = false;
}, new Date().getTime() - this.lastBackToTopShowTime < 500 ? 0 : 300)
}
}
},
}
}
// [z-paging]通用布局相关模块
// #ifdef APP-NVUE
const weexDom = weex.requireModule('dom');
// #endif
export default {
data() {
return {
systemInfo: null,
cssSafeAreaInsetBottom: -1,
}
},
computed: {
windowTop() {
if (!this.systemInfo) return 0;
//暂时修复vue3中隐藏系统导航栏后windowTop获取不正确的问题,具体bug详见https://ask.dcloud.net.cn/question/141634
//感谢litangyu!!https://github.com/SmileZXLee/uni-z-paging/issues/25
// #ifdef VUE3 && H5
const pageHeadNode = document.getElementsByTagName("uni-page-head");
if (!pageHeadNode.length) return 0;
// #endif
return this.systemInfo.windowTop || 0;
},
safeAreaBottom() {
if (!this.systemInfo) return 0;
let safeAreaBottom = 0;
// #ifdef APP-PLUS
safeAreaBottom = this.systemInfo.safeAreaInsets.bottom || 0 ;
// #endif
// #ifndef APP-PLUS
safeAreaBottom = Math.max(this.cssSafeAreaInsetBottom, 0);
// #endif
return safeAreaBottom;
},
isOldWebView() {
// #ifndef APP-NVUE || MP-KUAISHOU
try {
const systemInfos = systemInfo.system.split(' ');
const deviceType = systemInfos[0];
const version = parseInt(systemInfos[1]);
if ((deviceType === 'iOS' && version <= 10) || (deviceType === 'Android' && version <= 6)) {
return true;
}
} catch(e) {
return false;
}
// #endif
return false;
},
zSlots() {
// #ifdef VUE2
// #ifdef MP-ALIPAY
return this.$slots;
// #endif
return this.$scopedSlots || this.$slots;
// #endif
return this.$slots;
}
},
methods: {
//获取节点尺寸
_getNodeClientRect(select, inDom = true, scrollOffset = false) {
// #ifdef APP-NVUE
select = select.replace(/[.|#]/g, '');
const ref = this.$refs[select];
return new Promise((resolve, reject) => {
if (ref) {
weexDom.getComponentRect(ref, option => {
resolve(option && option.result ? [option.size] : false);
})
} else {
resolve(false);
}
});
return;
// #endif
//#ifdef MP-ALIPAY
inDom = false;
//#endif
let res = !!inDom ? uni.createSelectorQuery().in(inDom === true ? this : inDom) : uni.createSelectorQuery();
scrollOffset ? res.select(select).scrollOffset() : res.select(select).boundingClientRect();
return new Promise((resolve, reject) => {
res.exec(data => {
resolve((data && data != '' && data != undefined && data.length) ? data : false);
});
});
},
//获取slot="left"和slot="right"宽度并且更新布局
_updateLeftAndRightWidth(targetStyle, parentNodePrefix) {
this.$nextTick(() => {
let delayTime = 0;
// #ifdef MP-BAIDU
delayTime = 10;
// #endif
setTimeout(() => {
['left','right'].map(position => {
this._getNodeClientRect(`.${parentNodePrefix}-${position}`).then(res => {
this.$set(targetStyle, position, res ? res[0].width + 'px' : '0px');
});
})
}, delayTime)
})
},
//通过获取css设置的底部安全区域占位view高度设置bottom距离
_getCssSafeAreaInsetBottom(success) {
this._getNodeClientRect('.zp-safe-area-inset-bottom').then(res => {
this.cssSafeAreaInsetBottom = res ? res[0].height : -1;
res && success && success();
});
}
}
}
// [z-paging]空数据图view模块
import u from '.././z-paging-utils'
export default {
props: {
//是否强制隐藏空数据图,默认为否
hideEmptyView: {
type: Boolean,
default: u.gc('hideEmptyView', false)
},
//空数据图描述文字,默认为“没有数据哦~”
emptyViewText: {
type: [String, Object],
default: u.gc('emptyViewText', null)
},
//是否显示空数据图重新加载按钮(无数据时),默认为否
showEmptyViewReload: {
type: Boolean,
default: u.gc('showEmptyViewReload', false)
},
//加载失败时是否显示空数据图重新加载按钮,默认为是
showEmptyViewReloadWhenError: {
type: Boolean,
default: u.gc('showEmptyViewReloadWhenError', true)
},
//空数据图点击重新加载文字,默认为“重新加载”
emptyViewReloadText: {
type: [String, Object],
default: u.gc('emptyViewReloadText', null)
},
//空数据图图片,默认使用z-paging内置的图片
emptyViewImg: {
type: String,
default: u.gc('emptyViewImg', '')
},
//空数据图“加载失败”描述文字,默认为“很抱歉,加载失败”
emptyViewErrorText: {
type: [String, Object],
default: u.gc('emptyViewErrorText', null)
},
//空数据图“加载失败”图片,默认使用z-paging内置的图片
emptyViewErrorImg: {
type: String,
default: u.gc('emptyViewErrorImg', '')
},
//空数据图样式
emptyViewStyle: {
type: Object,
default: function() {
return u.gc('emptyViewStyle', {});
}
},
//空数据图容器样式
emptyViewSuperStyle: {
type: Object,
default: function() {
return u.gc('emptyViewSuperStyle', {});
}
},
//空数据图img样式
emptyViewImgStyle: {
type: Object,
default: function() {
return u.gc('emptyViewImgStyle', {});
}
},
//空数据图描述文字样式
emptyViewTitleStyle: {
type: Object,
default: function() {
return u.gc('emptyViewTitleStyle', {});
}
},
//空数据图重新加载按钮样式
emptyViewReloadStyle: {
type: Object,
default: function() {
return u.gc('emptyViewReloadStyle', {});
}
},
//空数据图片是否铺满z-paging,默认为否,即填充满z-paging内列表(滚动区域)部分。若设置为否,则为填铺满整个z-paging
emptyViewFixed: {
type: Boolean,
default: u.gc('emptyViewFixed', false)
},
//空数据图片是否垂直居中,默认为是,若设置为否即为从空数据容器顶部开始显示。emptyViewFixed为false时有效
emptyViewCenter: {
type: Boolean,
default: u.gc('emptyViewCenter', true)
},
//加载中时是否自动隐藏空数据图,默认为是
autoHideEmptyViewWhenLoading: {
type: Boolean,
default: u.gc('autoHideEmptyViewWhenLoading', true)
},
//用户下拉列表触发下拉刷新加载中时是否自动隐藏空数据图,默认为是
autoHideEmptyViewWhenPull: {
type: Boolean,
default: u.gc('autoHideEmptyViewWhenPull', true)
},
//空数据view的z-index,默认为9
emptyViewZIndex: {
type: Number,
default: u.gc('emptyViewZIndex', 9)
},
},
computed: {
finalEmptyViewImg() {
return this.isLoadFailed ? this.emptyViewErrorImg : this.emptyViewImg;
},
finalShowEmptyViewReload() {
return this.isLoadFailed ? this.showEmptyViewReloadWhenError : this.showEmptyViewReload;
},
showEmpty() {
if (this.refresherOnly || this.hideEmptyView || this.realTotalData.length) return false;
if (this.autoHideEmptyViewWhenLoading) {
if (this.isAddedData && !this.firstPageLoaded && !this.loading) return true;
} else {
return true;
}
return !this.autoHideEmptyViewWhenPull && !this.isUserReload;
},
},
methods: {
//点击了空数据view重新加载按钮
_emptyViewReload() {
let callbacked = false;
this.$emit('emptyViewReload', reload => {
if (reload === undefined || reload === true) {
this.fromEmptyViewReload = true;
this.reload().catch(() => {});
}
callbacked = true;
});
this.$nextTick(() => {
if (!callbacked) {
this.fromEmptyViewReload = true;
this.reload().catch(() => {});
}
})
},
//点击了空数据view
_emptyViewClick() {
this.$emit('emptyViewClick');
},
}
}
\ No newline at end of file
// [z-paging]i18n模块
import { initVueI18n } from '@dcloudio/uni-i18n'
import messages from '../../i18n/index.js'
const { t } = initVueI18n(messages)
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import interceptor from '../z-paging-interceptor'
const language = uni.getSystemInfoSync().language;
export default {
data() {
return {
language
}
},
computed: {
finalLanguage() {
try {
const local = uni.getLocale();
const language = this.language;
return local === 'auto' ? interceptor._handleLanguage2Local(language, this._language2Local(language)) : local;
} catch (e) {
return 'zh-Hans';
}
},
finalRefresherDefaultText() {
return this._getI18nText('zp.refresher.default', this.refresherDefaultText);
},
finalRefresherPullingText() {
return this._getI18nText('zp.refresher.pulling', this.refresherPullingText);
},
finalRefresherRefreshingText() {
return this._getI18nText('zp.refresher.refreshing', this.refresherRefreshingText);
},
finalRefresherCompleteText() {
return this._getI18nText('zp.refresher.complete', this.refresherCompleteText);
},
finalRefresherUpdateTimeTextMap() {
return {
title: t('zp.refresherUpdateTime.title'),
none: t('zp.refresherUpdateTime.none'),
today: t('zp.refresherUpdateTime.today'),
yesterday: t('zp.refresherUpdateTime.yesterday')
};
},
finalLoadingMoreDefaultText() {
return this._getI18nText('zp.loadingMore.default', this.loadingMoreDefaultText);
},
finalLoadingMoreLoadingText() {
return this._getI18nText('zp.loadingMore.loading', this.loadingMoreLoadingText);
},
finalLoadingMoreNoMoreText() {
return this._getI18nText('zp.loadingMore.noMore', this.loadingMoreNoMoreText);
},
finalLoadingMoreFailText() {
return this._getI18nText('zp.loadingMore.fail', this.loadingMoreFailText);
},
finalEmptyViewText() {
return this.isLoadFailed ? this.finalEmptyViewErrorText : this._getI18nText('zp.emptyView.title', this.emptyViewText);
},
finalEmptyViewReloadText() {
return this._getI18nText('zp.emptyView.reload', this.emptyViewReloadText);
},
finalEmptyViewErrorText() {
return this._getI18nText('zp.emptyView.error', this.emptyViewErrorText);
},
finalSystemLoadingText() {
return this._getI18nText('zp.systemLoading.title', this.systemLoadingText);
},
},
methods: {
//获取当前z-paging的语言
getLanguage() {
return this.finalLanguage;
},
//获取国际化转换后的文本
_getI18nText(key, value) {
const dataType = Object.prototype.toString.call(value);
if (dataType === '[object Object]') {
const nextValue = value[this.finalLanguage];
if (nextValue) return nextValue;
} else if (dataType === '[object String]') {
return value;
}
return t(key);
},
//系统language转i18n local
_language2Local(language) {
const formatedLanguage = language.toLowerCase().replace(new RegExp('_', ''), '-');
if (formatedLanguage.indexOf('zh') !== -1) {
if (formatedLanguage === 'zh' || formatedLanguage === 'zh-cn' || formatedLanguage.indexOf('zh-hans') !== -1) {
return 'zh-Hans';
}
return 'zh-Hant';
}
if (formatedLanguage.indexOf('en') !== -1) return 'en';
return language;
}
}
}
// [z-paging]loading相关模块
import u from '.././z-paging-utils'
import Enum from '.././z-paging-enum'
export default {
props: {
//第一次加载后自动隐藏loading slot,默认为是
autoHideLoadingAfterFirstLoaded: {
type: Boolean,
default: u.gc('autoHideLoadingAfterFirstLoaded', true)
},
//loading slot是否铺满屏幕并固定,默认为否
loadingFullFixed: {
type: Boolean,
default: u.gc('loadingFullFixed', false)
},
//是否自动显示系统Loading:即uni.showLoading,若开启则将在刷新列表时(调用reload、refresh时)显示,下拉刷新和滚动到底部加载更多不会显示,默认为false。
autoShowSystemLoading: {
type: Boolean,
default: u.gc('autoShowSystemLoading', false)
},
//显示系统Loading时是否显示透明蒙层,防止触摸穿透,默认为是(H5、App、微信小程序、百度小程序有效)
systemLoadingMask: {
type: Boolean,
default: u.gc('systemLoadingMask', true)
},
//显示系统Loading时显示的文字,默认为"加载中"
systemLoadingText: {
type: [String, Object],
default: u.gc('systemLoadingText', null)
},
},
data() {
return {
loading: false,
loadingForNow: false,
}
},
watch: {
loadingStatus(newVal) {
this.$emit('loadingStatusChange', newVal);
this.$nextTick(() => {
this.loadingStatusAfterRender = newVal;
})
// #ifdef APP-NVUE
if (this.useChatRecordMode) {
if (this.pageNo === this.defaultPageNo && newVal === Enum.More.NoMore) {
this.nIsFirstPageAndNoMore = true;
return;
}
}
this.nIsFirstPageAndNoMore = false;
// #endif
},
loading(newVal){
if (newVal) {
this.loadingForNow = newVal;
}
},
},
computed: {
showLoading() {
if (this.firstPageLoaded || !this.loading || !this.loadingForNow) return false;
if (this.finalShowSystemLoading){
uni.showLoading({
title: this.finalSystemLoadingText,
mask: this.systemLoadingMask
})
}
return this.autoHideLoadingAfterFirstLoaded ? (this.fromEmptyViewReload ? true : !this.pagingLoaded) : this.loadingType === Enum.LoadingType.Refresher;
},
finalShowSystemLoading() {
return this.autoShowSystemLoading && this.loadingType === Enum.LoadingType.Refresher;
}
},
methods: {
//处理开始加载更多状态
_startLoading(isReload = false) {
if ((this.showLoadingMoreWhenReload && !this.isUserPullDown) || !isReload) {
this.loadingStatus = Enum.More.Loading;
}
this.loading = true;
},
//停止系统loading和refresh
_endSystemLoadingAndRefresh(){
this.finalShowSystemLoading && uni.hideLoading();
!this.useCustomRefresher && uni.stopPullDownRefresh();
// #ifdef APP-NVUE
this.usePageScroll && uni.stopPullDownRefresh();
// #endif
}
}
}
// [z-paging]nvue独有部分模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import Enum from '.././z-paging-enum'
// #ifdef APP-NVUE
const weexAnimation = weex.requireModule('animation');
// #endif
export default {
props: {
// #ifdef APP-NVUE
//nvue中修改列表类型,可选值有list、waterfall和scroller,默认为list
nvueListIs: {
type: String,
default: u.gc('nvueListIs', 'list')
},
//nvue waterfall配置,仅在nvue中且nvueListIs=waterfall时有效,配置参数详情参见:https://uniapp.dcloud.io/component/waterfall
nvueWaterfallConfig: {
type: Object,
default: function() {
return u.gc('nvueWaterfallConfig', {});
}
},
//nvue 控制是否回弹效果,iOS不支持动态修改
nvueBounce: {
type: Boolean,
default: u.gc('nvueBounce', true)
},
//nvue中通过代码滚动到顶部/底部时,是否加快动画效果(无滚动动画时无效),默认为否
nvueFastScroll: {
type: Boolean,
default: u.gc('nvueFastScroll', false)
},
//nvue中list的id
nvueListId: {
type: String,
default: u.gc('nvueListId', '')
},
//nvue中refresh组件的样式
nvueRefresherStyle: {
type: Object,
default: function() {
return u.gc('nvueRefresherStyle', {});
}
},
//nvue中是否按分页模式(类似竖向swiper)显示List,默认为false
nvuePagingEnabled: {
type: Boolean,
default: u.gc('nvuePagingEnabled', false)
},
//是否隐藏nvue列表底部的tagView,此view用于标识滚动到底部位置,若隐藏则滚动到底部功能将失效,在nvue中实现吸顶+swiper功能时需将最外层z-paging的此属性设置为true。默认为否
hideNvueBottomTag: {
type: Boolean,
default: u.gc('hideNvueBottomTag', false)
},
//nvue中控制onscroll事件触发的频率:表示两次onscroll事件之间列表至少滚动了10px。注意,将该值设置为较小的数值会提高滚动事件采样的精度,但同时也会降低页面的性能
offsetAccuracy: {
type: Number,
default: u.gc('offsetAccuracy', 10)
},
// #endif
},
data() {
return {
nRefresherLoading: false,
nListIsDragging: false,
nShowBottom: true,
nFixFreezing: false,
nShowRefresherReveal: false,
nIsFirstPageAndNoMore: false,
nFirstPageAndNoMoreChecked: false,
nLoadingMoreFixedHeight: false,
nShowRefresherRevealHeight: 0,
nOldShowRefresherRevealHeight: -1,
nRefresherWidth: uni.upx2px(750),
}
},
watch: {
// #ifdef APP-NVUE
nIsFirstPageAndNoMore: {
handler(newVal) {
const cellStyle = !this.useChatRecordMode || newVal ? {} : { transform: 'rotate(180deg)' };
this.$emit('update:cellStyle', cellStyle);
this.$emit('cellStyleChange', cellStyle);
},
immediate: true
},
// #endif
},
computed: {
// #ifdef APP-NVUE
nScopedSlots() {
// #ifdef VUE2
return this.$scopedSlots;
// #endif
// #ifdef VUE3
return null;
// #endif
},
nWaterfallColumnCount() {
if (this.finalNvueListIs !== 'waterfall') return 0;
return this._nGetWaterfallConfig('column-count', 2);
},
nWaterfallColumnWidth() {
return this._nGetWaterfallConfig('column-width', 'auto');
},
nWaterfallColumnGap() {
return this._nGetWaterfallConfig('column-gap', 'normal');
},
nWaterfallLeftGap() {
return this._nGetWaterfallConfig('left-gap', 0);
},
nWaterfallRightGap() {
return this._nGetWaterfallConfig('right-gap', 0);
},
nViewIs() {
const is = this.finalNvueListIs;
return is === 'scroller' || is === 'view' ? 'view' : is === 'waterfall' ? 'header' : 'cell';
},
nSafeAreaBottomHeight() {
return this.safeAreaInsetBottom ? this.safeAreaBottom : 0;
},
nChatRecordRotateStyle() {
return this.useChatRecordMode ? { transform: this.nIsFirstPageAndNoMore ? 'rotate(0deg)' : 'rotate(180deg)' } : {};
},
finalNvueListIs() {
if (this.usePageScroll) return 'view';
const nvueListIsLowerCase = this.nvueListIs.toLowerCase();
if (['list','waterfall','scroller'].indexOf(nvueListIsLowerCase) !== -1) return nvueListIsLowerCase;
return 'list';
},
finalNvueSuperListIs() {
return this.usePageScroll ? 'view' : 'scroller';
},
finalNvueRefresherEnabled() {
return this.finalNvueListIs !== 'view' && this.finalRefresherEnabled && !this.nShowRefresherReveal && !this.useChatRecordMode;
},
// #endif
},
mounted(){
// #ifdef APP-NVUE
//旋转屏幕时更新宽度
uni.onWindowResize((res) => {
// this._nUpdateRefresherWidth();
})
// #endif
},
methods: {
// #ifdef APP-NVUE
//列表滚动时触发
_nOnScroll(e) {
this.$emit('scroll', e);
const contentOffsetY = -e.contentOffset.y;
this.oldScrollTop = contentOffsetY;
this.nListIsDragging = e.isDragging;
this._checkShouldShowBackToTop(contentOffsetY, contentOffsetY - 1);
},
//下拉刷新刷新中
_nOnRrefresh() {
if (this.nShowRefresherReveal) return;
this.nRefresherLoading = true;
this.refresherStatus = Enum.Refresher.Loading;
this._doRefresherLoad();
},
//下拉刷新下拉中
_nOnPullingdown(e) {
if (this.refresherStatus === Enum.Refresher.Loading || (this.isIos && !this.nListIsDragging)) return;
this._emitTouchmove(e);
const { viewHeight, pullingDistance } = e;
this.refresherStatus = pullingDistance >= viewHeight ? Enum.Refresher.ReleaseToRefresh : Enum.Refresher.Default;
},
//下拉刷新结束
_nRefresherEnd(doEnd = true) {
if (doEnd) {
this._nDoRefresherEndAnimation(0, -this.nShowRefresherRevealHeight);
!this.usePageScroll && this.$refs['zp-n-list'].resetLoadmore();
this.nRefresherLoading = false;
}
},
//执行主动触发下拉刷新动画
_nDoRefresherEndAnimation(height, translateY, animate = true, checkStack = true) {
this._cleanRefresherCompleteTimeout();
this._cleanRefresherEndTimeout();
if (!this.finalShowRefresherWhenReload) {
this.refresherEndTimeout = u.delay(() => {
this.refresherStatus = Enum.Refresher.Default;
}, this.refresherCompleteDuration);
return;
}
const stackCount = this.refresherRevealStackCount;
if (height === 0 && checkStack) {
this.refresherRevealStackCount --;
if (stackCount > 1) return;
this.refresherEndTimeout = u.delay(() => {
this.refresherStatus = Enum.Refresher.Default;
}, this.refresherCompleteDuration);
}
if (stackCount > 1) {
this.refresherStatus = Enum.Refresher.Loading;
}
const duration = animate ? 200 : 0;
if (this.nOldShowRefresherRevealHeight !== height) {
if (height > 0) {
this.nShowRefresherReveal = true;
}
weexAnimation.transition(this.$refs['zp-n-list-refresher-reveal'], {
styles: {
height: `${height}px`,
transform: `translateY(${translateY}px)`,
},
duration,
timingFunction: 'linear',
needLayout: true,
delay: 0
})
}
u.delay(() => {
if (animate) {
this.nShowRefresherReveal = height > 0;
}
}, duration > 0 ? duration - 60 : 0);
this.nOldShowRefresherRevealHeight = height;
},
//滚动到底部加载更多
_nOnLoadmore() {
if (this.nShowRefresherReveal || !this.totalData.length) return;
this.useChatRecordMode ? this.doChatRecordLoadMore() : this._onLoadingMore('toBottom');
},
//获取nvue waterfall单项配置
_nGetWaterfallConfig(key, defaultValue) {
return this.nvueWaterfallConfig[key] || defaultValue;
},
//更新nvue 下拉刷新view容器的宽度
_nUpdateRefresherWidth() {
u.delay(() => {
this.$nextTick(()=>{
this._getNodeClientRect('.zp-n-list').then(node => {
if (node) {
this.nRefresherWidth = node[0].width || this.nRefresherWidth;
}
})
})
})
}
// #endif
}
}
// [z-paging]处理main.js中的配置信息工具
let config = null;
let getedStorage = false;
const storageKey = 'Z-PAGING-CONFIG-STORAGE-KEY'
function setConfig(value) {
uni.setStorageSync(storageKey, value);
}
function getConfig() {
if (getedStorage) return config;
config = uni.getStorageSync(storageKey);
getedStorage = true;
return config;
}
export default {
setConfig,
getConfig
};
// [z-paging]常量
export default {
version: '2.5.9',
delayTime: 100,
errorUpdateKey: 'z-paging-error-emit',
completeUpdateKey: 'z-paging-complete-emit',
cachePrefixKey: 'z-paging-cache',
listCellIndexKey: 'zp_index',
listCellIndexUniqueKey: 'zp_unique_index'
}
// [z-paging]枚举
export default {
//当前加载类型 0.下拉刷新 1.上拉加载更多
LoadingType: {
Refresher: 0,
LoadingMore: 1
},
//下拉刷新状态 0.默认状态 1.松手立即刷新 2.刷新中 3.刷新结束
Refresher: {
Default: 0,
ReleaseToRefresh: 1,
Loading: 2,
Complete: 3
},
//底部加载更多状态 0.默认状态 1.加载中 2.没有更多数据 3.加载失败
More: {
Default: 0,
Loading: 1,
NoMore: 2,
Fail: 3
},
//@query触发来源 0.用户主动下拉刷新 1.通过reload触发 2.通过refresh触发 3.通过滚动到底部加载更多或点击底部加载更多触发
QueryFrom: {
UserPullDown: 0,
Reload: 1,
Refresh: 2,
LoadingMore: 3
},
//虚拟列表cell高度模式
CellHeightMode: {
//固定高度
Fixed: 'fixed',
//动态高度
Dynamic: 'dynamic'
},
//列表缓存模式
CacheMode: {
//默认模式,只会缓存一次
Default: 'default',
//总是缓存,每次列表刷新(下拉刷新、调用reload等)都会更新缓存
Always: 'always'
}
}
\ No newline at end of file
// [z-paging]拦截器
//拦截&处理@query事件
function handleQuery(callback) {
try {
setTimeout(function() {
_getApp().globalData.zp_handleQueryCallback = callback;
}, 1);
} catch (e) {}
}
//拦截&处理@query事件(私有,请勿调用)
function _handleQuery(pageNo, pageSize, from, lastItem){
const callback = _getApp().globalData.zp_handleQueryCallback;
return callback ? callback(pageNo, pageSize, from, lastItem) : [pageNo, pageSize, from];
}
//拦截&处理系统language转i18n local
function handleLanguage2Local(callback) {
try {
setTimeout(function() {
_getApp().globalData.zp_handleLanguage2LocalCallback = callback;
}, 1);
} catch (e) {}
}
//拦截&处理系统language转i18n local(私有,请勿调用)
function _handleLanguage2Local(language, local){
const callback = _getApp().globalData.zp_handleLanguage2LocalCallback;
return callback ? callback(language, local) : local;
}
//获取当前app对象
function _getApp(){
// #ifndef APP-NVUE
return getApp();
// #endif
// #ifdef APP-NVUE
return getApp({ allowDefault: true });
// #endif
}
export default {
handleQuery,
_handleQuery,
handleLanguage2Local,
_handleLanguage2Local
};
// [z-paging]使用页面滚动时引入此mixin,用于监听和处理onPullDownRefresh等页面生命周期方法
export default {
onPullDownRefresh() {
if (this.isPagingRefNotFound()) return;
this.$refs.paging.reload().catch(() => {});
},
onPageScroll(e) {
if (this.isPagingRefNotFound()) return;
this.$refs.paging.updatePageScrollTop(e.scrollTop);
e.scrollTop < 10 && this.$refs.paging.doChatRecordLoadMore();
},
onReachBottom() {
if (this.isPagingRefNotFound()) return;
this.$refs.paging.pageReachBottom();
},
methods: {
isPagingRefNotFound() {
return !this.$refs.paging;
}
}
}
// [z-paging]工具类
import zConfig from './z-paging-config'
import zLocalConfig from '../config/index'
import c from './z-paging-constant'
const storageKey = 'Z-PAGING-REFRESHER-TIME-STORAGE-KEY';
let config = null;
const timeoutMap = {};
/*
当z-paging未使用uni_modules管理时,控制台会有警告:WARNING: Module not found: Error: Can't resolve '@/uni_modules/z-paging'...
此时注释下方try中的代码即可
*/
// #ifdef VUE2
try {
const contextKeys = require.context('@/uni_modules/z-paging', false, /\z-paging-config$/).keys();
if (contextKeys.length) {
const suffix = '.js';
config = require('@/uni_modules/z-paging/z-paging-config' + suffix);
}
} catch (e) {}
// #endif
//获取默认配置信息
function gc(key, defaultValue) {
if (!config) {
if (zLocalConfig && Object.keys(zLocalConfig).length) {
config = zLocalConfig;
} else {
const tempConfig = zConfig.getConfig();
if (zConfig && tempConfig) {
config = tempConfig;
}
}
}
if (!config) return defaultValue;
const value = config[_toKebab(key)];
return value === undefined ? defaultValue : value;
}
//获取最终的touch位置
function getTouch(e) {
let touch = null;
if (e.touches && e.touches.length) {
touch = e.touches[0];
} else if (e.changedTouches && e.changedTouches.length) {
touch = e.changedTouches[0];
} else if (e.datail && e.datail != {}) {
touch = e.datail;
} else {
return {
touchX: 0,
touchY: 0
}
}
return {
touchX: touch.clientX,
touchY: touch.clientY
};
}
//判断当前手势是否在z-paging内触发
function getTouchFromZPaging(target) {
if (target && target.tagName && target.tagName !== 'BODY' && target.tagName !== 'UNI-PAGE-BODY') {
const classList = target.classList;
if (classList && classList.contains('z-paging-content')) {
return {
isFromZp: true,
isPageScroll: classList.contains('z-paging-content-page'),
isReachedTop: classList.contains('z-paging-reached-top')
};
} else {
return getTouchFromZPaging(target.parentNode);
}
} else {
return { isFromZp: false };
}
}
//获取z-paging所在的parent
function getParent(parent) {
if (!parent) return null;
if (parent.$refs.paging) return parent;
return getParent(parent.$parent);
}
//打印错误信息
function consoleErr(err) {
console.error(`[z-paging]${err}`);
}
//延时操作,如果key存在,调用时根据key停止之前的延时操作
function delay(callback, ms = c.delayTime, key) {
const timeout = setTimeout(callback, ms);;
if (!!key) {
timeoutMap[key] && clearTimeout(timeoutMap[key]);
timeoutMap[key] = timeout;
}
return timeout;
}
//设置下拉刷新时间
function setRefesrherTime(time, key) {
const datas = getRefesrherTime() || {};
datas[key] = time;
uni.setStorageSync(storageKey, datas);
}
//获取下拉刷新时间
function getRefesrherTime() {
return uni.getStorageSync(storageKey);
}
//通过下拉刷新标识key获取下拉刷新时间
function getRefesrherTimeByKey(key) {
const datas = getRefesrherTime();
return datas && datas[key] ? datas[key] : null;
}
//通过下拉刷新标识key获取下拉刷新时间(格式化之后)
function getRefesrherFormatTimeByKey(key, textMap) {
const time = getRefesrherTimeByKey(key);
const timeText = time ? _timeFormat(time, textMap) : textMap.none;
return `${textMap.title}${timeText}`;
}
//将文本的px或者rpx转为px的值
function convertToPx(text) {
const dataType = Object.prototype.toString.call(text);
if (dataType === '[object Number]') return text;
let isRpx = false;
if (text.indexOf('rpx') !== -1 || text.indexOf('upx') !== -1) {
text = text.replace('rpx', '').replace('upx', '');
isRpx = true;
} else if (text.indexOf('px') !== -1) {
text = text.replace('px', '');
}
if (!isNaN(text)) {
if (isRpx) return Number(uni.upx2px(text));
return Number(text);
}
return 0;
}
//获取当前时间
function getTime() {
return (new Date()).getTime();
}
//获取z-paging实例id
function getInstanceId() {
const s = [];
const hexDigits = "0123456789abcdef";
for (let i = 0; i < 10; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
return s.join('') + getTime();
}
// 等待一段时间
function wait(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
//------------------ 私有方法 ------------------------
//时间格式化
function _timeFormat(time, textMap) {
const date = new Date(time);
const currentDate = new Date();
const dateDay = new Date(time).setHours(0, 0, 0, 0);
const currentDateDay = new Date().setHours(0, 0, 0, 0);
const disTime = dateDay - currentDateDay;
let dayStr = '';
const timeStr = _dateTimeFormat(date);
if (disTime === 0) {
dayStr = textMap.today;
} else if (disTime === -86400000) {
dayStr = textMap.yesterday;
} else {
dayStr = _dateDayFormat(date, date.getFullYear() !== currentDate.getFullYear());
}
return `${dayStr} ${timeStr}`;
}
//date格式化为年月日
function _dateDayFormat(date, showYear = true) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return showYear ? `${year}-${_fullZeroToTwo(month)}-${_fullZeroToTwo(day)}` : `${_fullZeroToTwo(month)}-${_fullZeroToTwo(day)}`;
}
//data格式化为时分
function _dateTimeFormat(date) {
const hour = date.getHours();
const minute = date.getMinutes();
return `${_fullZeroToTwo(hour)}:${_fullZeroToTwo(minute)}`;
}
//不满2位在前面填充0
function _fullZeroToTwo(str) {
str = str.toString();
return str.length === 1 ? '0' + str : str;
}
//驼峰转短横线
function _toKebab(value) {
return value.replace(/([A-Z])/g, "-$1").toLowerCase();
}
export default {
gc,
setRefesrherTime,
getRefesrherFormatTimeByKey,
getTouch,
getTouchFromZPaging,
getParent,
convertToPx,
getTime,
getInstanceId,
consoleErr,
delay,
wait
};
// [z-paging]使用renderjs在app-vue和h5中对touchmove事件冒泡进行处理
import u from '../js/z-paging-utils'
const data = {
startY: 0,
isTouchFromZPaging: false,
isUsePageScroll: false,
isReachedTop: true,
isIosAndH5: false,
appLaunched: false
}
export default {
mounted() {
if (window) {
this._handleTouch();
// #ifdef APP-VUE
this.$ownerInstance.callMethod('_handlePageLaunch');
// #endif
}
},
methods: {
//接收逻辑层发送的数据
renderPropIsIosAndH5Change(newVal) {
if (newVal === -1) return;
data.isIosAndH5 = newVal;
},
//拦截处理touch事件
_handleTouch() {
if (!window.$zPagingRenderJsInited) {
window.$zPagingRenderJsInited = true;
window.addEventListener('touchstart', this._handleTouchstart, { passive: true })
window.addEventListener('touchmove', this._handleTouchmove, { passive: false })
}
},
_handleTouchstart(e) {
const touch = u.getTouch(e);
data.startY = touch.touchY;
const touchResult = u.getTouchFromZPaging(e.target);
data.isTouchFromZPaging = touchResult.isFromZp;
data.isUsePageScroll = touchResult.isPageScroll;
data.isReachedTop = touchResult.isReachedTop;
},
_handleTouchmove(e) {
const touch = u.getTouch(e);
const moveY = touch.touchY - data.startY;
if (data.isTouchFromZPaging && ((data.isReachedTop && moveY > 0) || (data.isIosAndH5 && !data.isUsePageScroll && moveY < 0))) {
if (e.cancelable && !e.defaultPrevented) {
e.preventDefault();
}
}
},
_removeAllEventListener(){
window.removeEventListener('touchstart');
window.removeEventListener('touchmove');
}
}
};
// [z-paging]微信小程序、QQ小程序、app-vue、h5上使用wxs实现自定义下拉刷新,降低逻辑层与视图层的通信折损,提升性能
var currentDis = 0;
var isPCFlag = -1;
var startY = -1;
function propObserver(newValue, oldValue, ownerIns, ins) {
var state = ownerIns.getState() || {};
state.currentIns = ins;
var dataset = ins.getDataset();
var loading = dataset.loading == true;
if (newValue && newValue.indexOf('end') != -1) {
var transition = newValue.split('end')[0];
_setTransform('translateY(0px)', ins, false, transition);
state.moveDis = 0;
state.oldMoveDis = 0;
currentDis = 0;
} else if (newValue && newValue.indexOf('begin') != -1) {
var refresherThreshold = ins.getDataset().refresherthreshold;
_setTransformValue(refresherThreshold, ins, state, false);
}
}
function touchstart(e, ownerIns) {
var ins = _getIns(ownerIns);
var state = {};
var dataset = {};
ownerIns.callMethod('_handleListTouchstart');
if (ins) {
state = ins.getState();
dataset = ins.getDataset();
if (_touchDisabled(e, ins, 0)) return;
}
var isTouchEnded = state.isTouchEnded;
state.oldMoveDis = 0;
var touch = _getTouch(e);
var loading = _isTrue(dataset.loading);
state.startY = touch.touchY;
startY = state.startY;
state.lastTouch = touch;
if (!loading && isTouchEnded) {
state.isTouchmoving = false;
}
state.isTouchEnded = false;
ownerIns.callMethod('_handleRefresherTouchstart', touch);
}
function touchmove(e, ownerIns) {
var touch = _getTouch(e);
var ins = _getIns(ownerIns);
var dataset = ins.getDataset();
var refresherThreshold = dataset.refresherthreshold;
var isIos = _isTrue(dataset.isios);
var state = ins.getState();
var watchTouchDirectionChange = _isTrue(dataset.watchtouchdirectionchange);
var moveDisObj = {};
var moveDis = 0;
var prevent = false;
if (watchTouchDirectionChange) {
moveDisObj = _getMoveDis(e, ins);
moveDis = moveDisObj.currentDis;
prevent = moveDisObj.isDown;
var direction = prevent ? 'top' : 'bottom';
if (prevent == state.oldTouchDirection && prevent != state.oldEmitedTouchDirection) {
ownerIns.callMethod('_handleTouchDirectionChange', { direction: direction });
state.oldEmitedTouchDirection = prevent;
}
state.oldTouchDirection = prevent;
}
if (_touchDisabled(e, ins, 1)) {
_handlePullingDown(state, ownerIns, false);
return true;
}
if (!_getAngleIsInRange(e, touch, state, dataset)) {
_handlePullingDown(state, ownerIns, false);
return true;
}
moveDisObj = _getMoveDis(e, ins);
moveDis = moveDisObj.currentDis;
prevent = moveDisObj.isDown;
if (moveDis < 0) {
_setTransformValue(0, ins, state, false);
_handlePullingDown(state, ownerIns, false);
return true;
}
if (prevent && !state.disabledBounce) {
ownerIns.callMethod('_handleScrollViewDisableBounce', {bounce: false});
state.disabledBounce = true;
_handlePullingDown(state, ownerIns, prevent);
return !prevent;
}
_setTransformValue(moveDis, ins, state, false);
var oldRefresherStatus = state.refresherStatus;
var oldIsTouchmoving = _isTrue(dataset.oldistouchmoving);
var hasTouchmove = _isTrue(dataset.hastouchmove);
var isTouchmoving = state.isTouchmoving;
state.refresherStatus = moveDis >= refresherThreshold ? 1 : 0;
if (!isTouchmoving) {
state.isTouchmoving = true;
isTouchmoving = true;
}
if (state.isTouchEnded) {
state.isTouchEnded = false;
}
if (hasTouchmove) {
ownerIns.callMethod('_handleWxsPullingDown', { moveDis:moveDis, diffDis:moveDisObj.diffDis });
}
if (oldRefresherStatus == undefined || oldRefresherStatus != state.refresherStatus || oldIsTouchmoving != isTouchmoving) {
ownerIns.callMethod('_handleRefresherTouchmove', moveDis, touch);
}
_handlePullingDown(state, ownerIns, prevent);
return !prevent;
}
function touchend(e, ownerIns) {
var touch = _getTouch(e);
var ins = _getIns(ownerIns);
var dataset = ins.getDataset();
var state = ins.getState();
if (_touchDisabled(e, ins, 2)) return;
state.reachMaxAngle = true;
state.hitReachMaxAngleCount = 0;
state.disabledBounce = false;
if (!state.isTouchmoving) return;
var oldRefresherStatus = state.refresherStatus;
var oldMoveDis = state.moveDis;
var refresherThreshold = ins.getDataset().refresherthreshold
var moveDis = _getMoveDis(e, ins).currentDis;
if (!(moveDis >= refresherThreshold && oldRefresherStatus === 1)) {
state.isTouchmoving = false;
}
ownerIns.callMethod('_handleRefresherTouchend', moveDis);
state.isTouchEnded = true;
if (oldMoveDis < refresherThreshold) return;
var animate = false;
if (moveDis >= refresherThreshold) {
moveDis = refresherThreshold;
animate = true;
}
_setTransformValue(moveDis, ins, state, animate);
}
// #ifdef H5
function isPC() {
if (!navigator) return false;
if (isPCFlag != -1) return isPCFlag;
var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
isPCFlag = agents.every(function(item) { return navigator.userAgent.indexOf(item) < 0 });
return isPCFlag;
}
var movable = false;
function mousedown(e, ins) {
if (!isPC()) return;
touchstart(e, ins);
movable = true;
}
function mousemove(e, ins) {
if (!isPC() || !movable) return;
touchmove(e, ins);
}
function mouseup(e, ins) {
if (!isPC()) return;
touchend(e, ins);
movable = false;
}
function mouseleave(e, ins) {
if (!isPC()) return;
movable = false;
}
// #endif
function _setTransformValue(value, ins, state, animate) {
value = value || 0;
if (state.moveDis == value) return;
state.moveDis = value;
_setTransform('translateY(' + value + 'px)', ins, animate, '');
}
function _setTransform(transform, ins, animate, transition) {
var dataset = ins.getDataset();
if (_isTrue(dataset.refreshernotransform)) return;
transform = transform == 'translateY(0px)' ? 'none' : transform;
ins.requestAnimationFrame(function() {
var stl = { 'transform': transform };
if (animate) {
stl['transition'] = 'transform .1s linear';
}
if (transition.length) {
stl['transition'] = transition;
}
ins.setStyle(stl);
})
}
function _getMoveDis(e, ins) {
var state = ins.getState();
var refresherThreshold = parseFloat(ins.getDataset().refresherthreshold);
var refresherOutRate = parseFloat(ins.getDataset().refresheroutrate);
var refresherPullRate = parseFloat(ins.getDataset().refresherpullrate);
var touch = _getTouch(e);
var currentStartY = !state.startY || state.startY == 'NaN' ? startY : state.startY;
var moveDis = touch.touchY - currentStartY;
var oldMoveDis = state.oldMoveDis || 0;
state.oldMoveDis = moveDis;
var diffDis = moveDis - oldMoveDis;
if (diffDis > 0) {
diffDis = diffDis * refresherPullRate;
if (currentDis > refresherThreshold) {
diffDis = diffDis * (1 - refresherOutRate);
}
}
diffDis = diffDis > 100 ? diffDis / 100 : (diffDis > 20 ? diffDis / 20 : diffDis);
currentDis += diffDis;
currentDis = Math.max(0, currentDis);
return {
currentDis: currentDis,
diffDis: diffDis,
isDown: diffDis > 0
};
}
function _getTouch(e) {
var touch = e;
if (e.touches && e.touches.length) {
touch = e.touches[0];
} else if (e.changedTouches && e.changedTouches.length) {
touch = e.changedTouches[0];
} else if (e.datail && e.datail != {}) {
touch = e.datail;
}
return {
touchX: touch.clientX,
touchY: touch.clientY
};
}
function _getIns(ownerIns) {
var ins = ownerIns.getState().currentIns;
if (!ins) {
ownerIns.callMethod('_handlePropUpdate');
}
return ins;
}
function _touchDisabled(e, ins, processTag) {
var dataset = ins.getDataset();
var state = ins.getState();
var loading = _isTrue(dataset.loading);
var useChatRecordMode = _isTrue(dataset.usechatrecordmode);
var refresherEnabled = _isTrue(dataset.refresherenabled);
var useCustomRefresher = _isTrue(dataset.usecustomrefresher);
var usePageScroll = _isTrue(dataset.usepagescroll);
var pageScrollTop = parseFloat(dataset.pagescrolltop);
var scrollTop = parseFloat(dataset.scrolltop);
return loading || useChatRecordMode || !refresherEnabled || !useCustomRefresher ||
(usePageScroll && useCustomRefresher && pageScrollTop > 5) ||
(!usePageScroll && useCustomRefresher && scrollTop > 5);
}
function _getAngleIsInRange(e, touch, state, dataset) {
var maxAngle = dataset.refreshermaxangle;
var refresherAecc = _isTrue(dataset.refresheraecc);
var lastTouch = state.lastTouch;
var reachMaxAngle = state.reachMaxAngle;
var moveDis = state.oldMoveDis;
if (!lastTouch) return true;
if (maxAngle >= 0 && maxAngle <= 90 && lastTouch) {
if ((!moveDis || moveDis < 1) && !refresherAecc && reachMaxAngle != null && !reachMaxAngle) return false;
var x = Math.abs(touch.touchX - lastTouch.touchX);
var y = Math.abs(touch.touchY - lastTouch.touchY);
var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if ((x || y) && x > 1) {
var angle = Math.asin(y / z) / Math.PI * 180;
if (angle < maxAngle) {
var hitReachMaxAngleCount = state.hitReachMaxAngleCount || 0;
state.hitReachMaxAngleCount = ++hitReachMaxAngleCount;
if (state.hitReachMaxAngleCount > 2) {
state.lastTouch = touch;
state.reachMaxAngle = false;
}
return false;
}
}
}
state.lastTouch = touch;
return true;
}
function _handlePullingDown(state, ins, onPullingDown) {
var oldOnPullingDown = state.onPullingDown || false;
if (oldOnPullingDown != onPullingDown) {
ins.callMethod('_handleWxsPullingDownStatusChange', onPullingDown);
}
state.onPullingDown = onPullingDown;
}
function _isTrue(value) {
value = (typeof(value) === 'string' ? JSON.parse(value) : value) || false;
return value == true || value == 'true';
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
mousedown: mousedown,
mousemove: mousemove,
mouseup: mouseup,
mouseleave: mouseleave,
propObserver: propObserver
}
{
"id": "z-paging",
"name": "z-paging",
"displayName": "【z-paging下拉刷新、上拉加载】高性能,全平台兼容。支持虚拟列表,支持nvue、vue3",
"version": "2.5.9",
"description": "超简单、低耦合!使用wxs+renderjs实现。支持长列表优化,支持自定义下拉刷新、上拉加载更多,支持自动管理空数据图、点击返回顶部,支持聊天分页、本地分页,支持国际化等100+项配置",
"keywords": [
"下拉刷新",
"上拉加载",
"分页器",
"nvue",
"虚拟列表"
],
"repository": "https://github.com/SmileZXLee/uni-z-paging",
"engines": {
"HBuilderX": "^3.0.7"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "393727164"
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/z-paging",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}
\ No newline at end of file
# z-paging
<p align="center">
<img alt="logo" src="https://z-paging.zxlee.cn/img/title-logo.png" height="100" style="margin-bottom: 50px;">
</p>
[![version](https://img.shields.io/badge/version-2.5.9-blue)](https://github.com/SmileZXLee/uni-z-paging)
[![license](https://img.shields.io/github/license/SmileZXLee/uni-z-paging)](https://en.wikipedia.org/wiki/MIT_License)
### 文档地址:[https://z-paging.zxlee.cn](https://z-paging.zxlee.cn)
### 更新组件前,请注意[版本差异](https://z-paging.zxlee.cn/start/upgrade-guide.html)
***
### 功能&特点
* 【配置简单】仅需两步(绑定网络请求方法、绑定分页结果数组)轻松完成完整下拉刷新,上拉加载更多功能。
* 【低耦合,低侵入】分页自动管理。在page中无需处理任何分页相关逻辑,无需在data中定义任何分页相关变量,全由z-paging内部处理。
* 【超灵活,支持各种类型自定义】支持自定义下拉刷新,自定义上拉加载更多等各种自定义效果;支持使用内置自动分页,同时也支持通过监听下拉刷新和滚动到底部事件自行处理;支持使用自带全屏布局规范,同时也支持将z-paging自由放在任意容器中。
* 【功能丰富】支持国际化,支持自定义且自动管理空数据图,支持主题模式切换,支持本地分页,支持聊天分页模式,支持展示最后更新时间,支持吸顶效果,支持内部scroll-view滚动与页面滚动,支持一键滚动到顶部等诸多功能。
* 【全平台兼容】支持vue、nvue,vue2、vue3,支持h5、app及各家小程序。
* 【高性能】在app-vue、h5、微信小程序、QQ小程序上使用wxs+renderjs从视图层实现下拉刷新;支持虚拟列表,轻松渲染万级数据!
***
### 反馈qq群
* 官方1群`已满`[790460711](https://jq.qq.com/?_wv=1027&k=vU2fKZZH)
* 官方2群:[371624008](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=avPmibADf2TNi4LxkIwjCE5vbfXpa-r1&authKey=dQ%2FVDAR87ONxI4b32Py%2BvmXbhnopjHN7%2FJPtdsqJdsCPFZB6zDQ17L06Uh0kITUZ&noverify=0&group_code=371624008)
***
### 预览
***
| 自定义下拉刷新效果演示 | 滑动切换选项卡+吸顶演示 |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| ![](https://z-paging.zxlee.cn/public/img/z-paging-demo5.gif) | ![](https://z-paging.zxlee.cn/public/img/z-paging-demo6.gif) |
| 聊天记录模式演示 | 虚拟列表(流畅渲染1万+条)演示 |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| ![](https://z-paging.zxlee.cn/public/img/z-paging-demo7.gif) | ![](https://z-paging.zxlee.cn/public/img/z-paging-demo8.gif) |
### 在线demo体验地址:
* [https://demo.z-paging.zxlee.cn](https://demo.z-paging.zxlee.cn)
| 扫码体验 |
| ------------------------------------------------------------ |
| ![](https://z-paging.zxlee.cn/public/img/code.png) |
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!