🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@vtx/ember

Package Overview
Dependencies
Maintainers
6
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vtx/ember - npm Package Compare versions

Comparing version
3.0.9
to
3.0.10
+585
components/v-facil.../data-analysis-v2/monitor-chart.bak.vue
<template>
<card title="指标趋势" style="margin-top: 20rpx;flex:none">
<template v-slot:rightContent>
<view class="history-query-wrap">
<view style="width: 160rpx">
<v-select
:data="
indicatorCycleFilterList.map(item => ({
key: item.value,
value: item.label,
}))
"
v-model="collectFrequency"
></v-select>
</view>
<view class="date-wrap">
<view
class="date-item"
v-for="item in radioList"
:key="item.key"
:class="{ selected: item.key === type }"
@click="type = item.key"
>{{ item.value }}
</view>
</view>
</view>
</template>
<view style="width: 100%">
<view
v-show="dataList.length !== 0"
style="height: 600rpx; width: calc(100vw - 80rpx)"
ref="echartsRef"
></view>
<!-- <canvas
v-show="dataList.length !== 0"
canvas-id="uchart-canvas"
id="uchart-canvas"
@tap="tap"
/> -->
<u-empty
v-show="dataList.length == 0"
text="暂无内容"
style="height: 600rpx; width: 100%"
/>
</view>
</card>
</template>
<script>
import card from '../card-v2.vue';
import mixins from '../mixins';
import { request } from '@vtx/utils';
import dayjs from 'dayjs';
import * as echarts from 'echarts';
import uCharts from '@qiun/ucharts';
let uChartsInstance = {};
export default {
components: { card },
mixins: [mixins],
props: {
deviceId: String,
monitorItemCodes: String,
},
data() {
return {
radioList: [
{
key: '1',
value: '今日',
factors: [
'realtime',
'minute1',
'minute5',
'minute10',
'minute15',
'minute30',
'hour',
'hour2',
'hour4',
'hour6',
'hour12',
],
startTime: dayjs().format('YYYY-MM-DD 00:00:00'),
endTime: dayjs().format('YYYY-MM-DD 23:59:59'),
},
{
key: '7',
value: '近7天',
factors: [
'hour',
'hour2',
'hour4',
'hour6',
'hour12',
'day',
'minute30',
'minute15',
'minute10',
'minute5',
'minute1',
'realtime',
],
startTime: dayjs()
.subtract(7, 'day')
.format('YYYY-MM-DD 00:00:00'),
endTime: dayjs().format('YYYY-MM-DD 23:59:59'),
},
],
type: '1',
hisData: [],
dataList: [],
myChart: null,
// 检测项
factorList: [],
// 所有统计周期枚举
collectFrequencyList: [],
// 所有的指标周期下拉
indicatorCycleList: [],
// 过滤后的指标周期下拉
indicatorCycleFilterList: [],
collectFrequency: undefined,
};
},
computed: {
watchParams() {
return {
type: this.type,
deviceId: this.deviceId,
collectFrequency: this.collectFrequency,
monitorItemCodes: this.monitorItemCodes,
};
},
},
watch: {
deviceId(val) {
if (val) {
// 拿到设备对应的因子配置
this.getFactorList();
// 因子配置的枚举
this.getEnumList();
}
},
// 根据时间跨度 和 所有统计周期枚举 确定所有的指标周期下拉
type(val) {
if (val) {
this.setIndicatorCycleList();
}
},
collectFrequencyList(val) {
if (val?.length) {
this.setIndicatorCycleList();
}
},
// 根据设备对应的因子配置 和 所有的指标周期下拉 和 因子 确认过滤后的指标周期下拉 并且默认第一个指标周期
factorList(val) {
if (val?.length) {
this.setCollectFrequency();
}
},
indicatorCycleList(val) {
if (val?.length) {
this.setCollectFrequency();
}
},
monitorItemCodes(val) {
if (val) {
this.setCollectFrequency();
}
},
// 根据多个参数改变调接口
watchParams() {
if (this.type && this.deviceId && this.collectFrequency && this.monitorItemCodes) {
this.hisDataByTime();
}
},
hisData(val) {
let _xAxis = [],
_yAxis = [],
_series = [];
const chartColor = [
'#73A0FA',
'#73DEB3',
'#29A634',
'#9CBF30',
'#F3B825',
'#EB532E',
'#DB2C6F',
'#9179F2',
];
let textColor = '#929292';
let bgColor = '#fff';
let borderColor = '#fff';
val.map((v, index) => {
const { dataList, monitorItemName, unit } = v;
if (!_xAxis?.length) {
_xAxis = dataList.map(d => d.timeDesc);
}
let values = dataList.map(d => d.value);
this.dataList = dataList;
let name = `${monitorItemName || ''}${unit ? `(${unit})` : ''}`;
let color = chartColor[index % chartColor.length];
let markLine = undefined,
max = undefined;
if (val?.length == 1) {
const { alarmConfig } = v;
const { alarmList, alarmMax } =
this.getAlarmLineAreaData(alarmConfig, values) || [];
if (alarmList?.length) {
markLine = {
data: alarmList,
lineStyle: {
color: 'red',
width: 2,
},
label: {
position: 'end',
distance: [10],
},
};
max = alarmMax;
}
}
_series.push({
name,
data: values,
color,
type: 'line',
yAxisIndex: _yAxis.length,
markLine,
});
_yAxis.push({
name: name,
type: 'value',
nameLocation: 'middle',
nameTextStyle: {
color,
},
nameGap: 0,
offset: _yAxis.length ? _yAxis.length * 45 : 0,
position: 'left',
alignTicks: true,
axisLabel: {
color,
margin: 12,
},
axisLine: {
show: true,
lineStyle: {
color,
},
},
axisTick: {
show: false,
},
minInterval: 1,
splitLine: {
lineStyle: {
type: 'dashed',
color: 'rgba(0,0,0,0.2)',
},
},
max,
});
});
const option = {
color: ['#0E85FF', '#49FF81', '#26D4FF', '#F5D629', '#C97CFF'],
toolbox: {
show: true,
top: 20,
feature: {
dataZoom: {
yAxisIndex: 'none',
},
restore: {},
magicType: { type: ['line', 'bar'] },
saveAsImage: {
show: true,
excludeComponents: ['toolbox'],
pixelRatio: 2,
name: '图片',
},
},
},
tooltip: {
trigger: 'axis',
backgroundColor: bgColor,
borderColor: borderColor,
textStyle: {
color: textColor,
},
confine: true,
},
xAxis: {
data: _xAxis,
type: 'category',
boundaryGap: true,
axisTick: {
show: true,
},
axisLine: {
show: true,
},
z: 10,
axisLabel: {
color: textColor,
},
},
legend: {
type: 'scroll',
top: 0,
left: 'center',
textStyle: {
color: textColor,
},
},
grid: {
top: 60,
left: 30,
right: 0,
bottom: 50,
containLabel: true,
},
yAxis: _yAxis,
dataZoom: [
{
// 这个dataZoom组件,默认控制x轴。
type: 'slider', // 这个 dataZoom 组件是 slider 型 dataZoom 组件
start: 0, // 左边在 10% 的位置。
end: 100, // 右边在 60% 的位置。
},
{
// 这个dataZoom组件,也控制x轴。
type: 'inside', // 这个 dataZoom 组件是 inside 型 dataZoom 组件
start: 0, // 左边在 10% 的位置。
end: 100, // 右边在 60% 的位置。
},
],
series: _series,
};
this.myChart.setOption(option);
// this.drawCharts('uchart-canvas', option);
},
},
mounted() {
this.initEcharts();
},
methods: {
initEcharts() {
this.myChart = echarts.init(this.$refs.echartsRef.$el);
},
getEnumList() {
const { token } = this;
request
.get(`/cloud/device/common/enumList`, {
body: {
token,
className: 'FactorCollectFrequencyEnum',
},
})
.then(res => {
if (res?.result == 0 && res?.data?.length) {
this.collectFrequencyList = res.data || [];
}
});
},
getFactorList() {
const { deviceId, token } = this;
request
.get(`/cloud/device/monitor/factor/list`, {
body: {
token,
deviceId,
collectFrequencies:
'realtime,minute1,minute5,minute10,minute15,minute30,hour,hour2,hour4,hour6,hour12,day,week,month,season,year',
},
})
.then(res => {
if (res?.result == 0 && res?.data?.length) {
this.factorList = res.data || [];
}
});
},
setIndicatorCycleList() {
if (this.type && this.collectFrequencyList?.length) {
const factors = this.radioList?.find(v => v?.key === this.type)?.factors;
const indicatorCycleList = [];
factors?.map(item => {
const obj = this.collectFrequencyList?.find(v => v?.code == item);
if (obj) {
indicatorCycleList.push({
label: obj?.name,
value: obj?.code,
});
}
});
this.indicatorCycleList = indicatorCycleList;
}
},
setCollectFrequency() {
if (
this.factorList?.length &&
this.monitorItemCodes &&
this.indicatorCycleList?.length
) {
let factors = this.factorList.filter(
item => item.monitorItemCode == this.monitorItemCodes,
);
let showList = factors.map(item => item.collectFrequency);
let arr = this.indicatorCycleList.filter(item => showList.includes(item.value));
this.indicatorCycleFilterList = arr;
if (arr.length) {
this.collectFrequency = arr?.[0]?.value;
} else {
this.collectFrequency = undefined;
this.indicatorCycleFilterList = [];
}
}
},
hisDataByTime() {
const { deviceId, token, type, monitorItemCodes, collectFrequency } = this;
const { startTime, endTime } = this.radioList?.find(v => v?.key === type);
if (deviceId && collectFrequency && monitorItemCodes && startTime && endTime) {
request
.get(`/cloud/zhsw-jcss/api/facility/hisDataByTime`, {
body: {
deviceId,
collectFrequency,
token,
startTime,
endTime,
monitorItemCodes,
},
})
.then(res => {
if (res && !res.result && res?.data) {
this.hisData = res?.data;
}
});
}
},
getAlarmLineAreaData(data = [], values) {
let alarmList = [],
alarmValues = [],
alarmMax = 0;
const getName = op => {
let name = '阈值线';
if (op.indexOf('>') !== -1) {
name = '上限';
} else if (op.indexOf('<') !== -1) {
name = '下限';
}
return name;
};
const getMarkLineItem = (name, value) => {
return {
name,
yAxis: value,
label: {
formatter: '{b}:{c}',
position: 'insideEndTop',
color: 'red',
},
};
};
if (data?.length) {
data.map(item => {
const alarmData = item?.ruleParam?.thresholdRuleDetails || [];
alarmData.length &&
alarmData.map(val => {
let { op, params } = val;
if (params?.length === 2) {
alarmList.push(getMarkLineItem('下限', params[0]));
alarmList.push(getMarkLineItem('上限', params[1]));
} else {
alarmList.push(getMarkLineItem(getName(op), params[0]));
}
if (val.params && val.params.length) {
alarmValues.push(...val.params);
}
});
});
}
if (alarmValues.length) {
const l = [...(values || []), ...(alarmValues || [])];
alarmMax = Number((Math.max(...l) * 1.1).toFixed(1));
}
return { alarmList, alarmMax };
},
drawCharts(id, option) {
const ctx = uni.createCanvasContext(id, this);
uChartsInstance[id] = new uCharts({
type: 'line',
enableScroll: true,
color: option.color,
context: ctx,
width: uni.upx2px(630),
height: uni.upx2px(600),
categories: option.xAxis.data,
series: option.series,
xAxis: {
disableGrid: true, //不绘制纵向网格(即默认绘制网格)
scrollShow: true, // 是否显示滚动条,配合拖拽滚动使用(即仅在启用【基本配置】的 enableScroll 时有效)
itemCount: 2, // 单屏数据密度即图表可视区域内显示的X轴数据点数量,仅在启用enableScroll时有效
scrollBackgroundColor: '#F8F8F8', // 滚动条背景颜色
scrollColor: '#19BE6B', //滚动条颜色
},
yAxis: {
showTitle: true,
disableGrid: true,
splitNumber: 1,
data: option.yAxis?.map(v => ({
title: v.name,
max: v.max,
tofix: 0,
titleFontColor: v.nameTextStyle.color,
textAlign: v.nameLocation,
})),
},
legend: { show: false },
// 手势缩放/平移(等价 inside)
extra: {
line: {
moveAble: true, // 手指拖移
zoomAble: true, // 双指缩放
scrollShow: true, // 是否显示滚动条
},
},
});
},
tap(e) {
uChartsInstance[e.target.id].touchLegend(e);
uChartsInstance[e.target.id].showToolTip(e);
},
},
};
</script>
<style lang="scss">
.history-query-wrap {
display: flex;
justify-content: space-between;
align-items: center;
.date-wrap {
display: flex;
height: 50rpx;
background: #f0f2f5;
border-radius: 8rpx;
font-size: 28rpx;
font-weight: 400;
color: rgba(0, 0, 0, 0.45);
align-items: center;
.date-item {
display: flex;
align-items: center;
height: 100%;
padding: 0 20rpx;
&.selected {
color: #ffffff;
background: #1890ff;
box-shadow: 0rpx 1rpx 4rpx 1rpx rgba(0, 0, 0, 0.16);
border-radius: 4rpx 4rpx 4rpx 4rpx;
}
}
}
}
::v-deep {
uni-canvas {
width: 100%;
height: 100%;
}
}
</style>
+7
-3
{
"name": "@vtx/ember",
"version": "3.0.9",
"version": "3.0.10",
"scripts": {

@@ -63,2 +63,3 @@ "start": "vuepress dev docs",

"@types/node": "^20.8.10",
"@vtx/utils": "^1.0.0",
"@vue/runtime-core": "^3.2.45",

@@ -97,5 +98,8 @@ "@vuepress/client": "^2.0.0-rc.0",

"vuepress": "^2.0.0-rc.0",
"@vtx/utils": "^1.0.0"
"@qiun/ucharts": "^2.5.0-20230101"
},
"dependencies": {}
"dependencies": {},
"files": [
"components"
]
}
# all files
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

Sorry, the diff of this file is not supported yet

module.exports = {
root: true,
env: {
browser: true,
es6: true,
},
extends: ['plugin:vue/essential'],
parserOptions: {
parser: 'babel-eslint',
},
plugins: ['vue'],
rules: {
/**
* 禁用 console
*/
'no-console': [2, { allow: ['warn', 'error'] }],
/**
* 禁止重复定义类的成员
*/
'no-dupe-class-members': 'error',
/**
* 禁止对象字面量中出现重复的 key
*/
'no-dupe-keys': 'error',
/**
* 禁止重复导入模块
*/
'no-duplicate-imports': 'error',
/**
* 禁止出现空语句块
*/
'no-empty': 'error',
/**
* 禁止解构赋值中出现空 {} 或 []
*/
'no-empty-pattern': 'error',
/**
* 禁止不必要的布尔转换
*/
'no-extra-boolean-cast': 'error',
/**
* switch 的 case 内必须有 break, return 或 throw,空的 case 除外
*/
'no-fallthrough': 'error',
/**
* 禁止使用 var
*/
'no-var': 'error',
'vue/valid-template-root': 'off',
'vue/no-v-html': 'off',
'vue/no-v-text-v-html-on-component': 'off'
},
};
pages:
cache:
paths:
- node_modules/
script:
- yarn install --registry=https://registry.npm.taobao.org --pure-lockfile
- yarn build
- yarn example:build
- mv doc-dist public
- mv dist/build/h5 public/example
artifacts:
paths:
- public
tags:
- node-docker
only:
- master
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
"version": "0.0",
"configurations": [{
"default" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

Sorry, the diff of this file is not supported yet

module.exports = {
// 一行最多 100 字符
printWidth: 100,
// 使用 4 个空格缩进
tabWidth: 4,
// 使用单引号
singleQuote: true,
// 末尾需要逗号
trailingComma: 'all',
proseWrap: 'never',
// 换行符使用 lf
endOfLine: 'lf',
};
{
"extends": [
"stylelint-config-standard",
"stylelint-config-rational-order",
"stylelint-config-prettier"
],
"plugins": ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],
"rules": {
"declaration-empty-line-before": null,
"at-rule-no-unknown": null,
"selector-type-no-unknown": [
true,
{
"ignoreTypes": ["/^components/"]
}
],
"unit-no-unknown": [true, { "ignoreUnits": ["rpx"] }]
}
}
{
"files.eol": "\n",
"editor.tabSize": 4,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"git.ignoreLimitWarning": true,
"i18n-ally.localesPaths": [
"src/locales"
],
"i18n-ally.enabledParsers": [
"json"
],
// 语言
// key 风格
"i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true,
"i18n-ally.displayLanguage": "zh-CN",
// 自动提取
"i18n-ally.extract.autoDetect": true,
"i18n-ally.annotationInPlace": false,
// 1. 文件名 → locale 映射(告诉插件“zh-CN.json”就是 locale: zh-CN)
"i18n-ally.localeCountryMap": {
"zh-CN": "zh-CN",
"en": "en"
},
/* ===== 新增 key 时同时写所有语言包 ===== */
"i18n-ally.extract.targetPickingStrategy": "none",
/* ===== 禁止再建 _locales 目录 ===== */
"i18n-ally.pathMatcher": "{locale}.{ext}",
/* ===== 缺失译文用中文自动填充 ===== */
"i18n-ally.copyFrom": "zh-CN",
/* ===== JSON 格式化 ===== */
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
module.exports = {
plugins: [
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator',
],
};
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'chore',
'build',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'style',
'test',
'release',
],
],
},
};
## 目录约定
```
├── components # 组件
│   └── v-text
│   └── v-text.vue
├── docs # 在线文档
│   └── components
│   └── text.md # 组件对应的文档 v-text.vue 对应 text.md
├── src # 组件示例
├── CHANGELOG.md # CHANGELOG 根据指令自动生成
├── README.md
└── vue.config.js # example 配置
```
## 组件开发约定
### 组件写在 `components` 目录下
所有的组件写在 `components` 目录下,并符合 `components/组件名称/组件名称.vue` 目录结构。可参考[easycom 组件规范](https://uniapp.dcloud.io/component/README?id=easycom%e7%bb%84%e4%bb%b6%e8%a7%84%e8%8c%83)
### 需兼容小程序和 H5
- [小程序不支持列表](https://uniapp.dcloud.io/vue-components?id=%e5%b0%8f%e7%a8%8b%e5%ba%8f%e4%b8%8d%e6%94%af%e6%8c%81%e5%88%97%e8%a1%a8)
- [命名限制](https://uniapp.dcloud.io/vue-components?id=%e5%91%bd%e5%90%8d%e9%99%90%e5%88%b6)
### 组件命名必须以 `v-` 开头
所有的组件命名必须以 `v-` 开头,举例我们需开发一个树形控件,我们在 `components` 目录下新建 `v-tree/v-tree.vue`
## Doc 在线文档约定
在线文档通过 [VuePress](https://vuepress.vuejs.org/zh/) 搭建。每个组件都必须提供一个使用文档(md 文件),目录为 `docs/components/*` 下
文档结构大致如下,标题 -> 代码示例 -> API -> example
```
# 组件标题
组件说明
## 代码示例
### 小标题
// 示例代码,代码块
## API
表格
```
## 提交流程规范
代码提交前必须执行`yarn lint`,校验通过过才可提交。提交信息必须遵循 [Angular 提交信息规范](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit) 。版本更新和 生成 `CHANGELOG.md` 通过`yarn release`指令执行
## scripts 说明
```
# 启动在线文档服务
$ yarn start
# 打包在线文档
$ yarn build
# 组件库示例服务
$ yarn example:dev
# 打包组件库示例
$ yarn example:build
# 校验
$ yarn lint
# 版本发布
$ yarn release
```
## 添加一个 `v-text` 组件
1. 在 `components` 目录下新建 `v-text/v-text.vue`
```vue
<template>
<view></view>
</template>
```
2. 新增示例界面,在 src/pages 目录下新建 `v-text/index.vue`
3. 在 src/pages.json 对刚新增的示例页面进行配置
```json
{
"pages": [
{
"path": "pages/v-text/index",
"style": {
"navigationStyle": "custom"
}
}
],
...
}
```
4. 新增 `v-text` 文档,在 `docs/components` 目录下新建 `text.md`
```
# Text 文字
组件说明
## 代码示例
### 小标题
// 示例代码,代码块
## API
表格
```
5. 配置文档让其显示,在 `docs/.vuepress/config.js` 内添加配置
```js
module.exports = (options, context) => ({
themeConfig: {
sidebar: {
'/component': [
'/components/installation',
{
title: '组件',
collapsable: false,
sidebarDepth: 0,
children: ['/components/text'],
},
],
},
},
});
```

Sorry, the diff of this file is not supported yet

<template>
<div class="demo-page">
<div class="page-content">
<iframe :src="src" frameborder="0" />
</div>
</div>
</template>
<style lang="less">
.demo-page {
position: fixed;
top: 80px;
right: 20px;
width: 310px;
height: 605.909091px;
box-sizing: border-box;
background-image: url('./iphoneX.png');
background-repeat: no-repeat;
background-size: 100%;
padding: 64px 21px 22px 23px;
}
@media (min-width: 1600px) {
.demo-page {
right: 20px;
width: 330px;
height: 645px;
}
}
.page-content {
box-sizing: border-box;
width: 100%;
height: 100%;
iframe {
height: 100%;
width: 100%;
}
}
</style>
<script>
export default {
props: {
src: String,
},
data() {
return {};
},
methods: {},
};
</script>
import { defaultTheme } from 'vuepress';
import { getDirname, path } from '@vuepress/utils';
import { registerComponentsPlugin } from '@vuepress/plugin-register-components';
const __dirname = getDirname(import.meta.url);
export default {
lang: 'zh-CN',
title: '@vtx/ember', //系统标题
description: '加载失败', // 系统logo加载失败显示
base: '/ember-plus/',
theme: defaultTheme({
repoLabel: 'GitHub', // github
navbar: [
{ text: '3.x组件', link: '/components/installation' },
{
text: '1.x组件',
link: 'http://front-end.pages.cloudhw.cn:8000/ember/components/installation.html',
},
{
text: '2.x组件',
link: 'http://yanshi.huanwei.envcloud.com.cn:8003/ember-next/components/installation.html',
},
], // 头部菜单
sidebar: {
'/component': [
'/components/installation',
{
title: '3.x组件',
collapsable: false,
sidebarDepth: 0,
children: [
'/components/date-picker',
'/components/image',
'/components/multiple-select',
'/components/select',
'/components/tree-select',
'/components/upload',
'/components/dropdown',
'/components/virtual-select',
],
},
],
},
editLink: false,
editLinkText: '编辑此页',
editLinkPattern: '',
sidebarDepth: 2,
lastUpdated: true,
lastUpdatedText: '更新时间',
contributors: false,
contributorsText: '贡献者',
notFound: [
'这里什么都没有',
'我们怎么到这来了?',
'这是一个 404 页面',
'看起来我们进入了错误的链接',
],
backToHome: '返回首页',
// a11y
openInNewWindow: '在新窗口打开',
toggleDarkMode: '切换夜间模式',
toggleSidebar: '切换侧边栏',
}),
plugins: [
registerComponentsPlugin({
componentsDir: path.resolve(__dirname, './components'),
}),
],
};
---
title: DatePicker 日期选择框
---
# DatePicker 日期选择框
选择日期的控件,基于 [uView Picker 选择器](https://www.uviewui.com/components/picker.html) 二次封装。
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/pages/v-date-picker/index"/>
## 代码演示
### 基础使用
```vue
<template>
<view>
//基本使用
<v-date-picker v-model="date" maxDate="2022"></v-date-picker>
//年
<v-date-picker type="year" v-model="date1"></v-date-picker>
//月
<v-date-picker type="month" v-model="date2"></v-date-picker>
//日
<v-date-picker type="date" v-model="date3"></v-date-picker>
//年月日时分秒
<v-date-picker type="datetime" v-model="date4"></v-date-picker>
//时分秒
<v-date-picker type="time" v-model="date5"></v-date-picker>
//时分
<v-date-picker type="hourMinute" v-model="date6"></v-date-picker>
//重置功能
<u-button @click="reset">重置</u-button>
</view>
</template>
<script>
import dayjs from 'dayjs';
export default {
watch: {
date(val) {
console.log('date', val);
},
date1(val) {
console.log('date1', val);
},
date2(val) {
console.log('date2', val);
},
date3(val) {
console.log('date3', val);
},
date4(val) {
console.log('date4', val);
},
date5(val) {
console.log('date5', val);
},
date6(val) {
console.log('date6', val);
},
},
data() {
return {
date: dayjs().format('YYYY-MM-DD'),
date1: dayjs().format('YYYY'),
date2: dayjs().format('YYYY-MM'),
date3: dayjs().format('YYYY-MM-DD'),
date4: dayjs().format('YYYY-MM-DD HH:mm:ss'),
date5: dayjs().format('HH:mm:ss'),
date6: dayjs().format('HH:mm'),
};
},
methods: {
reset() {
this.date1 = '2022';
this.date2 = '2022-01';
this.date3 = '2022-01-01';
this.date4 = '2022-01-01 23:59:59';
this.date5 = '23:59:59';
this.date6 = '23:59';
},
},
};
</script>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
| --- | --- | --- | --- | --- |
| v-model | 指定当前选中的日期 | String | - | - |
| type | 选择器类型 | String | date | date / month / year / datetime / time |
| disabled | 是否禁用选择 | Boolean | false | - |
| placeholder | 文本 placeholder 显示值 | string | - | - |
| border | 文本是否有边框 | Boolean | false | - |
| input | 是否展示 input | Boolean | true | - |
| maxDate | 可选的最大时间 | Number | - | - |
| minDate | 可选的最小时间 | Number | - | - |
| minHour | 可选的最小小时,仅 mode=time 有效 | Number | - | - |
| maxHour | 可选的最大小时,仅 mode=time 有效 | Number | - | - |
| minMinute | 可选的最小分钟,仅 mode=time 有效 | Number | - | - |
| maxMinute | 可选的最大分钟,仅 mode=time 有效 | Number | - | - |
| minSecond | 可选的最小秒钟,仅 mode=time 有效 | Number | - | - |
| maxSecond | 可选的最大秒钟,仅 mode=time 有效 | Number | - | - |
### Events
| 事件名 | 说明 | 回调参数 | 默认值 |
| ------ | -------------------------------- | -------------------- | ------------ |
| change | 时间改变时触发,返回当前选择的值 | Function(dateString) | 当前选中的值 |
---
title: Dropdown 下拉菜单
---
# Dropdown 下拉菜单
基于 [u-dropdown 下拉菜单](https://v1.uviewui.com/components/dropdown.html)封装 。
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/pages/v-dropdown/index"/>
## 代码演示
### 基础使用
```vue
<template>
<view>
<view class="u-m-p-50">
<view class="u-demo-area u-flex u-row-center">
<v-dropdown
:close-on-click-mask="mask"
ref="uDropdown"
:activeColor="activeColor"
:borderBottom="borderBottom"
>
<v-dropdown-item
@change="change"
v-model="value1"
title="距离"
:options="options1"
:titleSize="14"
></v-dropdown-item>
<v-dropdown-item
@change="change"
v-model="value2"
title="温度"
:options="options2"
></v-dropdown-item>
<v-dropdown-item title="属性">
<view class="slot-content">
<view class="item-box">
<view
class="item"
:class="[item.active ? 'active' : '']"
@tap="tagClick(index)"
v-for="(item, index) in list"
:key="index"
>
{{ item.label }}
</view>
</view>
<u-button type="primary" @click="closeDropdown">确定</u-button>
</view>
</v-dropdown-item>
</v-dropdown>
</view>
</view>
<view class="u-config-wrap">
<view class="u-config-title u-border-bottom"> 参数配置 </view>
<view class="u-config-item">
<view class="u-item-title">下边框</view>
<u-subsection
:current="curNow"
:list="['有', '无']"
@change="borderChange"
></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">激活颜色</view>
<u-subsection
:current="curNow2"
:list="['#2979ff', '#ff9900', '#19be6b']"
@change="activeColorChange"
></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">遮罩是否可点击</view>
<u-subsection
:current="curNow3"
:list="['是', '否']"
@change="maskChange"
></u-subsection>
</view>
</view>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
return {
curNow: 1,
curNow2: 0,
curNow3: 0,
value1: '',
value2: '2',
mask: true,
options1: [
{
label: '默认排序',
value: 1,
},
{
label: '距离优先',
value: 2,
},
{
label: '价格优先',
value: 3,
},
],
options2: [
{
label: '去冰',
value: 1,
},
{
label: '加冰',
value: 2,
},
{
label: '正常温',
value: 3,
},
{
label: '加热',
value: 4,
},
{
label: '极寒风暴',
value: 5,
},
],
list: [
{
label: '琪花瑶草',
active: true,
},
{
label: '清词丽句',
active: false,
},
{
label: '宛转蛾眉',
active: false,
},
{
label: '煦色韶光',
active: false,
},
{
label: '鱼沉雁落',
active: false,
},
{
label: '章台杨柳',
active: false,
},
{
label: '霞光万道',
active: false,
},
],
borderBottom: false,
activeColor: '#2979ff',
};
},
methods: {
borderChange(index) {
this.curNow = index;
this.borderBottom = !index;
},
activeColorChange(index) {
this.curNow2 = index;
this.activeColor = ['#2979ff', '#ff9900', '#19be6b'][index];
},
change(index) {
this.$u.toast(`点击了第${index}项`);
},
closeDropdown() {
this.$refs.uDropdown.close();
},
tagClick(index) {
this.list[index].active = !this.list[index].active;
},
maskChange(index) {
this.curNow3 = index;
this.mask = !index;
},
},
};
</script>
<style scoped lang="scss">
.u-config-wrap {
padding: 40rpx;
.u-config-title {
text-align: center;
font-size: 16px;
font-weight: bold;
margin: 10px 0;
padding: 5px;
}
.u-item-title {
padding: 12rpx;
}
.u-config-item {
padding: 24rpx 0;
}
}
.slot-content {
background-color: #ffffff;
padding: 24rpx;
.item-box {
margin-bottom: 50rpx;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item {
border: 1px solid #2979ff;
color: #2979ff;
padding: 8rpx 40rpx;
border-radius: 100rpx;
margin-top: 30rpx;
font-size: 28rpx;
}
.active {
color: #ffffff;
background-color: #2979ff;
}
}
}
</style>
```
## API
详细 API 请参考 [Dropdown 下拉菜单](https://v1.uviewui.com/components/dropdown.html),使用上无任何差异
---
title: Image 图片
---
# Image 图片
基于 [uView Image 图片](https://www.uviewui.com/components/image.html) 内置点击预览,预览方式 API 为 [uni.previewImage(OBJECT)](https://uniapp.dcloud.io/api/media/image?id=unipreviewimageobject)。
结合 `v-image-previewer` 使用支持多张图片预览。
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/pages/v-image/index"/>
## 代码演示
### 基础使用
```vue
<template>
<view>
//基本使用
<v-image width="100%" height="300rpx" :src="src"></v-image>
//多张图片预览
<v-image-previewer>
<v-image width="100%" height="300rpx" :src="src" style="margin-bottom: 8px"></v-image>
<v-image
width="100%"
height="300rpx"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/auto-orient,1/resize,p_10/quality,q_10"
></v-image>
</v-image-previewer>
</view>
</template>
<script>
export default {
data() {
return {
src:
'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=273034283,2961511002&fm=26&gp=0.jpg',
};
},
};
</script>
```
## API
详细 API 请参考 [uView Image 图片](https://www.uviewui.com/components/image.html),使用上无任何差异
---
title: 快速上手
sidebarDepth: 0
---
# 快速上手
::: warning
## 使用注意
因为本组件库很多都是基于 [uView](https://www.uviewui.com/components/intro.html) 做的一个二次封装,所以在使用该组件时需先引入 uView
1. main.js 引入 uView 库
```js
// main.js
import uviewPlus from 'uview-plus';
app.use(uviewPlus);
```
2. `App.vue` 引入基础样式(注意 style 标签需声明 scss 属性支持)
```vue
/* App.vue */
<style lang="scss">
@import 'uview-plus/index.scss';
</style>
```
3. `uni.scss` 引入全局 scss 变量文件
```scss
/* uni.scss */
@import 'uview-plus/theme.scss';
```
4. `pages.json` 配置 easycom 规则(按需引入)
```json
// pages.json
{
"easycom": {
"^u-(.*)": "uview-plus/components/u-$1/u-$1.vue"
},
// 此为本身已有的内容
"pages": [
// ......
]
}
```
:::
## Yarn 安装
```shell
yarn add @vtx/ember -S
```
## 使用方法
`pages.json` 配置 easycom 规则(按需引入)
```json
// pages.json
// pages.json
{
"easycom": {
// 注意一定要放在custom里,否则无效,https://ask.dcloud.net.cn/question/131175
"custom": {
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
// 此为本身已有的内容
"pages": [
// ......
]
}
```
配置 easycom 规则后,自动按需引入,无需 `import` 组件,直接引用即可。
```vue
<template>
<v-multiple-select></v-multiple-select>
</template>
```
<!-- <Mobile src="http://front-end.pages.cloudhw.cn:8000/ember/example/#/"/> -->
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/"/>
---
title: MultipleSelect 下拉多选
---
# MultipleSelect 下拉多选
此选择器用于单列多选场景,常用于表单中使用。
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/pages/v-multiple-select/index"/>
## 代码演示
### 基础使用
```vue
<template>
<view>
//基本使用
<v-multiple-select :data="list" v-model="checkedKeys"></v-multiple-select>
<v-multiple-select :data="list" v-model="checkedKeys1"></v-multiple-select>
<v-multiple-select
:data="list"
v-model="checkedKeys2"
border="surround"
></v-multiple-select>
//自定义确认颜色和字体
<v-multiple-select :data="[]" confirmText="提交" confirmColor="#18b566"></v-multiple-select>
//重置功能
<u-button @click="reset">重置</u-button>
</view>
</template>
<script>
export default {
data() {
const data = [];
for (let i = 10; i < 36; i++) {
data.push({
key: i.toString(36) + i,
value: i.toString(36) + i,
});
}
return {
list: data,
checkedKeys: [],
checkedKeys1: ['a10'],
checkedKeys2: [],
};
},
methods: {
reset() {
this.checkedKeys1 = ['a10'];
},
},
};
</script>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| ----------------- | -------------------------------------- | ------- | -------------- |
| v-model | 指定当前选中的条目 | Array | [] |
| data | 下拉列表数据,要求数据格式{key, value} | Array | [] |
| disabled | 是否禁用选择 | Boolean | - |
| placeholder | 文本 placeholder 显示值 | string | '请选择' |
| border | 文本是否有边框 | Boolean | false |
| searchPlaceholder | 搜索框 placeholder 显示值 | string | '请输入关键字' |
| confirmText | 弹出层确认提示文字 | string | '确认' |
| confirmColor | 弹出层确认提示文字颜色 | string | '#2979ff' |
| input | 是否显示 input 框 | Boolean | true |
### Events
| 事件名 | 说明 | 回调参数 | 默认值 |
| ------ | ------------------------------ | --------------------- | ------------ |
| hide | 弹出层隐藏时的回调方法 | Function() |
| change | 点击确定按钮,返回当前选择的值 | Function(checkedKeys) | 当前选中的值 |
---
title: Select 下拉
---
# Select 下拉
基于 [uView Select 选择器](https://www.uviewui.com/components/select.html) 二次封装,目前只支持单列,常用于 form 表单使用。
为什么要做封装,在 form 表单中如果要使用 `u-select`,我们需要这样去写,我们需提供一个状态 `show` 去控制 select 选择器的显隐,选择某一列后 input 的值也需要同步变更展示。当一个 form 表单内存在多个 select 组件时代码就会显得很混乱。
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/pages/v-select/index"/>
## 代码演示
### 基础使用
```vue
<template>
<view>
//基本使用
<v-select :data="list" v-model="checkedKeys1"></v-select>
<v-select :data="list" v-model="checkedKeys"></v-select>
<v-select
:data="list"
v-model="checkedKeys1"
border="surround"
:clear="clear"
:clearFlag="true"
></v-select>
<v-select :data="list" v-model="checkedKeys" border="surround"></v-select>
//重置功能
<u-button @click="reset">重置</u-button>
</view>
</template>
<script>
export default {
data() {
const data = [];
for (let i = 10; i < 36; i++) {
data.push({
key: i.toString(36) + i + Math.random(),
value: i.toString(36) + i,
});
}
return {
list: data,
checkedKeys: 'k20',
checkedKeys1: '',
test: '123',
};
},
methods: {
reset() {
this.checkedKeys = 'k20';
},
clear() {
console.log('111clear');
},
},
};
</script>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| ----------------- | -------------------------------------- | ------- | -------------- |
| v-model | 指定当前选中的条目 | String | '' |
| data | 下拉列表数据,要求数据格式{key, value} | Array | [] |
| disabled | 是否禁用选择 | Boolean | - |
| placeholder | 文本 placeholder 显示值 | string | '请选择' |
| border | 文本是否有边框 | Boolean | false |
| searchPlaceholder | 搜索文本 placeholder 显示值 | string | '请输入关键字' |
| input | 是否显示 input 框 | Boolean | true |
| clearFlag | 是否显示清除按钮 | Boolean | false |
### Events
| 事件名 | 说明 | 回调参数 | 默认值 |
| ------ | ------------------------------ | -------------------- | ---------------------- |
| change | 点击确定按钮,返回当前选择的值 | Function(value,show) | 当前选中的值和显示隐藏 |
| hide | 弹出层隐藏时的回调方法 | Function() | - |
| clear | 清除按钮回调方法 | Function(value) | false |
---
title: TreeSelect 下拉树
---
# TreeSelect 下拉树
树型选择控件,常用于表单中。基于 [next-tree](https://ext.dcloud.net.cn/plugin?id=12265) 封装
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/pages/v-tree-select/index"/>
## 代码演示
### 基础使用
```vue
<template>
<view>
//基本使用
<v-tree-select
v-model="checkedKey"
:treeData="treeData"
:node-key="'id'"
:node-label="'label'"
></v-tree-select>
//多选模式
<v-tree-select
v-model="checkedKey1"
:treeData="treeData"
mode="multiple"
:node-key="'id'"
:node-label="'label'"
@change="handleChange"
:checkStrictly="true"
></v-tree-select>
</view>
</template>
<script>
export default {
data() {
return {
treeData: [],
checkedKey: [1],
checkedKey1: [1, 11, 111],
};
},
onLoad() {
// 模拟异步请求
setTimeout(() => {
this.treeData = [
{
id: 1,
label: '一级 1',
children: [
{
id: 11,
label: '二级 1-1',
children: [
{
id: 111,
label: '三级 1-1-1',
},
],
},
],
},
{
id: 2,
label: '一级 2',
children: [
{
id: 21,
label: '二级 2-1',
children: [
{
id: 211,
label: '三级 2-1-1',
},
],
},
{
id: 22,
label: '二级 2-2',
children: [
{
id: 221,
label: '三级 2-2-1',
},
],
},
],
},
{
id: 3,
label: '一级 3',
children: [
{
id: 31,
label: '二级 3-1',
children: [
{
id: 311,
label: '三级 3-1-1',
},
],
},
{
id: 32,
label: '二级 3-2',
children: [
{
id: 321,
label: '三级 3-2-1',
},
],
},
],
},
];
}, 200);
},
methods: {
handleChange(checkedKeys) {
console.log(checkedKeys);
},
},
};
</script>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| v-model | 指定当前选中的条目 | Array | [] |
| treeData | 下拉树数据 https://ext.dcloud.net.cn/plugin?id=12265 | Array | [] |
| disabled | 是否禁用选择 | Boolean | false |
| placeholder | 文本 placeholder 显示值 | string | '请选择' |
| border | 文本是否有边框 | Boolean | false |
| searchPlaceholder | 搜索框 placeholder 显示值 | string | '请输入关键字' |
| confirmText | 弹出层确认提示文字 | string | '确认' |
| confirmColor | 弹出层确认提示文字颜色 | string | '#2979ff' |
| mode | 多选('multiple')或单选('single')模式 | String | 'single' |
| nodeKey | 每个树节点的 id | String | 'id' |
| nodeLabel | 每个树节点的名称 | String | 'label' |
| input | 是否显示 input 框 | Boolean | true |
| checkStrictly | 父子不互相关联 | Boolean | false |
| disabledKey | 禁用节点绑定属性 | String | 'disabled' |
### Events
| 事件名 | 说明 | 回调参数 | 默认值 |
| --- | --- | --- | --- |
| change | 点击确定按钮,返回当前选择的值 | Function(checkedKeys, object) | 当前选中的值和该值所在对象 |
---
title: Upload 上传
---
# Upload 上传
该组件用于上传图片场景,基于[uView Upload](https://www.uviewui.com/components/upload.html)封装。
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/pages/v-upload/index"/>
## 代码演示
### 基础使用
默认支持 `v-model`,内容格式为`[{id: 'f4f29d6892be4be796207a27a746e839', name: '2.jpg', type: 'image/jpeg'}, ...]`
```vue
<template>
<view>
//基本使用
<v-upload v-model="photos" @change="handleChange"></v-upload>
//只读
<v-upload v-model="photos" readonly></v-upload>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
return {
// [{id: 'f4f29d6892be4be796207a27a746e839', name: '2.jpg', type: 'image/jpeg'}]
photos: [],
};
},
methods: {
handleChange() {
// TODO
},
},
};
</script>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| v-model | 文件列表 | Array[file],详见下方说明 | [] |
| action | 上传后端地址 | String | '/cloudFile/common/uploadFile' |
| downloadUrl | 下载后端地址 | String | '/cloudFile/common/downloadFile?id=' |
| maxCount | 最大上传数量 | String \| Number | 6 |
| readonly | 是否只读 | Boolean | false |
| fileList | 显示已上传的文件列表 | Array[file] | [] |
更多 `Props` 参考 [uView Upload 上传](https://www.uviewui.com/components/upload.html)。
`file`格式:
`id` 图片服务器 id,`name`图片名称,`type`文件类型
```
{id: '', name: '', type: ''}
```
### Events
| 事件名 | 说明 | 回调参数 | 默认值 |
| --- | --- | --- | --- |
| change | 文件列表变化时触发 | Function(fileList) | 当前上传的文件 |
| on-remove | 移除图片时触发 | (index, lists, name),name 为通过 props 传递的 index 参数 | - |
| on-success | 图片上传成功时触发 | (data, index, lists, name),data 为服务器返回的数据,name 为通过 props 传递的 index 参数 | - |
---
title: VirtualSelect 虚拟下拉列表
---
# VirtualSelect 虚拟下拉列表
<Mobile src="http://yanshi.huanwei.envcloud.com.cn:8003/ember-plus/example/#/pages/v-virtual-select/index"/>
## 代码演示
### 基础使用
```vue
<template>
<view>
//基本使用
<v-virtual-select
:requestOption="{
url: '/cloud/advise/web/base/shop/list',
method: 'POST',
}"
:requestParams="{
order: 'asc,asc',
sort: 'orgName,manageUnitName',
}"
:setValueLabel="{
value: 'id',
label: 'name',
}"
:setPageRows="{
page: 'page',
rows: 'rows',
searchText: 'nameLike',
}"
v-model="checkedKeys"
></v-virtual-select>
// 多选
<v-virtual-select
:requestOption="{
url: '/cloud/advise/web/base/shop/list',
method: 'POST',
}"
:requestParams="{
order: 'asc,asc',
sort: 'orgName,manageUnitName',
}"
:setValueLabel="{
value: 'id',
label: 'name',
}"
:setPageRows="{
page: 'page',
rows: 'rows',
searchText: 'nameLike',
}"
v-model="checkedKeys1"
mode="multiple"
></v-virtual-select>
//重置功能
<u-button @click="reset">重置</u-button>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
const data = [];
for (let i = 10; i < 10000; i++) {
const value = i.toString(36) + i;
data.push({
value,
label: value,
});
}
return {
list: data,
checkedKeys: [],
checkedKeys1: [{ value: 'a10', label: 'a10' }],
};
},
methods: {
reset() {
this.checkedKeys = [{ value: 'a10', label: 'a10' }];
this.checkedKeys1 = [];
},
},
};
</script>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| v-model | 指定当前选中的值 | Array | [] |
| requestOption | 请求方法和地址 | Object({url:'',method: ''}) | - |
| requestParams | 请求参数 | Object | - |
| setValueLabel | 返回展示名称的字段 | Object({label:'',value: ''}) | Object({label:'id',value: 'name'}) |
| setPageRows | 请求的页面和行数 | Object({page:'',rows: ''}) | Object({page:'page',rows: 'rows'}) |
| mode | 多选或单选('multiple' 多选 ,'' 单选) | String | - |
| title | 顶部标题 | String | - |
| disabled | 是否禁用选择 | Boolean | false |
| border | 文本是否有边框 | String | 'border' |
| searchPlaceholder | 选择器搜索框 placeholder 显示值 | String | '请输入关键字' |
| cancelColor | "取消"按钮的颜色 | String | '#606266' |
| confirmColor | "确定"按钮的颜色 | String | '#2979ff' |
| zIndex | 弹出的 z-index 值 | String \| Number | 0 |
| maskCloseAble | 是否允许通过点击遮罩关闭 | Boolean | true |
| cancelText | 取消按钮的文字 | String | '取消' |
| confirmText | 确认按钮的文字 | String | '确认' |
| input | 是否显示 input | Boolean | true |
### Events
| 事件名 | 说明 | 回调参数 | 默认值 |
| ------ | ---------------- | --------------------- | ------------ |
| change | 返回当前选择的值 | Function(checkedKeys) | 当前选中的值 |
---
home: true
heroText: '@vtx/ember'
tagline: 伏泰 Uni-app 组件库
actionText: 快速入门 →
actionLink: /components/installation
---
## 使用 Yarn 安装
```shell
yarn add @vtx/ember -S
```
## 使用方法
`pages.json` 配置 easycom 规则(按需引入)
```json
// pages.json
{
"easycom": {
"^v-(.*)": "@vtx/ember/components/v-$1/v-$1.vue"
},
// 此为本身已有的内容
"pages": [
// ......
]
}
```
配置 easycom 规则后,自动按需引入,无需 `import` 组件,直接引用即可。
```vue
<template>
<v-multiple-select :data="[]"></v-multiple-select>
</template>
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
const path = require('path')
module.exports = {
parser: require('postcss-comment'),
plugins: [
require('postcss-import')({
resolve (id, basedir, importOptions) {
if (id.startsWith('~@/')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3))
} else if (id.startsWith('@/')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2))
} else if (id.startsWith('/') && !id.startsWith('//')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1))
}
return id
}
}),
require('autoprefixer')({
remove: process.env.UNI_PLATFORM !== 'h5'
}),
require('@dcloudio/vue-cli-plugin-uni/packages/postcss')
]
}
/// <reference types='@dcloudio/types' />
import 'vue'
declare module '@vue/runtime-core' {
type Hooks = App.AppInstance & Page.PageInstance;
interface ComponentCustomOptions extends Hooks {
}
}
<script>
export default {
onLaunch: function () {
console.log('App Launch')
},
onShow: function () {
console.log('App Show')
},
onHide: function () {
console.log('App Hide')
},
}
</script>
<style lang="scss">
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
@import "uview-plus/index.scss";
</style>
<template>
<div class="demo-block">
<div class="demo-block__title">{{ title }}</div>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
title: String,
},
};
</script>
<style lang="scss" scoped>
.demo-block {
padding: 0 16px;
&__title {
margin: 0;
padding: 24px 0 16px;
color: rgba(69, 90, 100, 0.6);
font-weight: normal;
font-size: 14px;
line-height: 16px;
}
}
</style>
{
"bu-xian-shi-input-shu-ru-kuang-chu-fa": "Hide input box trigger",
"can-shu-pei-zhi": "Parameter configuration",
"datepicker-ri-qi-xuan-ze-kuang": "DatePicker",
"dian-ji-ci-chu-chu-fa": "Click here to trigger",
"dian-ji-ci-chu-chu-fa-dan-xuan": "Click here to trigger (single)",
"dian-ji-ci-chu-chu-fa-duo-xuan": "Click here to trigger (multiple)",
"dian-ji-le-di-index-xiang": "Clicked item {0}",
"dropdown": "Dropdown",
"dropdown-xia-la-cai-dan": "Dropdown menu",
"duo-xuan": "Multiple",
"duo-xuan-mo-shi-fu-zi-bu-hu-xiang-guan-lian": "Multiple mode (parent & child no independent)",
"duo-xuan-mo-shi-fu-zi-hu-xiang-guan-lian": "Multiple mode (parent & child independent)",
"duo-zhang-tu-pian-yu-lan": "Multi-image preview",
"er-ji-11": "Level 2 1-1",
"er-ji-21": "Level 2 2-1",
"er-ji-22": "Level 2 2-2",
"er-ji-31": "Level 2 3-1",
"er-ji-32": "Level 2 3-2",
"fu-tai-qian-duan-uniapp-zu-jian-ku": "Futai Frontend uni-app component library",
"image": "Image",
"image-tu-pian": "Image",
"ji-ben-shi-yong": "Basic usage",
"ji-han-feng-bao": "Arctic Storm",
"ji-huo-yan-se": "Active color",
"jia-bing": "Extra ice",
"jia-ge-you-xian": "Price first",
"jia-re": "Heat up",
"jie-guo": "Result",
"ju-li": "Distance",
"ju-li-you-xian": "Distance first",
"mo-ren-pai-xu": "Default sort",
"multipleselect-xia-la-duo-xuan": "MultipleSelect",
"nian": "Year",
"nian-yue-ri-shi-fen-miao": "Year-Month-Day-Hour-Minute-Second",
"qi-hua-yao-cao": "Rare blossoms and jade-like grass",
"qing-ci-li-ju": "Elegant words and beautiful phrases",
"qu-bing": "No ice",
"que-ding": "OK",
"ri": "Day",
"ri-qi-xuan-ze-kuang": "DatePicker",
"san-ji-111": "Level 3 1-1-1",
"san-ji-211": "Level 3 2-1-1",
"san-ji-221": "Level 3 2-2-1",
"san-ji-311": "Level 3 3-1-1",
"san-ji-321": "Level 3 3-2-1",
"select": "Select",
"select-xia-la": "Select",
"shang-chuan": "Upload",
"shi-fen-miao": "Hour-Minute-Second",
"shu-xing": "Attribute",
"treeselect-xia-la-shu": "TreeSelect",
"tu-pian": "Image",
"upload": "Upload",
"upload-shang-chuan": "Upload",
"virtualselect-xu-ni-xia-la-lie-biao": "VirtualSelect",
"wan-zhuanemei": "Graceful eyebrows",
"wen-du": "Temperature",
"xia-bian-kuang": "Bottom border",
"xia-guang-wan-dao": "Rays of dawn",
"xia-la": "Select",
"xia-la-cai-dan": "Dropdown menu",
"xia-la-duo-xuan": "MultipleSelect",
"xia-la-shu": "TreeSelect",
"xu-ni-xia-la-lie-biao": "VirtualSelect",
"xu-se-shao-guang": "Warm spring light",
"yi-ji-1": "Level 1 1",
"yi-ji-2": "Level 1 2",
"yi-ji-3": "Level 1 3",
"yu-chen-yan-la": "Fish sink and geese alight (devastating beauty)",
"yue": "Month",
"zhang-tai-yang-liu": "Zhangtai willows",
"zhe-zhao-shi-fou-ke-dian-ji": "Mask closable",
"zheng-chang-wen": "Normal temperature",
"zhong-zhi": "Reset",
"zhong-zhi-gong-neng": "Reset function",
"zi-ding-yi-que-ren-yan-se-he-zi-ti": "Custom confirm color & font",
"zu-jian": "Component",
"zan-wu-shu-ju": "No Data"
}
import zhHans from './zh-CN.json'
import en from './en.json'
import { getUrlParam } from '@vtx/utils'
import { createI18n } from 'vue-i18n'
const getLocale = () => {
if (getUrlParam('locale') === 'zh-CN') {
return 'zh-Hans'
} else if (getUrlParam('locale') === 'en-US') {
return 'en'
} else if (uni.getStorageSync('locale') === 'zh-CN') {
return 'zh-Hans'
} else if (uni.getStorageSync('locale') === 'en-US') {
return 'en'
} else if (uni.getStorageSync('UNI_LOCALE') === 'zh-Hans') {
return 'zh-Hans'
} else if (uni.getStorageSync('UNI_LOCALE') === 'en') {
return 'en'
}
else {
return 'zh-Hans'
}
}
const locale = getLocale();
if (locale === 'zh-Hans') {
uni.setStorageSync('locale', 'zh-CN')
uni.setStorageSync('UNI_LOCALE', 'zh-Hans')
}
if (locale === 'en') {
uni.setStorageSync('locale', 'en-US')
uni.setStorageSync('UNI_LOCALE', 'en')
}
const i18n = createI18n({
legacy: false,
locale,
fallbackLocale: locale,
messages: {
'zh-Hans': zhHans,
en
}
})
export default i18n
{
"bu-he-fa-de-data-shu-ju": "不合法的data数据",
"bu-xian-shi-input-shu-ru-kuang-chu-fa": "不显示input输入框触发",
"can-shu-pei-zhi": "参数配置",
"datepicker-ri-qi-xuan-ze-kuang": "DatePicker 日期选择框",
"dian-ji-ci-chu-chu-fa": "点击此处触发",
"dian-ji-ci-chu-chu-fa-dan-xuan": "点击此处触发单选",
"dian-ji-ci-chu-chu-fa-duo-xuan": "点击此处触发多选",
"dian-ji-le-di-index-xiang": "点击了第{0}项",
"dian-ji-le-di-index-xiang-0": "点击了第{0}项",
"dropdown": "Dropdown",
"dropdown-xia-la-cai-dan": "Dropdown 下拉菜单",
"duo-xuan": "多选",
"duo-xuan-mo-shi": "多选模式",
"duo-xuan-mo-shi-fu-zi-bu-hu-xiang-guan-lian": "多选模式(父子不互相关联)",
"duo-xuan-mo-shi-fu-zi-hu-xiang-guan-lian": "多选模式(父子互相关联)",
"duo-zhang-tu-pian-yu-lan": "多张图片预览",
"er-ji-11": "二级 1-1",
"er-ji-21": "二级 2-1",
"er-ji-22": "二级 2-2",
"er-ji-31": "二级 3-1",
"er-ji-32": "二级 3-2",
"fu-tai-qian-duan-uniapp-zu-jian-ku": "伏泰前端uni-app组件库",
"image": "Image",
"image-tu-pian": "Image 图片",
"ji-ben-shi-yong": "基本使用",
"ji-han-feng-bao": "极寒风暴",
"ji-huo-yan-se": "激活颜色",
"jia-bing": "加冰",
"jia-ge-you-xian": "价格优先",
"jia-re": "加热",
"jia-zai-zhong": "加载中...",
"jie-guo": "结果",
"ju-li": "距离",
"ju-li-you-xian": "距离优先",
"mo-ren-pai-xu": "默认排序",
"multipleselect-xia-la-duo-xuan": "MultipleSelect 下拉多选",
"nian": "年",
"nian-yue-ri-shi-fen-miao": "年月日时分秒",
"qi-hua-yao-cao": "琪花瑶草",
"qing-ci-li-ju": "清词丽句",
"qu-bing": "去冰",
"que-ding": "确定",
"ri": "日",
"ri-qi-xuan-ze-kuang": "日期选择框",
"san-ji-111": "三级 1-1-1",
"san-ji-211": "三级 2-1-1",
"san-ji-221": "三级 2-2-1",
"san-ji-311": "三级 3-1-1",
"san-ji-321": "三级 3-2-1",
"select": "Select",
"select-xia-la": "Select 下拉",
"shang-chuan": "上传",
"shi-fen-miao": "时分秒",
"shu-xing": "属性",
"treeselect-xia-la-shu": "TreeSelect 下拉树",
"tu-pian": "图片",
"upload": "Upload",
"upload-shang-chuan": "Upload 上传",
"virtualselect-xu-ni-xia-la-lie-biao": "VirtualSelect 虚拟下拉列表",
"wan-zhuanemei": "宛转蛾眉",
"wen-du": "温度",
"xia-bian-kuang": "下边框",
"xia-guang-wan-dao": "霞光万道",
"xia-la": "下拉",
"xia-la-cai-dan": "下拉菜单",
"xia-la-duo-xuan": "下拉多选",
"xia-la-shu": "下拉树",
"xu-ni-xia-la-lie-biao": "虚拟下拉列表",
"xu-se-shao-guang": "煦色韶光",
"yi-ji-1": "一级 1",
"yi-ji-2": "一级 2",
"yi-ji-3": "一级 3",
"yu-chen-yan-la": "鱼沉雁落",
"yue": "月",
"zan-wu-shu-ju": "暂无数据",
"zhang-tai-yang-liu": "章台杨柳",
"zhe-zhao-shi-fou-ke-dian-ji": "遮罩是否可点击",
"zheng-chang-wen": "正常温",
"zhong-zhi": "重置",
"zhong-zhi-gong-neng": "重置功能",
"zi-ding-yi-que-ren-yan-se-he-zi-ti": "自定义确认颜色和字体",
"zu-jian": "组件"
}
import {
createSSRApp
} from "vue";
import App from "./App.vue";
import uviewPlus from 'uview-plus';
import CDemo from './components/c-demo';
import i18n from './locales/index';
export function createApp() {
const app = createSSRApp(App);
app.use(uviewPlus)
app.use(i18n)
app.component('c-demo', CDemo);
return {
app
};
}
{
"name": "",
"appid": "",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"h5": {
"router": {
"base": "./"
},
"template": "./index.html"
}
}
export default {
methods: {
navigateToHome() {
uni.navigateTo({ url: '/pages/index/index' });
},
},
};
{
"easycom": {
"custom": {
"^v-(.*)": "/components/v-$1/v-$1.vue",
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "@vtx/ember"
}
},
{
"path": "pages/v-date-picker/index",
"style": {
"navigationBarTitleText": "DatePicker 日期选择框"
}
},
{
"path": "pages/v-image/index",
"style": {
"navigationBarTitleText": "Image 图片"
}
},
{
"path": "pages/v-multiple-select/index",
"style": {
"navigationBarTitleText": "MultipleSelect 下拉多选"
}
},
{
"path": "pages/v-select/index",
"style": {
"navigationBarTitleText": "Select 下拉"
}
},
{
"path": "pages/v-tree-select/index",
"style": {
"navigationBarTitleText": "TreeSelect 下拉树"
}
},
{
"path": "pages/v-virtual-select/index",
"style": {
"navigationBarTitleText": "VirtualSelect 虚拟下拉列表",
"enablePullDownRefresh": false
}
},
{
"path": "pages/v-upload/index",
"style": {
"navigationBarTitleText": "Upload 上传"
}
},
{
"path": "pages/v-dropdown/index",
"style": {
"navigationBarTitleText": "Dropdown 下拉菜单"
}
},
{
"path": "pages/v-facility-detail/index",
"style": {
"navigationBarTitleText": "FacilityDetail 设备、设施详情弹出"
}
},
{
"path": "pages/v-facility-detail-v2/index",
"style": {
"navigationBarTitleText": "FacilityDetail 设备、设施详情"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}
<template>
<div class="demo-home">
<h1 class="demo-home__title">
<span>@vtx/ember</span>
</h1>
<h2 class="demo-home__desc">{{ $t('fu-tai-qian-duan-uniapp-zu-jian-ku') }}</h2>
<div class="demo-home-nav">
<div class="demo-home-nav__title">{{ $t('zu-jian') }}</div>
<div class="demo-home-nav__group">
<view
class="demo-home-nav__block"
v-for="item in components"
:key="item.key"
@click="to(item.path)"
>
{{ item.title }}
<u-icon name="arrow-right" class="demo-home-nav__icon"></u-icon>
</view>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
components: [
{
key: 'DatePicker',
title: this.$t('datepicker-ri-qi-xuan-ze-kuang'),
path: '/pages/v-date-picker/index',
},
{
key: this.$t('image'),
title: this.$t('image-tu-pian'),
path: '/pages/v-image/index',
},
{
key: 'MultipleSelect',
title: this.$t('multipleselect-xia-la-duo-xuan'),
path: '/pages/v-multiple-select/index',
},
{
key: this.$t('select'),
title: this.$t('select-xia-la'),
path: '/pages/v-select/index',
},
{
key: 'TreeSelect',
title: this.$t('treeselect-xia-la-shu'),
path: '/pages/v-tree-select/index',
},
{
key: 'VirtualSelect',
title: this.$t('virtualselect-xu-ni-xia-la-lie-biao'),
path: '/pages/v-virtual-select/index',
},
{
key: this.$t('upload'),
title: this.$t('upload-shang-chuan'),
path: '/pages/v-upload/index',
},
{
key: this.$t('dropdown'),
title: this.$t('dropdown-xia-la-cai-dan'),
path: '/pages/v-dropdown/index',
},
{
key: 'FacilityDetail',
title: 'FacilityDetail 设备、设施详情弹出',
path: '/pages/v-facility-detail/index',
},
{
key: 'FacilityDetailV2',
title: 'FacilityDetailV2 设备、设施详情无弹窗',
path: '/pages/v-facility-detail-v2/index',
},
],
};
},
methods: {
to(path) {
uni.navigateTo({ url: path });
},
},
};
</script>
<style lang="scss" scoped>
.demo-home {
box-sizing: border-box;
width: 100%;
min-height: 100vh;
padding: 24px 10px 20px;
background: #fff;
}
.demo-home__title,
.demo-home__desc {
padding-left: 16px;
font-weight: normal;
line-height: 1;
user-select: none;
}
.demo-home__title {
margin: 0 0 16px;
font-size: 32px;
span {
display: inline-block;
vertical-align: middle;
}
}
.demo-home__desc {
margin: 0 0 30px;
color: rgba(69, 90, 100, 0.6);
font-size: 14px;
}
.demo-home-nav {
&__title {
margin: 24px 0 8px 16px;
color: rgba(69, 90, 100, 0.6);
font-size: 14px;
}
}
.demo-home-nav__block {
position: relative;
display: flex;
margin: 0 0 12px;
padding-left: 20px;
color: #323233;
font-weight: 600;
font-size: 14px;
line-height: 40px;
background: #f7f8fa;
border-radius: 99px;
transition: background 0.3s;
.demo-home-nav__icon {
position: absolute;
top: 50%;
right: 16px;
width: 16px;
height: 16px;
margin-top: -8px;
}
&:hover {
background: #eef0f4;
}
}
</style>
<template>
<view>
<u-navbar :title="$t('ri-qi-xuan-ze-kuang')" @leftClick="navigateToHome" :border="true" > </u-navbar>
<c-demo :title="$t('ji-ben-shi-yong')">
<v-date-picker v-model="date"></v-date-picker>
</c-demo>
<c-demo :title="$t('nian')">
<v-date-picker type="year" v-model="date1"></v-date-picker>
</c-demo>
<c-demo :title="$t('yue')">
<v-date-picker type="month" v-model="date2"></v-date-picker>
</c-demo>
<c-demo :title="$t('ri')">
<v-date-picker type="date" v-model="date3"></v-date-picker>
</c-demo>
<c-demo :title="$t('nian-yue-ri-shi-fen-miao')">
<v-date-picker type="datetime" v-model="date4"></v-date-picker>
</c-demo>
<c-demo :title="$t('shi-fen-miao')">
<v-date-picker type="time" v-model="date5"></v-date-picker>
</c-demo>
<c-demo :title="$t('shi-fen')">
<v-date-picker type="hourMinute" v-model="date6"></v-date-picker>
</c-demo>
<c-demo :title="$t('zhong-zhi-gong-neng')">
<u-button @click="reset">{{ $t('zhong-zhi') }}</u-button>
</c-demo>
</view>
</template>
<script>
import dayjs from 'dayjs';
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
watch: {
date(val) {
console.log('date', val);
},
date1(val) {
console.log('date1', val);
},
date2(val) {
console.log('date2', val);
},
date3(val) {
console.log('date3', val);
},
date4(val) {
console.log('date4', val);
},
date5(val) {
console.log('date5', val);
},
date6(val) {
console.log('date6', val);
},
},
data() {
return {
date: dayjs().format('YYYY-MM-DD'),
date1: dayjs().format('YYYY'),
date2: dayjs().format('YYYY-MM'),
date3: dayjs().format('YYYY-MM-DD'),
date4: dayjs().format('YYYY-MM-DD HH:mm:ss'),
date5: dayjs().format('HH:mm:ss'),
date6: dayjs().format('HH:mm'),
};
},
methods: {
reset() {
this.date = dayjs().format('YYYY-MM-DD');
this.date1 = dayjs().format('YYYY');
this.date2 = dayjs().format('YYYY-MM');
this.date3 = dayjs().format('YYYY-MM-DD');
this.date4 = dayjs().format('YYYY-MM-DD HH:mm:ss');
this.date5 = dayjs().format('HH:mm:ss');
this.date6 = dayjs().format('HH:mm');
},
},
};
</script>
<template>
<view>
<u-navbar
:title="$t('xia-la-cai-dan')"
@leftClick="navigateToHome"
:border="true"
></u-navbar>
<c-demo :title="$t('ji-ben-shi-yong')">
<view class="u-demo-area u-flex u-row-center">
<v-dropdown
:close-on-click-mask="mask"
ref="uDropdown"
:activeColor="activeColor"
:borderBottom="borderBottom"
>
<v-dropdown-item
@change="change"
v-model="value1"
:title="$t('ju-li')"
:options="options1"
:titleSize="14"
></v-dropdown-item>
<v-dropdown-item
@change="change"
v-model="value2"
:title="$t('wen-du')"
:options="options2"
></v-dropdown-item>
<v-dropdown-item :title="$t('shu-xing')">
<view class="slot-content">
<view class="item-box">
<view
class="item"
:class="[item.active ? 'active' : '']"
@tap="tagClick(index)"
v-for="(item, index) in list"
:key="index"
>
{{ item.label }}
</view>
</view>
<u-button type="primary" @click="closeDropdown">{{
$t('que-ding')
}}</u-button>
</view>
</v-dropdown-item>
</v-dropdown>
</view>
<view class="u-config-wrap">
<view class="u-config-title u-border-bottom"> {{ $t('can-shu-pei-zhi') }} </view>
<view class="u-config-item">
<view class="u-item-title">{{ $t('xia-bian-kuang') }}</view>
<u-subsection
:current="curNow"
:list="['有', '无']"
@change="borderChange"
></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">{{ $t('ji-huo-yan-se') }}</view>
<u-subsection
:current="curNow2"
:list="['#2979ff', '#ff9900', '#19be6b']"
@change="activeColorChange"
></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">{{ $t('zhe-zhao-shi-fou-ke-dian-ji') }}</view>
<u-subsection
:current="curNow3"
:list="['是', '否']"
@change="maskChange"
></u-subsection>
</view>
</view>
</c-demo>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
return {
curNow: 1,
curNow2: 0,
curNow3: 0,
value1: '',
value2: '2',
mask: true,
options1: [
{
label: this.$t('mo-ren-pai-xu'),
value: 1,
},
{
label: this.$t('ju-li-you-xian'),
value: 2,
},
{
label: this.$t('jia-ge-you-xian'),
value: 3,
},
],
options2: [
{
label: this.$t('qu-bing'),
value: 1,
},
{
label: this.$t('jia-bing'),
value: 2,
},
{
label: this.$t('zheng-chang-wen'),
value: 3,
},
{
label: this.$t('jia-re'),
value: 4,
},
{
label: this.$t('ji-han-feng-bao'),
value: 5,
},
],
list: [
{
label: this.$t('qi-hua-yao-cao'),
active: true,
},
{
label: this.$t('qing-ci-li-ju'),
active: false,
},
{
label: this.$t('wan-zhuanemei'),
active: false,
},
{
label: this.$t('xu-se-shao-guang'),
active: false,
},
{
label: this.$t('yu-chen-yan-la'),
active: false,
},
{
label: this.$t('zhang-tai-yang-liu'),
active: false,
},
{
label: this.$t('xia-guang-wan-dao'),
active: false,
},
],
borderBottom: false,
activeColor: '#2979ff',
};
},
methods: {
borderChange(index) {
this.curNow = index;
this.borderBottom = !index;
},
activeColorChange(index) {
this.curNow2 = index;
this.activeColor = ['#2979ff', '#ff9900', '#19be6b'][index];
},
change(index) {
this.$u.toast(this.$t('dian-ji-le-di-index-xiang', [index]));
},
closeDropdown() {
this.$refs.uDropdown.close();
},
tagClick(index) {
this.list[index].active = !this.list[index].active;
},
maskChange(index) {
this.curNow3 = index;
this.mask = !index;
},
},
};
</script>
<style scoped lang="scss">
.u-config-wrap {
padding: 40rpx;
.u-config-title {
text-align: center;
font-size: 16px;
font-weight: bold;
margin: 10px 0;
padding: 5px;
}
.u-item-title {
padding: 12rpx;
}
.u-config-item {
padding: 24rpx 0;
}
}
.slot-content {
background-color: #ffffff;
padding: 24rpx;
.item-box {
margin-bottom: 50rpx;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item {
border: 1px solid #2979ff;
color: #2979ff;
padding: 8rpx 40rpx;
border-radius: 100rpx;
margin-top: 30rpx;
font-size: 28rpx;
}
.active {
color: #ffffff;
background-color: #2979ff;
}
}
}
</style>
<template>
<view style="height: calc(100vh - 44px);background-color: #f5f5f5;">
<v-facility-detail
:title="title"
:type="type"
:id="id"
:token="token"
:reqParams="reqParams"
:version="version"
></v-facility-detail>
</view>
</template>
<script>
import { getUrlParam } from '@vtx/utils';
export default {
data() {
return {
reqParams: {
token: getUrlParam('token') || '6640c8db-23c2-4800-b60e-968f5471607e',
tenantId: getUrlParam('tenantId') || '7cf54d8ea4864ff28cd515af6eed5e40',
userId: getUrlParam('userId') || 'e69114cc5f382745917341bd9d18b0ba',
apiPre: 'https://lhsw.cloudhw.cn:8466/',
},
type: '1',
id: getUrlParam('id') || '2001908740425117698',
title: decodeURIComponent(getUrlParam('title') || '919雨水窨井1'),
token: getUrlParam('token') || '6640c8db-23c2-4800-b60e-968f5471607e',
version: '2',
};
},
};
</script>
<style></style>
<template>
<view style="height: 100vh; background-color: #f5f5f5">
<u-button @click="click">打开详情</u-button>
<v-facility-detail
:title="title"
:type="type"
:id="id"
:token="token"
:reqParams="reqParams"
:show="show"
@close="show = false"
></v-facility-detail>
</view>
</template>
<script>
export default {
data() {
return {
show: false,
reqParams: {
token: '0fc6621a-727b-4dc0-8d70-e6e7a4f81781',
tenantId: '7cf54d8ea4864ff28cd515af6eed5e40',
userId: 'e69114cc5f382745917341bd9d18b0ba',
apiPre: 'http://183.129.170.219:8103/',
},
type: '1',
id: '1839180487893975040',
title: '0926雨1水窨井1',
token: '0fc6621a-727b-4dc0-8d70-e6e7a4f81781',
};
},
methods: {
click() {
if (this.id == '1839180487893975040') {
this.id = '1819197588860043264';
this.title = '东方大道雨水窨井001';
} else {
this.id = '1839180487893975040';
this.title = '0926雨1水窨井1';
}
this.show = true;
},
},
};
</script>
<style></style>
<template>
<view>
<u-navbar :title="$t('tu-pian')" @leftClick="navigateToHome" :border="true"></u-navbar>
<c-demo :title="$t('ji-ben-shi-yong')">
<v-image width="100%" height="300rpx" :src="src"></v-image>
</c-demo>
<c-demo :title="$t('duo-zhang-tu-pian-yu-lan')">
<v-image-previewer>
<v-image width="100%" height="300rpx" :src="src" style="margin-bottom: 8px"></v-image>
<v-image width="100%" height="300rpx"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/auto-orient,1/resize,p_10/quality,q_10"></v-image>
</v-image-previewer>
</c-demo>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
return {
src:
'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=273034283,2961511002&fm=26&gp=0.jpg',
};
},
};
</script>
<template>
<view>
<u-navbar :title="$t('xia-la-duo-xuan')" @leftClick="navigateToHome" :border="true"></u-navbar>
<c-demo :title="$t('ji-ben-shi-yong')">
<v-multiple-select :data="list" v-model="checkedKeys"></v-multiple-select>
<v-multiple-select :data="list" v-model="checkedKeys1"></v-multiple-select>
<v-multiple-select :data="list" v-model="checkedKeys2" border="surround"></v-multiple-select>
</c-demo>
<c-demo :title="$t('zi-ding-yi-que-ren-yan-se-he-zi-ti')">
<v-multiple-select :data="[]" confirmText="提交" confirmColor="#18b566"></v-multiple-select>
</c-demo>
<c-demo :title="$t('zhong-zhi-gong-neng')">
<u-button @click="reset">{{ $t('zhong-zhi') }}</u-button>
</c-demo>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
const data = [];
for (let i = 10; i < 36; i++) {
data.push({
key: i.toString(36) + i,
value: i.toString(36) + i,
});
}
return {
list: data,
checkedKeys: [],
checkedKeys1: ['a10'],
checkedKeys2: [],
};
},
methods: {
reset() {
this.checkedKeys1 = ['a10'];
},
},
};
</script>
<style></style>
<template>
<view>
<u-navbar :title="$t('xia-la')" @leftClick="navigateToHome" :border="true"></u-navbar>
<c-demo :title="$t('ji-ben-shi-yong')">
<v-select :data="list" v-model="checkedKeys1" :customStyle="{ background: '#f3f5f9' }"
/></c-demo>
<c-demo :title="$t('')">
<v-select :data="list" v-model="checkedKeys2" @change="change" />
</c-demo>
<c-demo :title="$t('')">
<v-select
:data="list"
v-model="checkedKeys3"
border="surround"
:clear="clear"
:clearFlag="true"
/></c-demo>
<c-demo :title="$t('')">
<v-select :data="list" v-model="checkedKeys4" border="surround" />
</c-demo>
<c-demo :title="$t('zhong-zhi-gong-neng')">
<u-button @click="reset">{{ $t('zhong-zhi') }}</u-button>
</c-demo>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
const data = [];
for (let i = 10; i < 36; i++) {
data.push({
key: i.toString(36) + i + Math.random(),
value: i.toString(36) + i,
});
}
return {
list: data,
checkedKeys1: 'k20',
checkedKeys2: '',
checkedKeys3: '',
checkedKeys4: '',
};
},
methods: {
reset() {
this.checkedKeys1 = 'k20';
this.checkedKeys2 = '';
this.checkedKeys3 = '';
this.checkedKeys4 = '';
},
clear() {
this.checkedKeys1 = '';
},
change(val) {
console.log('111val', val);
},
},
};
</script>
<style></style>
<template>
<view>
<u-navbar :title="$t('xia-la-shu')" @leftClick="navigateToHome" :border="true"></u-navbar>
<c-demo :title="$t('ji-ben-shi-yong')">
<v-tree-select
v-model="checkedKey"
:treeData="treeData"
:node-key="'id'"
:node-label="'label'"
></v-tree-select>
</c-demo>
<c-demo :title="$t('duo-xuan-mo-shi-fu-zi-bu-hu-xiang-guan-lian')">
<v-tree-select
v-model="checkedKey1"
:treeData="treeData"
mode="multiple"
:node-key="'id'"
:node-label="'label'"
@change="handleChange"
></v-tree-select>
</c-demo>
<c-demo :title="$t('duo-xuan-mo-shi-fu-zi-hu-xiang-guan-lian')">
<v-tree-select
v-model="checkedKey11"
:treeData="treeData"
mode="multiple"
:node-key="'id'"
:node-label="'label'"
@change="handleChange"
:checkStrictly="true"
></v-tree-select>
</c-demo>
<c-demo :title="$t('bu-xian-shi-input-shu-ru-kuang-chu-fa')">
<u-button type="primary" @click="visible2 = true" size="medium">{{
$t('dian-ji-ci-chu-chu-fa-duo-xuan')
}}</u-button>
<v-tree-select
v-model="checkedKey2"
:treeData="treeData"
mode="multiple"
:node-key="'id'"
:node-label="'label'"
:input="false"
v-if="visible2"
@hide="visible2 = false"
></v-tree-select>
<u-cell :title="$t('jie-guo')" :value="inputValue2" :arrow="false"></u-cell>
<u-button type="primary" @click="visible3 = true" size="medium">{{
$t('dian-ji-ci-chu-chu-fa-dan-xuan')
}}</u-button>
<v-tree-select
v-model="checkedKey3"
:treeData="treeData"
:node-key="'id'"
:node-label="'label'"
:input="false"
v-if="visible3"
@hide="visible3 = false"
></v-tree-select>
<u-cell :title="$t('jie-guo')" :value="inputValue3" :arrow="false"></u-cell>
</c-demo>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
return {
treeData: [],
checkedKey: [],
checkedKey1: [1, 11, 111],
checkedKey11: [1, 11, 111],
checkedKey2: [],
checkedKey3: [],
visible2: false,
visible3: false,
};
},
onLoad() {
// 模拟异步请求
setTimeout(() => {
this.treeData = [
{
id: 1,
label: this.$t('yi-ji-1'),
children: [
{
id: 11,
label: this.$t('er-ji-11'),
children: [
{
id: 111,
label: this.$t('san-ji-111'),
},
],
},
],
},
{
id: 2,
label: this.$t('yi-ji-2'),
children: [
{
id: 21,
label: this.$t('er-ji-21'),
children: [
{
id: 211,
label: this.$t('san-ji-211'),
},
],
},
{
id: 22,
label: this.$t('er-ji-22'),
children: [
{
id: 221,
label: this.$t('san-ji-221'),
},
],
},
],
},
{
id: 3,
label: this.$t('yi-ji-3'),
children: [
{
id: 31,
label: this.$t('er-ji-31'),
children: [
{
id: 311,
label: this.$t('san-ji-311'),
},
],
},
{
id: 32,
label: this.$t('er-ji-32'),
children: [
{
id: 321,
label: this.$t('san-ji-321'),
},
],
},
],
},
];
}, 200);
},
computed: {
// 计算文本框显示的值
inputValue2: function() {
return this.checkedKey2.join(',');
},
inputValue3: function() {
return this.checkedKey3.join(',');
},
},
methods: {
handleChange(checkedKeys) {
console.log(checkedKeys);
},
},
};
</script>
<style></style>
<template>
<view>
<u-navbar @leftClick="navigateToHome" :border="true"></u-navbar>
<c-demo :title="$t('ji-ben-shi-yong')">
<v-upload :fileList="photos" @change="handleChange"></v-upload>
</c-demo>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
return {
// [{id: 'f4f29d6892be4be796207a27a746e839', name: '2.jpg', type: 'image/jpeg'}]
photos: [],
};
},
methods: {
handleChange(fileList) {
console.log('111fileList', fileList);
// TODO
},
},
};
</script>
<template>
<view>
<u-navbar @leftClick="navigateToHome" :border="true"></u-navbar>
<c-demo :title="$t('ji-ben-shi-yong')">
<v-virtual-select
:requestOption="{
url: '/cloud/advise/web/base/shop/list',
method: 'POST',
}"
:requestParams="{
order: 'asc,asc',
sort: 'orgName,manageUnitName',
}"
:setValueLabel="{
value: 'id',
label: 'name',
}"
:setPageRows="{
page: 'page',
rows: 'rows',
searchText: 'nameLike',
}"
v-model="checkedKeys"
border="bottom"
></v-virtual-select>
</c-demo>
<c-demo :title="$t('duo-xuan')">
<v-virtual-select
:requestOption="{
url: '/cloud/advise/web/base/shop/list',
method: 'POST',
}"
:requestParams="{
order: 'asc,asc',
sort: 'orgName,manageUnitName',
}"
:setValueLabel="{
value: 'id',
label: 'name',
}"
:setPageRows="{
page: 'page',
rows: 'rows',
searchText: 'nameLike',
}"
v-model="checkedKeys1"
border="bottom"
mode="multiple"
></v-virtual-select>
</c-demo>
<c-demo :title="$t('zhong-zhi-gong-neng')">
<u-button @click="reset">{{ $t('zhong-zhi') }}</u-button>
</c-demo>
</view>
</template>
<script>
import mixin from '../../mixins/components';
export default {
mixins: [mixin],
data() {
const data = [];
for (let i = 10; i < 10000; i++) {
const value = i.toString(36) + i;
data.push({
value,
label: value,
});
}
return {
list: data,
checkedKeys: [],
checkedKeys1: [{ value: 'a10', label: 'a10' }],
};
},
methods: {
reset() {
this.checkedKeys = [{ value: 'a10', label: 'a10' }];
this.checkedKeys1 = [];
},
},
};
</script>
<style></style>
## 2.6.2(2023-10-31)
1.`修复` 在源码中有异常字符导致的在vue3中编译报错的问题。
2.`修复` 在微信小程序中`z-paging-refresh`的height无效的问题(by xiaohe0601)。
近期更新:
=============================
1.`新增` 手动更新自定义下拉刷新view高度方法。
2.`新增` 点击返回顶部按钮添加事件监听&支持拦截。
3.`新增` 是否开启下拉刷新状态栏占位,适用于隐藏导航栏时,下拉刷新需要避开状态栏高度的情况。
4.`新增` 支持配置网络请求失败触发`reject`。
5.`修复` 显示空数据图时,滚动到底部依然可以加载更多的问题。
6.`修复` 在vue2中底部加载更多相关`slot`使用`template`插入无效的问题。
7.`修复` `complete`的`Promise`可能无效的问题。
8.`优化` `hooks`判断`z-paging`为空则不调用。
<!-- 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 {}
/* [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-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 {
overflow: hidden;
}
.zp-custom-refresher-refresh {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
}
.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...",
"2022-01-01-23-59-59": "2022-01-01 23:59:59",
"yyyy-mm-dd-hh-mm-ss": "YYYY-MM-DD HH:mm:ss",
"zhong-zhi": "重置",
"zhong-zhi-gong-neng": "重置功能",
"shi-fen": "时分",
"shi-fen-miao": "时分秒",
"nian-yue-ri-shi-fen-miao": "年月日时分秒",
"ri": "日",
"yue": "月",
"nian": "年",
"ji-ben-shi-yong": "基本使用"
}
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 || !cPaging.value) return;
cPaging.value.reload().catch(() => {});
})
onPageScroll(e => {
if (!cPaging || !cPaging.value) return;
cPaging.value.updatePageScrollTop(e.scrollTop);
e.scrollTop < 10 && cPaging.value.doChatRecordLoadMore();
})
onReachBottom(() => {
if (!cPaging || !cPaging.value) return;
cPaging.value.pageReachBottom();
})
}
export default useZPaging
// [z-paging]useZPagingComp hooks
function useZPagingComp(paging) {
const cPaging = !!paging ? paging.value || paging : null;
const reload = () => {
if (!cPaging || !cPaging.value) return;
cPaging.value.reload().catch(() => {});
}
const updatePageScrollTop = scrollTop => {
if (!cPaging || !cPaging.value) return;
cPaging.value.updatePageScrollTop(scrollTop);
}
const doChatRecordLoadMore = () => {
if (!cPaging || !cPaging.value) return;
cPaging.value.doChatRecordLoadMore();
}
const pageReachBottom = () => {
if (!cPaging || !cPaging.value) return;
cPaging.value.pageReachBottom();
}
return { reload, updatePageScrollTop, doChatRecordLoadMore, pageReachBottom };
}
export default useZPagingComp
// [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() {
let callbacked = false;
this.$emit('backToTopClick', toTop => {
(toTop === undefined || toTop === true) && this._handleToTop();
callbacked = true;
});
this.$nextTick(() => {
!callbacked && this._handleToTop();
})
},
//处理滚动到顶部
_handleToTop() {
!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 = uni.getSystemInfoSync().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]数据处理模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import Enum from '.././z-paging-enum'
import interceptor from '../z-paging-interceptor'
export default {
props: {
//自定义初始的pageNo,默认为1
defaultPageNo: {
type: [Number, String],
default: u.gc('defaultPageNo', 1),
observer: function(newVal) {
this.pageNo = newVal;
},
},
//自定义pageSize,默认为10
defaultPageSize: {
type: [Number, String],
default: u.gc('defaultPageSize', 10),
validator: (value) => {
if (value <= 0) u.consoleErr('default-page-size必须大于0!');
return value > 0;
}
},
//为保证数据一致,设置当前tab切换时的标识key,并在complete中传递相同key,若二者不一致,则complete将不会生效
dataKey: {
type: [Number, String, Object],
default: function() {
return u.gc('dataKey', null);
},
},
//使用缓存,若开启将自动缓存第一页的数据,默认为否。请注意,因考虑到切换tab时不同tab数据不同的情况,默认仅会缓存组件首次加载时第一次请求到的数据,后续的下拉刷新操作不会更新缓存。
useCache: {
type: Boolean,
default: u.gc('useCache', false)
},
//使用缓存时缓存的key,用于区分不同列表的缓存数据,useCache为true时必须设置,否则缓存无效
cacheKey: {
type: String,
default: u.gc('cacheKey', null)
},
//缓存模式,默认仅会缓存组件首次加载时第一次请求到的数据,可设置为always,即代表总是缓存,每次列表刷新(下拉刷新、调用reload等)都会更新缓存
cacheMode: {
type: String,
default: u.gc('cacheMode', Enum.CacheMode.Default)
},
//自动注入的list名,可自动修改父view(包含ref="paging")中对应name的list值
autowireListName: {
type: String,
default: u.gc('autowireListName', '')
},
//自动注入的query名,可自动调用父view(包含ref="paging")中的query方法
autowireQueryName: {
type: String,
default: u.gc('autowireQueryName', '')
},
//z-paging mounted后自动调用reload方法(mounted后自动调用接口),默认为是
auto: {
type: Boolean,
default: u.gc('auto', true)
},
//用户下拉刷新时是否触发reload方法,默认为是
reloadWhenRefresh: {
type: Boolean,
default: u.gc('reloadWhenRefresh', true)
},
//reload时自动滚动到顶部,默认为是
autoScrollToTopWhenReload: {
type: Boolean,
default: u.gc('autoScrollToTopWhenReload', true)
},
//reload时立即自动清空原list,默认为是,若立即自动清空,则在reload之后、请求回调之前页面是空白的
autoCleanListWhenReload: {
type: Boolean,
default: u.gc('autoCleanListWhenReload', true)
},
//列表刷新时自动显示下拉刷新view,默认为否
showRefresherWhenReload: {
type: Boolean,
default: u.gc('showRefresherWhenReload', false)
},
//列表刷新时自动显示加载更多view,且为加载中状态,默认为否
showLoadingMoreWhenReload: {
type: Boolean,
default: u.gc('showLoadingMoreWhenReload', false)
},
//组件created时立即触发reload(可解决一些情况下先看到页面再看到loading的问题),auto为true时有效。为否时将在mounted+nextTick后触发reload,默认为否
createdReload: {
type: Boolean,
default: u.gc('createdReload', false)
},
//本地分页时上拉加载更多延迟时间,单位为毫秒,默认200毫秒
localPagingLoadingTime: {
type: [Number, String],
default: u.gc('localPagingLoadingTime', 200)
},
//使用聊天记录模式,默认为否
useChatRecordMode: {
type: Boolean,
default: u.gc('useChatRecordMode', false)
},
//使用聊天记录模式时是否自动隐藏键盘:在用户触摸列表时候自动隐藏键盘,默认为是
autoHideKeyboardWhenChat: {
type: Boolean,
default: u.gc('autoHideKeyboardWhenChat', true)
},
//自动拼接complete中传过来的数组(使用聊天记录模式时无效)
concat: {
type: Boolean,
default: u.gc('concat', true)
},
//请求失败是否触发reject,默认为是
callNetworkReject: {
type: Boolean,
default: u.gc('callNetworkReject', true)
},
//父组件v-model所绑定的list的值
value: {
type: Array,
default: function() {
return [];
}
},
// #ifdef VUE3
modelValue: {
type: Array,
default: function() {
return [];
}
}
// #endif
},
data (){
return {
currentData: [],
totalData: [],
realTotalData: [],
totalLocalPagingList: [],
dataPromiseResultMap: {
reload: null,
complete: null,
localPaging: null
},
isSettingCacheList: false,
pageNo: 1,
currentRefreshPageSize: 0,
isLocalPaging: false,
isAddedData: false,
isTotalChangeFromAddData: false,
privateConcat: true,
myParentQuery: -1,
firstPageLoaded: false,
pagingLoaded: false,
loaded: false,
isUserReload: true,
fromEmptyViewReload: false,
queryFrom: '',
listRendering: false,
isHandlingRefreshToPage: false
}
},
computed: {
pageSize() {
return this.defaultPageSize;
},
finalConcat() {
return this.concat && this.privateConcat;
},
finalUseCache() {
if (this.useCache && !this.cacheKey) {
u.consoleErr('use-cache为true时,必须设置cache-key,否则缓存无效!');
}
return this.useCache && !!this.cacheKey;
},
finalCacheKey() {
return this.cacheKey ? `${c.cachePrefixKey}-${this.cacheKey}` : null;
},
isFirstPage() {
return this.pageNo === this.defaultPageNo;
}
},
watch: {
totalData(newVal, oldVal) {
this._totalDataChange(newVal, oldVal);
},
currentData(newVal, oldVal) {
this._currentDataChange(newVal, oldVal);
},
useChatRecordMode(newVal, oldVal) {
if (newVal) {
this.nLoadingMoreFixedHeight = false;
}
},
value: {
handler(newVal) {
this.realTotalData = newVal;
},
immediate: true
},
// #ifdef VUE3
modelValue: {
handler(newVal) {
this.realTotalData = newVal;
},
immediate: true
}
// #endif
},
methods: {
//请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为是否成功(默认是是)
complete(data, success = true) {
this.customNoMore = -1;
return this.addData(data, success);
},
//【保证数据一致】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为dataKey,需与:data-key绑定的一致,第三个参数为是否成功(默认为是)
completeByKey(data, dataKey = null, success = true) {
if (dataKey !== null && this.dataKey !== null && dataKey !== this.dataKey) {
this.isFirstPage && this.endRefresh();
return new Promise(resolve => resolve());
}
this.customNoMore = -1;
return this.addData(data, success);
},
//【通过total判断是否有更多数据】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为total(列表总数),第三个参数为是否成功(默认为是)
completeByTotal(data, total, success = true) {
if (total == 'undefined') {
this.customNoMore = -1;
} else {
const dataTypeRes = this._checkDataType(data, success, false);
data = dataTypeRes.data;
success = dataTypeRes.success;
if (total >= 0 && success) {
return new Promise((resolve, reject) => {
this.$nextTick(() => {
let nomore = false;
const realTotalDataCount = this.pageNo == this.defaultPageNo ? 0 : this.realTotalData.length;
const dataLength = this.privateConcat ? data.length : 0;
let exceedCount = realTotalDataCount + dataLength - total;
if (exceedCount >= 0) {
nomore = true;
exceedCount = this.defaultPageSize - exceedCount;
if (this.privateConcat && exceedCount > 0 && exceedCount < data.length) {
data = data.splice(0, exceedCount);
}
}
this.completeByNoMore(data, nomore, success).then(res => resolve(res)).catch(() => reject());
})
});
}
}
return this.addData(data, success);
},
//【自行判断是否有更多数据】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为是否有更多数据,第三个参数为是否成功(默认是是)
completeByNoMore(data, nomore, success = true) {
if (nomore != 'undefined') {
this.customNoMore = nomore == true ? 1 : 0;
}
return this.addData(data, success);
},
//与上方complete方法功能一致,新版本中设置服务端回调数组请使用complete方法
addData(data, success = true) {
if (!this.fromCompleteEmit) {
this.disabledCompleteEmit = true;
this.fromCompleteEmit = false;
}
const currentTimeStamp = u.getTime();
const disTime = currentTimeStamp - this.requestTimeStamp;
let minDelay = this.minDelay;
if (this.isFirstPage && this.finalShowRefresherWhenReload) {
minDelay = Math.max(400, minDelay);
}
const addDataDalay = (this.requestTimeStamp > 0 && disTime < minDelay) ? minDelay - disTime : 0;
this.$nextTick(() => {
u.delay(() => {
this._addData(data, success, false);
}, this.delay > 0 ? this.delay : addDataDalay)
})
return new Promise((resolve, reject) => {
this.dataPromiseResultMap.complete = { resolve, reject };
});
},
//从顶部添加数据,不会影响分页的pageNo和pageSize
addDataFromTop(data, toTop = true, toTopWithAnimate = true) {
data = Object.prototype.toString.call(data) !== '[object Array]' ? [data] : data.reverse();
// #ifndef APP-NVUE
this.finalUseVirtualList && this._setCellIndex(data, 'top')
// #endif
this.totalData = [...data, ...this.totalData];
if (toTop) {
u.delay(() => this._scrollToTop(toTopWithAnimate));
}
},
//重新设置列表数据,调用此方法不会影响pageNo和pageSize,也不会触发请求。适用场景:当需要删除列表中某一项时,将删除对应项后的数组通过此方法传递给z-paging。(当出现类似的需要修改列表数组的场景时,请使用此方法,请勿直接修改page中:list.sync绑定的数组)
resetTotalData(data) {
this.isTotalChangeFromAddData = true;
data = Object.prototype.toString.call(data) !== '[object Array]' ? [data] : data;
this.totalData = data;
},
//添加聊天记录
addChatRecordData(data, toBottom = true, toBottomWithAnimate = true) {
data = Object.prototype.toString.call(data) !== '[object Array]' ? [data] : data;
if (!this.useChatRecordMode) return;
this.isTotalChangeFromAddData = true;
//#ifndef APP-NVUE
this.totalData = [...this.totalData, ...data];
//#endif
//#ifdef APP-NVUE
this.totalData = this.nIsFirstPageAndNoMore ? [...this.totalData, ...data] : [...data, ...this.totalData];
//#endif
if (toBottom) {
u.delay(() => {
//#ifndef APP-NVUE
this._scrollToBottom(toBottomWithAnimate);
//#endif
//#ifdef APP-NVUE
this.nIsFirstPageAndNoMore ? this._scrollToBottom(toBottomWithAnimate) : this._scrollToTop(toBottomWithAnimate);
//#endif
})
}
},
//设置本地分页数据,请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging作分页处理(若调用了此方法,则上拉加载更多时内部会自动分页,不会触发@query所绑定的事件)
setLocalPaging(data, success = true) {
this.isLocalPaging = true;
this.$nextTick(() => {
this._addData(data, success, true);
})
return new Promise((resolve, reject) => {
this.dataPromiseResultMap.localPaging = { resolve, reject };
});
},
//重新加载分页数据,pageNo会恢复为默认值,相当于下拉刷新的效果(animate为true时会展示下拉刷新动画,默认为false)
reload(animate = this.showRefresherWhenReload) {
if (animate) {
this.privateShowRefresherWhenReload = animate;
this.isUserPullDown = true;
}
if (!this.showLoadingMoreWhenReload) {
this.listRendering = true;
}
this.$nextTick(() => {
this._preReload(animate, false);
})
return new Promise((resolve, reject) => {
this.dataPromiseResultMap.reload = { resolve, reject };
});
},
//刷新列表数据,pageNo和pageSize不会重置,列表数据会重新从服务端获取。必须保证@query绑定的方法中的pageNo和pageSize和传给服务端的一致
refresh() {
return this._handleRefreshWithDisPageNo(this.pageNo - this.defaultPageNo + 1);
},
//刷新列表数据至指定页,例如pageNo=5时则代表刷新列表至第5页,此时pageNo会变为5,列表会展示前5页的数据。必须保证@query绑定的方法中的pageNo和pageSize和传给服务端的一致
refreshToPage(pageNo) {
this.isHandlingRefreshToPage = true;
return this._handleRefreshWithDisPageNo(pageNo + this.defaultPageNo - 1);
},
//手动更新列表缓存数据,将自动截取v-model绑定的list中的前pageSize条覆盖缓存,请确保在list数据更新到预期结果后再调用此方法
updateCache() {
if (this.finalUseCache && this.totalData.length) {
this._saveLocalCache(this.totalData.slice(0, Math.min(this.totalData.length, this.pageSize)));
}
},
//清空分页数据
clean() {
this._reload(true);
this._addData([], true, false);
},
//清空分页数据
clear() {
this.clean();
},
//手动触发滚动到顶部加载更多,聊天记录模式时有效
doChatRecordLoadMore() {
this.useChatRecordMode && this._onLoadingMore('click');
},
//reload之前的一些处理
_preReload(animate = this.showRefresherWhenReload, isFromMounted = true) {
const showRefresher = this.finalRefresherEnabled && this.useCustomRefresher;
// #ifndef APP-NVUE
if (this.customRefresherHeight === -1 && showRefresher) {
u.delay(() => this._preReload(animate, isFromMounted), c.delayTime / 2);
return;
}
// #endif
this.isUserReload = true;
this.loadingType = Enum.LoadingType.Refresher;
if (animate) {
this.privateShowRefresherWhenReload = animate;
// #ifndef APP-NVUE
if (this.useCustomRefresher) {
this._doRefresherRefreshAnimate();
} else {
this.refresherTriggered = true;
}
// #endif
// #ifdef APP-NVUE
this.refresherStatus = Enum.Refresher.Loading;
this.refresherRevealStackCount ++;
u.delay(() => {
this._getNodeClientRect('zp-n-refresh-container', false).then((node) => {
if (node) {
let nodeHeight = node[0].height;
this.nShowRefresherReveal = true;
this.nShowRefresherRevealHeight = nodeHeight;
u.delay(() => {
this._nDoRefresherEndAnimation(0, -nodeHeight, false, false);
u.delay(() => {
this._nDoRefresherEndAnimation(nodeHeight, 0);
}, 10)
}, 10)
}
this._reload(false, isFromMounted);
this._doRefresherLoad(false);
});
}, this.pagingLoaded ? 10 : 100)
return;
// #endif
} else {
this._refresherEnd(false, false, false, false);
}
this._reload(false, isFromMounted);
},
//重新加载分页数据
_reload(isClean = false, isFromMounted = false, isUserPullDown = false) {
this.isAddedData = false;
this.insideOfPaging = -1;
this.cacheScrollNodeHeight = -1;
this.pageNo = this.defaultPageNo;
this._cleanRefresherEndTimeout();
!this.privateShowRefresherWhenReload && !isClean && this._startLoading(true);
this.firstPageLoaded = true;
this.isTotalChangeFromAddData = false;
if (!this.isSettingCacheList) {
this.totalData = [];
}
if (!isClean) {
this._emitQuery(this.pageNo, this.defaultPageSize, isUserPullDown ? Enum.QueryFrom.UserPullDown : Enum.QueryFrom.Reload);
let delay = 0;
// #ifdef MP-TOUTIAO
delay = 5;
// #endif
u.delay(this._callMyParentQuery, delay);
if (!isFromMounted && this.autoScrollToTopWhenReload) {
let checkedNRefresherLoading = true;
// #ifdef APP-NVUE
checkedNRefresherLoading = !this.nRefresherLoading;
// #endif
checkedNRefresherLoading && this._scrollToTop(false);
}
}
// #ifdef APP-NVUE
this.$nextTick(() => {
this.nShowBottom = this.realTotalData.length > 0;
})
// #endif
},
//处理服务端返回的数组
_addData(data, success, isLocal) {
this.isAddedData = true;
this.fromEmptyViewReload = false;
this.isTotalChangeFromAddData = true;
this.refresherTriggered = false;
this._endSystemLoadingAndRefresh();
const tempIsUserPullDown = this.isUserPullDown;
if (this.showRefresherUpdateTime && this.isFirstPage) {
u.setRefesrherTime(u.getTime(), this.refresherUpdateTimeKey);
this.$refs.refresh && this.$refs.refresh.updateTime();
}
if (!isLocal && tempIsUserPullDown && this.isFirstPage) {
this.isUserPullDown = false;
}
if (!this.isFirstPage) {
this.listRendering = true;
this.$nextTick(() => {
u.delay(() => this.listRendering = false);
})
} else {
this.listRendering = false;
}
let dataTypeRes = this._checkDataType(data, success, isLocal);
data = dataTypeRes.data;
success = dataTypeRes.success;
let delayTime = c.delayTime;
// #ifdef APP-NVUE
if (this.useChatRecordMode) delayTime = 0;
// #endif
this.loadingForNow = false;
u.delay(() => {
this.pagingLoaded = true;
this.$nextTick(()=>{
!isLocal && this._refresherEnd(delayTime > 0, true, tempIsUserPullDown);
})
})
if (this.isFirstPage) {
this.isLoadFailed = !success;
this.$emit('isLoadFailedChange', this.isLoadFailed);
if (this.finalUseCache && success && (this.cacheMode === Enum.CacheMode.Always ? true : this.isSettingCacheList)) {
this._saveLocalCache(data);
}
}
this.isSettingCacheList = false;
if (success) {
if (!(this.privateConcat === false && this.loadingStatus === Enum.More.NoMore)) {
this.loadingStatus = Enum.More.Default;
}
if (isLocal) {
this.totalLocalPagingList = data;
const localPageNo = this.defaultPageNo;
const localPageSize = this.queryFrom !== Enum.QueryFrom.Refresh ? this.defaultPageSize : this.currentRefreshPageSize;
this._localPagingQueryList(localPageNo, localPageSize, 0, res => {
this.completeByTotal(res, this.totalLocalPagingList.length);
})
} else {
let dataChangeDelayTime = 0;
// #ifdef APP-NVUE
if (this.privateShowRefresherWhenReload && this.finalNvueListIs === 'waterfall') {
dataChangeDelayTime = 150;
}
// #endif
u.delay(() => {
this._currentDataChange(data, this.currentData);
this._callDataPromise(true, this.totalData);
}, dataChangeDelayTime)
}
if (this.isHandlingRefreshToPage) {
this.isHandlingRefreshToPage = false;
this.pageNo = this.defaultPageNo + Math.ceil(data.length / this.pageSize) - 1;
if (data.length % this.pageSize !== 0) {
this.customNoMore = 1;
}
}
} else {
this._currentDataChange(data, this.currentData);
this._callDataPromise(false);
this.loadingStatus = Enum.More.Fail;
this.isHandlingRefreshToPage = false;
if (this.loadingType === Enum.LoadingType.LoadingMore) {
this.pageNo --;
}
}
},
//所有数据改变时调用
_totalDataChange(newVal, oldVal, eventThrow=true) {
if ((!this.isUserReload || !this.autoCleanListWhenReload) && this.firstPageLoaded && !newVal.length && oldVal.length) {
return;
}
this._doCheckScrollViewShouldFullHeight(newVal);
if(!this.realTotalData.length && !newVal.length){
eventThrow = false;
}
this.realTotalData = newVal;
if (eventThrow) {
this.$emit('input', newVal);
// #ifdef VUE3
this.$emit('update:modelValue', newVal);
// #endif
this.$emit('update:list', newVal);
this.$emit('listChange', newVal);
this._callMyParentList(newVal);
}
this.firstPageLoaded = false;
this.isTotalChangeFromAddData = false;
this.$nextTick(() => {
u.delay(()=>{
this._getNodeClientRect('.zp-paging-container-content').then(res => {
res && this.$emit('contentHeightChanged', res[0].height);
});
}, c.delayTime * (this.isIos ? 1 : 3))
// #ifdef APP-NVUE
if (this.useChatRecordMode && this.nIsFirstPageAndNoMore && this.isFirstPage && !this.nFirstPageAndNoMoreChecked) {
this.nFirstPageAndNoMoreChecked = true;
this._scrollToBottom(false);
}
u.delay(() => {
this.nShowBottom = true;
}, c.delayTime * 6, 'nShowBottomDelay');
// #endif
})
},
//当前数据改变时调用
_currentDataChange(newVal, oldVal) {
newVal = [...newVal];
// #ifndef APP-NVUE
this.finalUseVirtualList && this._setCellIndex(newVal, 'bottom');
this.useChatRecordMode && newVal.reverse();
// #endif
if (this.isFirstPage && this.finalConcat) {
this.totalData = [];
}
if (this.customNoMore !== -1) {
if (this.customNoMore === 1 || !newVal.length) {
this.loadingStatus = Enum.More.NoMore;
}
} else {
if (!newVal.length || (newVal.length && newVal.length < this.defaultPageSize)) {
this.loadingStatus = Enum.More.NoMore;
}
}
if (!this.totalData.length) {
if (this.finalConcat) {
// #ifdef APP-NVUE
if (this.useChatRecordMode && this.isFirstPage && this.loadingStatus === Enum.More.NoMore) {
newVal.reverse();
}
// #endif
this.totalData = newVal;
}
if (this.useChatRecordMode) {
// #ifndef APP-NVUE
this.$nextTick(() => {
this._scrollToBottom(false);
})
// #endif
}
} else {
if (this.useChatRecordMode) {
// #ifdef APP-NVUE
this.totalData = [...this.totalData, ...newVal];
// #endif
//#ifndef APP-NVUE
const idIndex = newVal.length;
let idIndexStr = `z-paging-${idIndex}`;
this.totalData = [...newVal, ...this.totalData];
if (this.pageNo !== this.defaultPageNo) {
this.privateScrollWithAnimation = 0;
this.$emit('update:chatIndex', idIndex);
this.$nextTick(() => {
this._scrollIntoView(idIndexStr, 30 + Math.max(0, this.cacheTopHeight), false, () => {
this.$emit('update:chatIndex', 0);
});
})
} else {
this.$nextTick(() => {
this._scrollToBottom(false);
})
}
//#endif
} else {
if (this.finalConcat) {
const currentScrollTop = this.oldScrollTop;
this.totalData = [...this.totalData, ...newVal];
// #ifdef MP-WEIXIN
if (!this.isIos && !this.refresherOnly && !this.usePageScroll && newVal.length) {
this.loadingMoreTimeStamp = u.getTime();
this.$nextTick(() => {
this.scrollToY(currentScrollTop);
})
}
// #endif
} else {
this.totalData = newVal;
}
}
}
this.privateConcat = true;
},
//根据pageNo处理refresh操作
_handleRefreshWithDisPageNo(pageNo) {
if (!this.realTotalData.length) return this.reload();
if (pageNo >= 1) {
this.loading = true;
this.privateConcat = false;
const totalPageSize = pageNo * this.pageSize;
this.currentRefreshPageSize = totalPageSize;
this._emitQuery(this.defaultPageNo, totalPageSize, Enum.QueryFrom.Refresh);
this._callMyParentQuery(this.defaultPageNo, totalPageSize);
}
return new Promise((resolve, reject) => {
this.dataPromiseResultMap.reload = { resolve, reject };
});
},
//本地分页请求
_localPagingQueryList(pageNo, pageSize, localPagingLoadingTime, callback) {
pageNo = Math.max(1, pageNo);
pageSize = Math.max(1, pageSize);
const totalPagingList = [...this.totalLocalPagingList];
const pageNoIndex = (pageNo - 1) * pageSize;
const finalPageNoIndex = Math.min(totalPagingList.length, pageNoIndex + pageSize);
const resultPagingList = totalPagingList.splice(pageNoIndex, finalPageNoIndex - pageNoIndex);
u.delay(() => callback(resultPagingList), localPagingLoadingTime)
},
//存储列表缓存数据
_saveLocalCache(data) {
uni.setStorageSync(this.finalCacheKey, data);
},
//通过缓存数据填充列表数据
_setListByLocalCache() {
this.totalData = uni.getStorageSync(this.finalCacheKey) || [];
this.isSettingCacheList = true;
},
//修改父view的list
_callMyParentList(newVal) {
if (this.autowireListName.length) {
const myParent = u.getParent(this.$parent);
if (myParent && myParent[this.autowireListName]) {
myParent[this.autowireListName] = newVal;
}
}
},
//调用父view的query
_callMyParentQuery(customPageNo = 0, customPageSize = 0) {
if (this.autowireQueryName) {
if (this.myParentQuery === -1) {
const myParent = u.getParent(this.$parent);
if (myParent && myParent[this.autowireQueryName]) {
this.myParentQuery = myParent[this.autowireQueryName];
}
}
if (this.myParentQuery !== -1) {
customPageSize > 0 ? this.myParentQuery(customPageNo, customPageSize) : this.myParentQuery(this.pageNo, this.defaultPageSize);
}
}
},
//emit query事件
_emitQuery(pageNo, pageSize, from){
this.queryFrom = from;
this.requestTimeStamp = u.getTime();
const [lastItem] = this.realTotalData.slice(-1);
this.$emit('query', ...interceptor._handleQuery(pageNo, pageSize, from, lastItem || null));
},
//触发数据改变promise
_callDataPromise(success, totalList) {
for (const key in this.dataPromiseResultMap) {
const obj = this.dataPromiseResultMap[key];
if (!obj) continue;
success ? obj.resolve({ totalList, noMore: this.loadingStatus === Enum.More.NoMore }) : this.callNetworkReject && obj.reject(`z-paging-${key}-error`);
}
},
//检查complete data的类型
_checkDataType(data, success, isLocal) {
const dataType = Object.prototype.toString.call(data);
if (dataType === '[object Boolean]') {
success = data;
data = [];
} else if (dataType !== '[object Array]') {
data = [];
if (dataType !== '[object Undefined]' && dataType !== '[object Null]') {
u.consoleErr(`${isLocal ? 'setLocalPaging' : 'complete'}参数类型不正确,第一个参数类型必须为Array!`);
}
}
return { data, 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');
},
}
}
// [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]滚动到底部加载更多模块
import u from '.././z-paging-utils'
import Enum from '.././z-paging-enum'
export default {
props: {
//自定义底部加载更多样式
loadingMoreCustomStyle: {
type: Object,
default: function() {
return u.gc('loadingMoreCustomStyle', {});
}
},
//自定义底部加载更多文字样式
loadingMoreTitleCustomStyle: {
type: Object,
default: function() {
return u.gc('loadingMoreTitleCustomStyle', {});
}
},
//自定义底部加载更多加载中动画样式
loadingMoreLoadingIconCustomStyle: {
type: Object,
default: function() {
return u.gc('loadingMoreLoadingIconCustomStyle', {});
}
},
//自定义底部加载更多加载中动画图标类型,可选flower或circle,默认为flower
loadingMoreLoadingIconType: {
type: String,
default: u.gc('loadingMoreLoadingIconType', 'flower')
},
//自定义底部加载更多加载中动画图标图片
loadingMoreLoadingIconCustomImage: {
type: String,
default: u.gc('loadingMoreLoadingIconCustomImage', '')
},
//底部加载更多加载中view是否展示旋转动画,默认为是
loadingMoreLoadingAnimated: {
type: Boolean,
default: u.gc('loadingMoreLoadingAnimated', true)
},
//是否启用加载更多数据(含滑动到底部加载更多数据和点击加载更多数据),默认为是
loadingMoreEnabled: {
type: Boolean,
default: u.gc('loadingMoreEnabled', true)
},
//是否启用滑动到底部加载更多数据,默认为是
toBottomLoadingMoreEnabled: {
type: Boolean,
default: u.gc('toBottomLoadingMoreEnabled', true)
},
//滑动到底部状态为默认状态时,以加载中的状态展示,默认为否。若设置为是,可避免滚动到底部看到默认状态然后立刻变为加载中状态的问题,但分页数量未超过一屏时,不会显示【点击加载更多】
loadingMoreDefaultAsLoading: {
type: [Boolean],
default: u.gc('loadingMoreDefaultAsLoading', false)
},
//滑动到底部"默认"文字,默认为【点击加载更多】
loadingMoreDefaultText: {
type: [String, Object],
default: u.gc('loadingMoreDefaultText', null)
},
//滑动到底部"加载中"文字,默认为【正在加载...】
loadingMoreLoadingText: {
type: [String, Object],
default: u.gc('loadingMoreLoadingText', null)
},
//滑动到底部"没有更多"文字,默认为【没有更多了】
loadingMoreNoMoreText: {
type: [String, Object],
default: u.gc('loadingMoreNoMoreText', null)
},
//滑动到底部"加载失败"文字,默认为【加载失败,点击重新加载】
loadingMoreFailText: {
type: [String, Object],
default: u.gc('loadingMoreFailText', null)
},
//当没有更多数据且分页内容未超出z-paging时是否隐藏没有更多数据的view,默认为否
hideNoMoreInside: {
type: Boolean,
default: u.gc('hideNoMoreInside', false)
},
//当没有更多数据且分页数组长度少于这个值时,隐藏没有更多数据的view,默认为0,代表不限制。
hideNoMoreByLimit: {
type: Number,
default: u.gc('hideNoMoreByLimit', 0)
},
//是否显示默认的加载更多text,默认为是
showDefaultLoadingMoreText: {
type: Boolean,
default: u.gc('showDefaultLoadingMoreText', true)
},
//是否显示没有更多数据的view
showLoadingMoreNoMoreView: {
type: Boolean,
default: u.gc('showLoadingMoreNoMoreView', true)
},
//是否显示没有更多数据的分割线,默认为是
showLoadingMoreNoMoreLine: {
type: Boolean,
default: u.gc('showLoadingMoreNoMoreLine', true)
},
//自定义底部没有更多数据的分割线样式
loadingMoreNoMoreLineCustomStyle: {
type: Object,
default: function() {
return u.gc('loadingMoreNoMoreLineCustomStyle', {});
},
},
//当分页未满一屏时,是否自动加载更多,默认为否(nvue无效)
insideMore: {
type: Boolean,
default: u.gc('insideMore', false)
},
//距底部/右边多远时(单位px),触发 scrolltolower 事件,默认为100rpx
lowerThreshold: {
type: [Number, String],
default: u.gc('lowerThreshold', '100rpx')
},
},
data() {
return {
M: Enum.More,
//底部加载更多状态
loadingStatus: Enum.More.Default,
loadingStatusAfterRender: Enum.More.Default,
loadingMoreTimeStamp: 0,
loadingMoreDefaultSlot: null,
showLoadingMore: false,
customNoMore: -1,
}
},
computed: {
zLoadMoreConfig() {
return {
status: this.loadingStatusAfterRender,
defaultAsLoading: this.loadingMoreDefaultAsLoading,
defaultThemeStyle: this.finalLoadingMoreThemeStyle,
customStyle: this.loadingMoreCustomStyle,
titleCustomStyle: this.loadingMoreTitleCustomStyle,
iconCustomStyle: this.loadingMoreLoadingIconCustomStyle,
loadingIconType: this.loadingMoreLoadingIconType,
loadingIconCustomImage: this.loadingMoreLoadingIconCustomImage,
loadingAnimated: this.loadingMoreLoadingAnimated,
showNoMoreLine: this.showLoadingMoreNoMoreLine,
noMoreLineCustomStyle: this.loadingMoreNoMoreLineCustomStyle,
defaultText: this.finalLoadingMoreDefaultText,
loadingText: this.finalLoadingMoreLoadingText,
noMoreText: this.finalLoadingMoreNoMoreText,
failText: this.finalLoadingMoreFailText,
hideContent: !this.loadingMoreDefaultAsLoading && this.listRendering,
};
},
finalLoadingMoreThemeStyle() {
return this.loadingMoreThemeStyle.length ? this.loadingMoreThemeStyle : this.defaultThemeStyle;
},
showLoadingMoreDefault() {
return this._showLoadingMore('Default');
},
showLoadingMoreLoading() {
return this._showLoadingMore('Loading');
},
showLoadingMoreNoMore() {
return this._showLoadingMore('NoMore');
},
showLoadingMoreFail() {
return this._showLoadingMore('Fail');
},
showLoadingMoreCustom() {
return this._showLoadingMore('Custom');
}
},
methods: {
//页面滚动到底部时通知z-paging进行进一步处理
pageReachBottom() {
!this.useChatRecordMode && this._onLoadingMore('toBottom');
},
//手动触发上拉加载更多(非必须,可依据具体需求使用)
doLoadMore(type) {
this._onLoadingMore(type);
},
//通过@scroll事件检测是否滚动到了底部
_checkScrolledToBottom(scrollDiff, checked = false) {
if (this.cacheScrollNodeHeight === -1) {
this._getNodeClientRect('.zp-scroll-view').then((res) => {
if (res) {
const pageScrollNodeHeight = res[0].height;
this.cacheScrollNodeHeight = pageScrollNodeHeight;
if (scrollDiff - pageScrollNodeHeight <= this.finalLowerThreshold) {
this._onLoadingMore('toBottom');
}
}
});
} else {
if (scrollDiff - this.cacheScrollNodeHeight <= this.finalLowerThreshold) {
this._onLoadingMore('toBottom');
} else if (scrollDiff - this.cacheScrollNodeHeight <= 500 && !checked) {
u.delay(() => {
this._getNodeClientRect('.zp-scroll-view', true, true).then((res) => {
this.oldScrollTop = res[0].scrollTop;
const newScrollDiff = res[0].scrollHeight - this.oldScrollTop;
this._checkScrolledToBottom(newScrollDiff, true);
})
}, 150, 'checkScrolledToBottomDelay')
}
}
},
//触发加载更多时调用,from:toBottom-滑动到底部触发;1、click-点击加载更多触发
_onLoadingMore(from = 'click') {
if (this.isIos && from === 'toBottom' && !this.scrollToBottomBounceEnabled && this.scrollEnable) {
this.scrollEnable = false;
this.$nextTick(() => {
this.scrollEnable = true;
})
}
this.$emit('scrolltolower', from);
if (from === 'toBottom' && (!this.toBottomLoadingMoreEnabled || this.useChatRecordMode)) return;
if (this.refresherOnly || !this.loadingMoreEnabled || !(this.loadingStatus === Enum.More.Default || this.loadingStatus === Enum.More.Fail) || this.loading || this.showEmpty) return;
// #ifdef MP-WEIXIN
if (!this.isIos && !this.refresherOnly && !this.usePageScroll) {
const currentTimestamp = u.getTime();
if (this.loadingMoreTimeStamp > 0 && currentTimestamp - this.loadingMoreTimeStamp < 100) {
this.loadingMoreTimeStamp = 0;
return;
}
}
// #endif
this._doLoadingMore();
},
//处理开始加载更多
_doLoadingMore() {
if (this.pageNo >= this.defaultPageNo && this.loadingStatus !== Enum.More.NoMore) {
this.pageNo ++;
this._startLoading(false);
if (this.isLocalPaging) {
this._localPagingQueryList(this.pageNo, this.defaultPageSize, this.localPagingLoadingTime, res => {
this.completeByTotal(res, this.totalLocalPagingList.length);
})
} else {
this._emitQuery(this.pageNo, this.defaultPageSize, Enum.QueryFrom.LoadingMore);
this._callMyParentQuery();
}
this.loadingType = Enum.LoadingType.LoadingMore;
}
},
//(预处理)判断当没有更多数据且分页内容未超出z-paging时是否显示没有更多数据的view
_preCheckShowNoMoreInside(newVal, scrollViewNode, pagingContainerNode) {
if (this.loadingStatus === Enum.More.NoMore && this.hideNoMoreByLimit > 0 && newVal.length) {
this.showLoadingMore = newVal.length > this.hideNoMoreByLimit;
} else if ((this.loadingStatus === Enum.More.NoMore && this.hideNoMoreInside && newVal.length) || (this.insideMore && this.insideOfPaging !== false && newVal.length)) {
this.$nextTick(() => {
this._checkShowNoMoreInside(newVal, scrollViewNode, pagingContainerNode);
})
if (this.insideMore && this.insideOfPaging !== false && newVal.length) {
this.showLoadingMore = newVal.length;
}
} else {
this.showLoadingMore = newVal.length;
}
},
//判断当没有更多数据且分页内容未超出z-paging时是否显示没有更多数据的view
async _checkShowNoMoreInside(totalData, oldScrollViewNode, oldPagingContainerNode) {
try {
const scrollViewNode = oldScrollViewNode || await this._getNodeClientRect('.zp-scroll-view');
if (this.usePageScroll) {
if (scrollViewNode) {
const scrollViewTotalH = scrollViewNode[0].top + scrollViewNode[0].height;
this.insideOfPaging = scrollViewTotalH < this.windowHeight;
if (this.hideNoMoreInside) {
this.showLoadingMore = !this.insideOfPaging;
}
this._updateInsideOfPaging();
}
} else {
const pagingContainerNode = oldPagingContainerNode || await this._getNodeClientRect('.zp-paging-container-content');
const pagingContainerH = pagingContainerNode ? pagingContainerNode[0].height : 0;
const scrollViewH = scrollViewNode ? scrollViewNode[0].height : 0;
this.insideOfPaging = pagingContainerH < scrollViewH;
if (this.hideNoMoreInside) {
this.showLoadingMore = !this.insideOfPaging;
}
this._updateInsideOfPaging();
}
} catch (e) {
this.insideOfPaging = !totalData.length;
if (this.hideNoMoreInside) {
this.showLoadingMore = !this.insideOfPaging;
}
this._updateInsideOfPaging();
}
},
//是否要展示上拉加载更多view
_showLoadingMore(type) {
if (!this.showLoadingMoreWhenReload && (!(this.loadingStatus === Enum.More.Default ? this.nShowBottom : true) || !this.realTotalData.length)) return false;
if (((!this.showLoadingMoreWhenReload || this.isUserPullDown || this.loadingStatus !== Enum.More.Loading) && !this.showLoadingMore) ||
(!this.loadingMoreEnabled && (!this.showLoadingMoreWhenReload || this.isUserPullDown || this.loadingStatus !== Enum.More.Loading)) || this.refresherOnly) {
return false;
}
if (this.useChatRecordMode && type !== 'Loading') return false;
if (!this.zSlots) return false;
if (type === 'Custom') {
return this.showDefaultLoadingMoreText && !(this.loadingStatus === Enum.More.NoMore && !this.showLoadingMoreNoMoreView);
}
const res = this.loadingStatus === Enum.More[type] && this.zSlots[`loadingMore${type}`] && (type === 'NoMore' ? this.showLoadingMoreNoMoreView : true);
if (res) {
// #ifdef APP-NVUE
if (!this.isIos) {
this.nLoadingMoreFixedHeight = false;
}
// #endif
}
return res;
},
}
}
// [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]下拉刷新view模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import Enum from '.././z-paging-enum'
export default {
props: {
//下拉刷新的主题样式,支持black,white,默认black
refresherThemeStyle: {
type: String,
default: u.gc('refresherThemeStyle', '')
},
//自定义下拉刷新中左侧图标的样式
refresherImgStyle: {
type: Object,
default: function() {
return u.gc('refresherImgStyle', {});
}
},
//自定义下拉刷新中右侧状态描述文字的样式
refresherTitleStyle: {
type: Object,
default: function() {
return u.gc('refresherTitleStyle', {});
}
},
//自定义下拉刷新中右侧最后更新时间文字的样式(show-refresher-update-time为true时有效)
refresherUpdateTimeStyle: {
type: Object,
default: function() {
return u.gc('refresherUpdateTimeStyle', {});
}
},
//在微信小程序和QQ小程序中,是否实时监听下拉刷新中进度,默认为否
watchRefresherTouchmove: {
type: Boolean,
default: u.gc('watchRefresherTouchmove', false)
},
//底部加载更多的主题样式,支持black,white,默认black
loadingMoreThemeStyle: {
type: String,
default: u.gc('loadingMoreThemeStyle', '')
},
//是否只使用下拉刷新,设置为true后将关闭mounted自动请求数据、关闭滚动到底部加载更多,强制隐藏空数据图。默认为否
refresherOnly: {
type: Boolean,
default: u.gc('refresherOnly', false)
},
//自定义下拉刷新默认状态下回弹动画时间,单位为毫秒,默认为100毫秒,nvue无效
refresherDefaultDuration: {
type: [Number, String],
default: u.gc('refresherDefaultDuration', 100)
},
//自定义下拉刷新结束以后延迟回弹的时间,单位为毫秒,默认为0
refresherCompleteDelay: {
type: [Number, String],
default: u.gc('refresherCompleteDelay', 0)
},
//自定义下拉刷新结束回弹动画时间,单位为毫秒,默认为300毫秒(refresherEndBounceEnabled为false时,refresherCompleteDuration为设定值的1/3),nvue无效
refresherCompleteDuration: {
type: [Number, String],
default: u.gc('refresherCompleteDuration', 300)
},
//自定义下拉刷新结束状态下是否允许列表滚动,默认为否
refresherCompleteScrollable: {
type: Boolean,
default: u.gc('refresherCompleteScrollable', false)
},
//是否使用自定义的下拉刷新,默认为是,即使用z-paging的下拉刷新。设置为false即代表使用uni scroll-view自带的下拉刷新,h5、App、微信小程序以外的平台不支持uni scroll-view自带的下拉刷新
useCustomRefresher: {
type: Boolean,
default: u.gc('useCustomRefresher', true)
},
//自定义下拉刷新下拉帧率,默认为40,过高可能会出现抖动问题
refresherFps: {
type: [Number, String],
default: u.gc('refresherFps', 40)
},
//自定义下拉刷新允许触发的最大下拉角度,默认为40度,当下拉角度小于设定值时,自定义下拉刷新动画不会被触发
refresherMaxAngle: {
type: [Number, String],
default: u.gc('refresherMaxAngle', 40)
},
//自定义下拉刷新的角度由未达到最大角度变到达到最大角度时,是否继续下拉刷新手势,默认为否
refresherAngleEnableChangeContinued: {
type: Boolean,
default: u.gc('refresherAngleEnableChangeContinued', false)
},
//自定义下拉刷新默认状态下的文字
refresherDefaultText: {
type: [String, Object],
default: u.gc('refresherDefaultText', null)
},
//自定义下拉刷新松手立即刷新状态下的文字
refresherPullingText: {
type: [String, Object],
default: u.gc('refresherPullingText', null)
},
//自定义下拉刷新刷新中状态下的文字
refresherRefreshingText: {
type: [String, Object],
default: u.gc('refresherRefreshingText', null)
},
//自定义下拉刷新刷新结束状态下的文字
refresherCompleteText: {
type: [String, Object],
default: u.gc('refresherCompleteText', null)
},
//自定义下拉刷新默认状态下的图片
refresherDefaultImg: {
type: String,
default: u.gc('refresherDefaultImg', null)
},
//自定义下拉刷新松手立即刷新状态下的图片,默认与refresherDefaultImg一致
refresherPullingImg: {
type: String,
default: u.gc('refresherPullingImg', null)
},
//自定义下拉刷新刷新中状态下的图片
refresherRefreshingImg: {
type: String,
default: u.gc('refresherRefreshingImg', null)
},
//自定义下拉刷新刷新结束状态下的图片
refresherCompleteImg: {
type: String,
default: u.gc('refresherCompleteImg', null)
},
//自定义下拉刷新刷新中状态下是否展示旋转动画
refresherRefreshingAnimated: {
type: Boolean,
default: u.gc('refresherRefreshingAnimated', true)
},
//是否开启自定义下拉刷新刷新结束回弹效果,默认为是
refresherEndBounceEnabled: {
type: Boolean,
default: u.gc('refresherEndBounceEnabled', true)
},
//是否开启自定义下拉刷新,默认为是
refresherEnabled: {
type: Boolean,
default: u.gc('refresherEnabled', true)
},
//设置自定义下拉刷新阈值,默认为80rpx
refresherThreshold: {
type: [Number, String],
default: u.gc('refresherThreshold', '80rpx')
},
//设置系统下拉刷新默认样式,支持设置 black,white,none,none 表示不使用默认样式,默认为black
refresherDefaultStyle: {
type: String,
default: u.gc('refresherDefaultStyle', 'black')
},
//设置自定义下拉刷新区域背景
refresherBackground: {
type: String,
default: u.gc('refresherBackground', 'transparent')
},
//设置固定的自定义下拉刷新区域背景
refresherFixedBackground: {
type: String,
default: u.gc('refresherFixedBackground', 'transparent')
},
//设置固定的自定义下拉刷新区域高度,默认为0
refresherFixedBacHeight: {
type: [Number, String],
default: u.gc('refresherFixedBacHeight', 0)
},
//设置自定义下拉刷新下拉超出阈值后继续下拉位移衰减的比例,范围0-1,值越大代表衰减越多。默认为0.65(nvue无效)
refresherOutRate: {
type: Number,
default: u.gc('refresherOutRate', 0.65)
},
//设置自定义下拉刷新下拉时实际下拉位移与用户下拉距离的比值,默认为0.75,即代表若用户下拉10px,则实际位移为7.5px(nvue无效)
refresherPullRate: {
type: Number,
default: u.gc('refresherPullRate', 0.75)
},
//是否显示最后更新时间,默认为否
showRefresherUpdateTime: {
type: Boolean,
default: u.gc('showRefresherUpdateTime', false)
},
//如果需要区别不同页面的最后更新时间,请为不同页面的z-paging的`refresher-update-time-key`设置不同的字符串
refresherUpdateTimeKey: {
type: String,
default: u.gc('refresherUpdateTimeKey', 'default')
},
//下拉刷新时下拉到“松手立即刷新”状态时是否使手机短振动,默认为否(h5无效)
refresherVibrate: {
type: Boolean,
default: u.gc('refresherVibrate', false)
},
//下拉刷新时是否禁止下拉刷新view跟随用户触摸竖直移动,默认为否。注意此属性只是禁止下拉刷新view移动,其他下拉刷新逻辑依然会正常触发
refresherNoTransform: {
type: Boolean,
default: u.gc('refresherNoTransform', false)
},
//是否开启下拉刷新状态栏占位,适用于隐藏导航栏时,下拉刷新需要避开状态栏高度的情况,默认为否
useRefresherStatusBarPlaceholder: {
type: Boolean,
default: u.gc('useRefresherStatusBarPlaceholder', false)
},
},
data() {
return {
R: Enum.Refresher,
//下拉刷新状态
refresherStatus: Enum.Refresher.Default,
refresherTouchstartY: 0,
lastRefresherTouchmove: null,
refresherReachMaxAngle: true,
refresherTransform: 'translateY(0px)',
refresherTransition: '',
finalRefresherDefaultStyle: 'black',
refresherRevealStackCount: 0,
refresherCompleteTimeout: null,
refresherCompleteSubTimeout: null,
refresherEndTimeout: null,
isTouchmovingTimeout: null,
refresherTriggered: false,
isTouchmoving: false,
isTouchEnded: false,
isUserPullDown: false,
privateRefresherEnabled: -1,
privateShowRefresherWhenReload: false,
customRefresherHeight: -1,
showCustomRefresher: false,
doRefreshAnimateAfter: false,
isRefresherInComplete: false,
pullDownTimeStamp: 0,
moveDis: 0,
oldMoveDis: 0,
currentDis: 0,
oldCurrentMoveDis: 0,
oldRefresherTouchmoveY: 0,
oldTouchDirection: '',
oldEmitedTouchDirection: '',
oldPullingDistance: -1,
refresherThresholdUpdateTag: 0
}
},
watch: {
refresherDefaultStyle: {
handler(newVal) {
if (newVal.length) {
this.finalRefresherDefaultStyle = newVal;
}
},
immediate: true
},
refresherStatus(newVal) {
newVal === Enum.Refresher.Loading && this._cleanRefresherEndTimeout();
this.refresherVibrate && newVal === Enum.Refresher.ReleaseToRefresh && this._doVibrateShort();
this.$emit('refresherStatusChange', newVal);
this.$emit('update:refresherStatus', newVal);
},
refresherEnabled(newVal) {
!newVal && this.endRefresh();
}
},
computed: {
pullDownDisTimeStamp() {
return 1000 / this.refresherFps;
},
finalRefresherEnabled() {
if (this.useChatRecordMode) return false;
if (this.privateRefresherEnabled === -1) return this.refresherEnabled;
return this.privateRefresherEnabled === 1;
},
finalRefresherThreshold() {
let refresherThreshold = this.refresherThreshold;
let idDefault = false;
if (refresherThreshold === '80rpx') {
idDefault = true;
if (this.showRefresherUpdateTime) {
refresherThreshold = '120rpx';
}
}
if (idDefault && this.customRefresherHeight > 0) return this.customRefresherHeight + this.finalRefresherThresholdPlaceholder;
return u.convertToPx(refresherThreshold) + this.finalRefresherThresholdPlaceholder;
},
finalRefresherThresholdPlaceholder() {
return this.useRefresherStatusBarPlaceholder ? this.statusBarHeight : 0;
},
finalRefresherFixedBacHeight() {
return u.convertToPx(this.refresherFixedBacHeight);
},
finalRefresherThemeStyle() {
return this.refresherThemeStyle.length ? this.refresherThemeStyle : this.defaultThemeStyle;
},
finalRefresherOutRate() {
let rate = this.refresherOutRate;
rate = Math.max(0,rate);
rate = Math.min(1,rate);
return rate;
},
finalRefresherPullRate() {
let rate = this.refresherPullRate;
rate = Math.max(0,rate);
return rate;
},
finalRefresherTransform() {
if (this.refresherNoTransform || this.refresherTransform === 'translateY(0px)') return 'none';
return this.refresherTransform;
},
finalShowRefresherWhenReload() {
return this.showRefresherWhenReload || this.privateShowRefresherWhenReload;
},
finalRefresherTriggered() {
if (!(this.finalRefresherEnabled && !this.useCustomRefresher)) return false;
return this.refresherTriggered;
},
showRefresher() {
const showRefresher = this.finalRefresherEnabled && this.useCustomRefresher;
// #ifndef APP-NVUE
this.customRefresherHeight === -1 && showRefresher && this.updateCustomRefresherHeight();
// #endif
return showRefresher;
},
hasTouchmove(){
// #ifdef VUE2
// #ifdef APP-VUE || H5
if (this.$listeners && !this.$listeners.refresherTouchmove) return false;
// #endif
// #ifdef MP-WEIXIN || MP-QQ
return this.watchRefresherTouchmove;
// #endif
return true;
// #endif
return this.watchRefresherTouchmove;
},
},
methods: {
//终止下拉刷新状态
endRefresh() {
this.totalData = this.realTotalData;
this._refresherEnd();
this._endSystemLoadingAndRefresh();
this._handleScrollViewDisableBounce({ bounce: true });
this.$nextTick(() => {
this.refresherTriggered = false;
})
},
handleRefresherStatusChanged(func) {
this.refresherStatusChangedFunc = func;
},
//手动更新自定义下拉刷新view高度
updateCustomRefresherHeight() {
u.delay(() => this.$nextTick(this._updateCustomRefresherHeight));
},
//自定义下拉刷新被触发
_onRefresh(fromScrollView = false,isUserPullDown = true) {
if (fromScrollView && !(this.finalRefresherEnabled && !this.useCustomRefresher)) return;
this.$emit('onRefresh');
this.$emit('Refresh');
// #ifdef APP-NVUE
if (this.loading) {
u.delay(this._nRefresherEnd, 500)
return;
}
// #endif
if (this.loading || this.isRefresherInComplete) return;
this.loadingType = Enum.LoadingType.Refresher;
if (this.nShowRefresherReveal) return;
this.isUserPullDown = isUserPullDown;
this.isUserReload = !isUserPullDown;
this._startLoading(true);
this.refresherTriggered = true;
if(this.reloadWhenRefresh && isUserPullDown){
this.useChatRecordMode ? this._onLoadingMore('click') : this._reload(false, false, isUserPullDown);
}
},
//自定义下拉刷新被复位
_onRestore() {
this.refresherTriggered = 'restore';
this.$emit('onRestore');
this.$emit('Restore');
},
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
//拖拽开始
_refresherTouchstart(e) {
this._handleListTouchstart();
if (this._touchDisabled()) return;
this._handleRefresherTouchstart(u.getTouch(e));
},
// #endif
//进一步处理拖拽开始结果
_handleRefresherTouchstart(touch) {
if (!this.loading && this.isTouchEnded) {
this.isTouchmoving = false;
}
this.loadingType = Enum.LoadingType.Refresher;
this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
this.isTouchEnded = false;
this.refresherTransition = '';
this.refresherTouchstartY = touch.touchY;
this.$emit('refresherTouchstart', this.refresherTouchstartY);
this.lastRefresherTouchmove = touch;
this._cleanRefresherCompleteTimeout();
this._cleanRefresherEndTimeout();
},
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
//拖拽中
_refresherTouchmove(e) {
const currentTimeStamp = u.getTime();
let touch = null;
let refresherTouchmoveY = 0;
if (this.watchTouchDirectionChange) {
touch = u.getTouch(e);
refresherTouchmoveY = touch.touchY;
const direction = refresherTouchmoveY > this.oldRefresherTouchmoveY ? 'top' : 'bottom';
if (direction === this.oldTouchDirection && direction !== this.oldEmitedTouchDirection) {
this._handleTouchDirectionChange({ direction });
this.oldEmitedTouchDirection = direction;
}
this.oldTouchDirection = direction;
this.oldRefresherTouchmoveY = refresherTouchmoveY;
}
if (this.pullDownTimeStamp && currentTimeStamp - this.pullDownTimeStamp <= this.pullDownDisTimeStamp) return;
if (this._touchDisabled()) return;
this.pullDownTimeStamp = Number(currentTimeStamp);
touch = u.getTouch(e);
refresherTouchmoveY = touch.touchY;
let moveDis = refresherTouchmoveY - this.refresherTouchstartY;
if (moveDis < 0) return;
if (this.refresherMaxAngle >= 0 && this.refresherMaxAngle <= 90 && this.lastRefresherTouchmove && this.lastRefresherTouchmove.touchY <= refresherTouchmoveY) {
if (!moveDis && !this.refresherAngleEnableChangeContinued && this.moveDis < 1 && !this.refresherReachMaxAngle) return;
const x = Math.abs(touch.touchX - this.lastRefresherTouchmove.touchX);
const y = Math.abs(refresherTouchmoveY - this.lastRefresherTouchmove.touchY);
const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if ((x || y) && x > 1) {
const angle = Math.asin(y / z) / Math.PI * 180;
if (angle < this.refresherMaxAngle) {
this.lastRefresherTouchmove = touch;
this.refresherReachMaxAngle = false;
return;
}
}
}
moveDis = this._getFinalRefresherMoveDis(moveDis);
this._handleRefresherTouchmove(moveDis, touch);
if (!this.disabledBounce) {
if(this.isIos){
// #ifndef MP-LARK
this._handleScrollViewDisableBounce({ bounce: false });
// #endif
}
this.disabledBounce = true;
}
this._emitTouchmove({ pullingDistance: moveDis, dy: this.moveDis - this.oldMoveDis });
},
// #endif
//进一步处理拖拽中结果
_handleRefresherTouchmove(moveDis, touch) {
this.refresherReachMaxAngle = true;
this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
this.isTouchmoving = true;
this.isTouchEnded = false;
this.refresherStatus = moveDis >= this.finalRefresherThreshold ? Enum.Refresher.ReleaseToRefresh : this.refresherStatus = Enum.Refresher.Default;
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
// this.scrollEnable = false;
this.refresherTransform = `translateY(${moveDis}px)`;
this.lastRefresherTouchmove = touch;
// #endif
this.moveDis = moveDis;
},
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
//拖拽结束
_refresherTouchend(e) {
if (this._touchDisabled() || !this.isTouchmoving) return;
const touch = u.getTouch(e);
let refresherTouchendY = touch.touchY;
let moveDis = refresherTouchendY - this.refresherTouchstartY;
moveDis = this._getFinalRefresherMoveDis(moveDis);
this._handleRefresherTouchend(moveDis);
this._handleScrollViewDisableBounce({bounce: true});
this.disabledBounce = false;
},
// #endif
//进一步处理拖拽结束结果
_handleRefresherTouchend(moveDis) {
// #ifndef APP-PLUS || H5 || MP-WEIXIN
if (!this.isTouchmoving) return;
// #endif
this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
this.refresherReachMaxAngle = true;
this.isTouchEnded = true;
const refresherThreshold = this.finalRefresherThreshold;
if (moveDis >= refresherThreshold && this.refresherStatus === Enum.Refresher.ReleaseToRefresh) {
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.refresherTransform = `translateY(${refresherThreshold}px)`;
this.refresherTransition = 'transform .1s linear';
// #endif
u.delay(() => {
this._emitTouchmove({ pullingDistance: refresherThreshold, dy: this.moveDis - refresherThreshold });
}, 0.1);
this.moveDis = refresherThreshold;
this.refresherStatus = Enum.Refresher.Loading;
this._doRefresherLoad();
} else {
this._refresherEnd();
this.isTouchmovingTimeout = u.delay(() => {
this.isTouchmoving = false;
}, this.refresherDefaultDuration);
}
this.scrollEnable = true;
this.$emit('refresherTouchend', moveDis);
},
//处理列表触摸开始事件
_handleListTouchstart() {
if (this.useChatRecordMode && this.autoHideKeyboardWhenChat) {
uni.hideKeyboard();
this.$emit('hidedKeyboard');
}
},
//处理scroll-view bounce是否生效
_handleScrollViewDisableBounce({ bounce }) {
if (!this.usePageScroll && !this.scrollToTopBounceEnabled && this.wxsScrollTop <= 5) {
// #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.refresherTransition = '';
// #endif
this.scrollEnable = bounce;
}
},
//wxs正在下拉状态改变处理
_handleWxsPullingDownStatusChange(onPullingDown) {
this.wxsOnPullingDown = onPullingDown;
if (onPullingDown && !this.useChatRecordMode) {
this.renderPropScrollTop = 0;
}
},
//wxs正在下拉处理
_handleWxsPullingDown({ moveDis, diffDis }){
this._emitTouchmove({ pullingDistance: moveDis,dy: diffDis });
},
//wxs触摸方向改变
_handleTouchDirectionChange({ direction }) {
this.$emit('touchDirectionChange',direction);
},
//wxs通知更新其props
_handlePropUpdate(){
this.wxsPropType = u.getTime().toString();
},
//下拉刷新结束
_refresherEnd(shouldEndLoadingDelay = true, fromAddData = false, isUserPullDown = false, setLoading = true) {
if (this.loadingType === Enum.LoadingType.Refresher) {
const refresherCompleteDelay = (fromAddData && (isUserPullDown || this.showRefresherWhenReload)) ? this.refresherCompleteDelay : 0;
const refresherStatus = refresherCompleteDelay > 0 ? Enum.Refresher.Complete : Enum.Refresher.Default;
if (this.finalShowRefresherWhenReload) {
const stackCount = this.refresherRevealStackCount;
this.refresherRevealStackCount --;
if (stackCount > 1) return;
}
this._cleanRefresherEndTimeout();
this.refresherEndTimeout = u.delay(() => {
this.refresherStatus = refresherStatus;
}, this.refresherStatus !== Enum.Refresher.Default && refresherStatus === Enum.Refresher.Default ? this.refresherCompleteDuration : 0);
// #ifndef APP-NVUE
if (refresherCompleteDelay > 0) {
this.isRefresherInComplete = true;
}
// #endif
this._cleanRefresherCompleteTimeout();
this.refresherCompleteTimeout = u.delay(() => {
let animateDuration = 1;
const animateType = this.refresherEndBounceEnabled && fromAddData ? 'cubic-bezier(0.19,1.64,0.42,0.72)' : 'linear';
if (fromAddData) {
animateDuration = this.refresherEndBounceEnabled ? this.refresherCompleteDuration / 1000 : this.refresherCompleteDuration / 3000;
}
this.refresherTransition = `transform ${fromAddData ? animateDuration : this.refresherDefaultDuration / 1000}s ${animateType}`;
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.refresherTransform = 'translateY(0px)';
this.currentDis = 0;
// #endif
// #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.wxsPropType = this.refresherTransition + 'end' + u.getTime();
// #endif
// #ifdef APP-NVUE
this._nRefresherEnd();
// #endif
this.moveDis = 0;
// #ifndef APP-NVUE
if (refresherStatus === Enum.Refresher.Complete) {
if (this.refresherCompleteSubTimeout) {
clearTimeout(this.refresherCompleteSubTimeout);
this.refresherCompleteSubTimeout = null;
}
this.refresherCompleteSubTimeout = u.delay(() => {
this.$nextTick(() => {
this.refresherStatus = Enum.Refresher.Default;
this.isRefresherInComplete = false;
})
}, animateDuration * 800);
}
// #endif
this._emitTouchmove({ pullingDistance: 0, dy: this.moveDis });
}, refresherCompleteDelay);
}
if (setLoading) {
u.delay(() => this.loading = false, shouldEndLoadingDelay ? c.delayTime : 0);
isUserPullDown && this._onRestore();
}
},
//模拟用户手动触发下拉刷新
_doRefresherRefreshAnimate() {
this._cleanRefresherCompleteTimeout();
// #ifndef APP-NVUE
const doRefreshAnimateAfter = !this.doRefreshAnimateAfter && (this.finalShowRefresherWhenReload) && this
.customRefresherHeight === -1 && this.refresherThreshold === '80rpx';
if (doRefreshAnimateAfter) {
this.doRefreshAnimateAfter = true;
return;
}
// #endif
this.refresherRevealStackCount ++;
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.refresherTransform = `translateY(${this.finalRefresherThreshold}px)`;
// #endif
// #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.wxsPropType = 'begin' + u.getTime();
// #endif
this.moveDis = this.finalRefresherThreshold;
this.refresherStatus = Enum.Refresher.Loading;
this.isTouchmoving = true;
this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
this._doRefresherLoad(false);
},
//触发下拉刷新
_doRefresherLoad(isUserPullDown = true) {
this._onRefresh(false,isUserPullDown);
this.loading = true;
},
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
//获取处理后的moveDis
_getFinalRefresherMoveDis(moveDis) {
let diffDis = moveDis - this.oldCurrentMoveDis;
this.oldCurrentMoveDis = moveDis;
if (diffDis > 0) {
diffDis = diffDis * this.finalRefresherPullRate;
if (this.currentDis > this.finalRefresherThreshold) {
diffDis = diffDis * (1 - this.finalRefresherOutRate);
}
}
diffDis = diffDis > 100 ? diffDis / 100 : diffDis;
this.currentDis += diffDis;
this.currentDis = Math.max(0, this.currentDis);
return this.currentDis;
},
//判断touch手势是否要触发
_touchDisabled() {
const checkOldScrollTop = this.oldScrollTop > 5;
return this.loading || this.isRefresherInComplete || this.useChatRecordMode || !this.refresherEnabled || !this.useCustomRefresher ||(this.usePageScroll && this.useCustomRefresher && this.pageScrollTop > 10) || (!(this.usePageScroll && this.useCustomRefresher) && checkOldScrollTop);
},
// #endif
//更新自定义下拉刷新view高度
_updateCustomRefresherHeight() {
this._getNodeClientRect('.zp-custom-refresher-slot-view').then((res) => {
this.customRefresherHeight = res ? res[0].height : 0;
this.showCustomRefresher = this.customRefresherHeight > 0;
if (this.doRefreshAnimateAfter) {
this.doRefreshAnimateAfter = false;
this._doRefresherRefreshAnimate();
}
});
},
//发射pullingDown事件
_emitTouchmove(e) {
// #ifndef APP-NVUE
e.viewHeight = this.finalRefresherThreshold;
// #endif
e.rate = e.viewHeight > 0 ? e.pullingDistance / e.viewHeight : 0;
this.hasTouchmove && this.oldPullingDistance !== e.pullingDistance && this.$emit('refresherTouchmove', e);
this.oldPullingDistance = e.pullingDistance;
},
//清除refresherCompleteTimeout
_cleanRefresherCompleteTimeout() {
this.refresherCompleteTimeout = this._cleanTimeout(this.refresherCompleteTimeout);
// #ifdef APP-NVUE
this._nRefresherEnd(false);
// #endif
},
//清除refresherEndTimeout
_cleanRefresherEndTimeout() {
this.refresherEndTimeout = this._cleanTimeout(this.refresherEndTimeout);
},
}
}
// [z-paging]scroll相关模块
import u from '.././z-paging-utils'
import Enum from '.././z-paging-enum'
// #ifdef APP-NVUE
const weexDom = weex.requireModule('dom');
// #endif
export default {
props: {
//使用页面滚动,默认为否,当设置为是时则使用页面的滚动而非此组件内部的scroll-view的滚动,使用页面滚动时z-paging无需设置确定的高度且对于长列表展示性能更高,但配置会略微繁琐
usePageScroll: {
type: Boolean,
default: u.gc('usePageScroll', false)
},
//是否可以滚动,使用内置scroll-view和nvue时有效,默认为是
scrollable: {
type: Boolean,
default: u.gc('scrollable', true)
},
//控制是否出现滚动条,默认为是
showScrollbar: {
type: Boolean,
default: u.gc('showScrollbar', true)
},
//是否允许横向滚动,默认为否
scrollX: {
type: Boolean,
default: u.gc('scrollX', false)
},
//iOS设备上滚动到顶部时是否允许回弹效果,默认为否。关闭回弹效果后可使滚动到顶部与下拉刷新更连贯,但是有吸顶view时滚动到顶部时可能出现抖动。
scrollToTopBounceEnabled: {
type: Boolean,
default: u.gc('scrollToTopBounceEnabled', false)
},
//iOS设备上滚动到底部时是否允许回弹效果,默认为是。
scrollToBottomBounceEnabled: {
type: Boolean,
default: u.gc('scrollToBottomBounceEnabled', true)
},
//在设置滚动条位置时使用动画过渡,默认为否
scrollWithAnimation: {
type: Boolean,
default: u.gc('scrollWithAnimation', false)
},
//值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素
scrollIntoView: {
type: String,
default: u.gc('scrollIntoView', '')
},
},
data() {
return {
scrollTop: 0,
oldScrollTop: 0,
scrollViewStyle: {},
scrollViewContainerStyle: {},
scrollViewInStyle: {},
pageScrollTop: -1,
scrollEnable: true,
privateScrollWithAnimation: -1,
cacheScrollNodeHeight: -1
}
},
watch: {
oldScrollTop(newVal) {
!this.usePageScroll && this._scrollTopChange(newVal,false);
},
pageScrollTop(newVal) {
this.usePageScroll && this._scrollTopChange(newVal,true);
},
usePageScroll: {
handler(newVal) {
this.loaded && this.autoHeight && this._setAutoHeight(!newVal);
// #ifdef H5
if (newVal) {
this.$nextTick(() => {
const mainScrollRef = this.$refs['zp-scroll-view'].$refs.main;
if (mainScrollRef) {
mainScrollRef.style = {};
}
})
}
// #endif
},
immediate: true
},
finalScrollTop(newVal) {
if (!this.useChatRecordMode) {
this.renderPropScrollTop = newVal < 6 ? 0 : 10;
}
},
},
computed: {
finalScrollWithAnimation() {
if (this.privateScrollWithAnimation !== -1) {
const scrollWithAnimation = this.privateScrollWithAnimation === 1;
this.privateScrollWithAnimation = -1;
return scrollWithAnimation;
}
return this.scrollWithAnimation;
},
finalScrollViewStyle() {
if (this.superContentZIndex != 1) {
this.scrollViewStyle['z-index'] = this.superContentZIndex;
this.scrollViewStyle['position'] = 'relative';
}
return this.scrollViewStyle;
},
finalScrollTop() {
return this.usePageScroll ? this.pageScrollTop : this.oldScrollTop;
},
finalIsOldWebView() {
return this.isOldWebView && !this.usePageScroll;
}
},
methods: {
//滚动到顶部,animate为是否展示滚动动画,默认为是
scrollToTop(animate, checkReverse = true) {
// #ifdef APP-NVUE
if (checkReverse && this.useChatRecordMode) {
if (!this.nIsFirstPageAndNoMore) {
this.scrollToBottom(animate, false);
return;
}
}
// #endif
this.$nextTick(() => {
this._scrollToTop(animate, false);
// #ifdef APP-NVUE
if (this.nvueFastScroll && animate) {
u.delay(() => {
this._scrollToTop(false, false);
});
}
// #endif
})
},
//滚动到底部,animate为是否展示滚动动画,默认为是
scrollToBottom(animate, checkReverse = true) {
// #ifdef APP-NVUE
if (checkReverse && this.useChatRecordMode) {
if (!this.nIsFirstPageAndNoMore) {
this.scrollToTop(animate, false);
return;
}
}
// #endif
this.$nextTick(() => {
this._scrollToBottom(animate);
// #ifdef APP-NVUE
if (this.nvueFastScroll && animate) {
u.delay(() => {
this._scrollToBottom(false);
});
}
// #endif
})
},
//滚动到指定view(vue中有效)。sel为需要滚动的view的id值,不包含"#";offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollIntoViewById(sel, offset, animate) {
this._scrollIntoView(sel, offset, animate);
},
//滚动到指定view(vue中有效)。nodeTop为需要滚动的view的top值(通过uni.createSelectorQuery()获取);offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollIntoViewByNodeTop(nodeTop, offset, animate) {
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this._scrollIntoViewByNodeTop(nodeTop, offset, animate);
})
},
//滚动到指定位置(vue中有效)。y为与顶部的距离,单位为px;offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollToY(y, offset, animate) {
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this._scrollToY(y, offset, animate);
})
},
//滚动到指定view(nvue中有效)。index为需要滚动的view的index(第几个);offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollIntoViewByIndex(index, offset, animate) {
this._scrollIntoView(index, offset, animate);
},
//滚动到指定view(nvue中有效)。view为需要滚动的view(通过`this.$refs.xxx`获取),不包含"#";offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollIntoViewByView(view, offset, animate) {
this._scrollIntoView(view, offset, animate);
},
//当使用页面滚动并且自定义下拉刷新时,请在页面的onPageScroll中调用此方法,告知z-paging当前的pageScrollTop,否则会导致在任意位置都可以下拉刷新
updatePageScrollTop(value) {
this.pageScrollTop = value;
},
//当使用页面滚动并且设置了slot="top"时,默认初次加载会自动获取其高度,并使内部容器下移,当slot="top"的view高度动态改变时,在其高度需要更新时调用此方法
updatePageScrollTopHeight() {
this._updatePageScrollTopOrBottomHeight('top');
},
//当使用页面滚动并且设置了slot="bottom"时,默认初次加载会自动获取其高度,并使内部容器下移,当slot="bottom"的view高度动态改变时,在其高度需要更新时调用此方法
updatePageScrollBottomHeight() {
this._updatePageScrollTopOrBottomHeight('bottom');
},
//更新slot="left"和slot="right"宽度,当slot="left"或slot="right"宽度动态改变时调用
updateLeftAndRightWidth() {
if (!this.finalIsOldWebView) return;
this.$nextTick(() => this._updateLeftAndRightWidth(this.scrollViewContainerStyle, 'zp-page'));
},
//更新z-paging内置scroll-view的scrollTop
updateScrollViewScrollTop(scrollTop, animate = true) {
this.privateScrollWithAnimation = animate ? 1 : 0;
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this.scrollTop = scrollTop;
this.oldScrollTop = this.scrollTop;
});
},
//当滚动到顶部时
_onScrollToUpper() {
this.$emit('scrolltoupper');
this.$emit('scrollTopChange', 0);
this.$nextTick(() => {
this.oldScrollTop = 0;
})
this.useChatRecordMode && this.loadingStatus !== Enum.More.NoMore && this._onLoadingMore('click');
},
//当滚动到底部时
_onScrollToLower(e) {
(!e.detail || !e.detail.direction || e.detail.direction === 'bottom') && this._onLoadingMore('toBottom')
},
//滚动到顶部
_scrollToTop(animate = true, isPrivate = true) {
// #ifdef APP-NVUE
const el = this.$refs['zp-n-list-top-tag'];
if (this.usePageScroll) {
this._getNodeClientRect('zp-page-scroll-top', false).then(node => {
const nodeHeight = node ? node[0].height : 0;
weexDom.scrollToElement(el, {
offset: -nodeHeight,
animated: animate
});
});
} else {
if (!this.isIos && this.nvueListIs === 'scroller') {
this._getNodeClientRect('zp-n-refresh-container', false).then(node => {
const nodeHeight = node ? node[0].height : 0;
weexDom.scrollToElement(el, {
offset: -nodeHeight,
animated: animate
});
});
} else {
weexDom.scrollToElement(el, {
offset: 0,
animated: animate
});
}
}
return;
// #endif
if (this.usePageScroll) {
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: 0,
duration: animate ? 100 : 0,
});
});
return;
}
this.privateScrollWithAnimation = animate ? 1 : 0;
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this.scrollTop = 0;
this.oldScrollTop = this.scrollTop;
});
},
//滚动到底部
async _scrollToBottom(animate = true) {
// #ifdef APP-NVUE
const el = this.$refs['zp-n-list-bottom-tag'];
if (el) {
weexDom.scrollToElement(el, {
offset: 0,
animated: animate
});
} else {
u.consoleErr('滚动到底部失败,因为您设置了hideNvueBottomTag为true');
}
return;
// #endif
if (this.usePageScroll) {
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: Number.MAX_VALUE,
duration: animate ? 100 : 0,
});
});
return;
}
try {
this.privateScrollWithAnimation = animate ? 1 : 0;
const pagingContainerNode = await this._getNodeClientRect('.zp-paging-container');
const scrollViewNode = await this._getNodeClientRect('.zp-scroll-view');
const pagingContainerH = pagingContainerNode ? pagingContainerNode[0].height : 0;
const scrollViewH = scrollViewNode ? scrollViewNode[0].height : 0;
if (pagingContainerH > scrollViewH) {
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this.scrollTop = pagingContainerH - scrollViewH + this.virtualPlaceholderTopHeight;
this.oldScrollTop = this.scrollTop;
});
}
} catch (e) {}
},
//滚动到指定view
_scrollIntoView(sel, offset = 0, animate = false, finishCallback) {
try {
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
// #ifdef APP-NVUE
const refs = this.$parent.$refs;
if (!refs) return;
const dataType = Object.prototype.toString.call(sel);
let el = null;
if (dataType === '[object Number]') {
const els = refs[`z-paging-${sel}`];
el = els ? els[0] : null;
} else if (dataType === '[object Array]') {
el = sel[0];
} else {
el = sel;
}
if (el) {
weexDom.scrollToElement(el, {
offset: -offset,
animated: animate
});
} else {
u.consoleErr('在nvue中滚动到指定位置,cell必须设置 :ref="`z-paging-${index}`"');
}
return;
// #endif
this._getNodeClientRect('#' + sel.replace('#', ''), this.$parent).then((node) => {
if (node) {
let nodeTop = node[0].top;
this._scrollIntoViewByNodeTop(nodeTop, offset, animate);
finishCallback && finishCallback();
}
});
});
} catch (e) {}
},
//通过nodeTop滚动到指定view
_scrollIntoViewByNodeTop(nodeTop, offset = 0, animate = false) {
this._scrollToY(nodeTop, offset, animate, true);
},
//滚动到指定位置
_scrollToY(y, offset = 0, animate = false, addScrollTop = false) {
this.privateScrollWithAnimation = animate ? 1 : 0;
if (this.usePageScroll) {
uni.pageScrollTo({
scrollTop: y - offset,
duration: animate ? 100 : 0
});
} else {
if(addScrollTop){
y += this.oldScrollTop;
}
this.scrollTop = y - offset;
this.oldScrollTop = this.scrollTop;
}
},
//scroll-view滚动中
_scroll(e) {
this.$emit('scroll', e);
const scrollTop = e.detail.scrollTop;
// #ifndef APP-NVUE
this.finalUseVirtualList && this._updateVirtualScroll(scrollTop, this.oldScrollTop - scrollTop);
// #endif
this.oldScrollTop = scrollTop;
const scrollDiff = e.detail.scrollHeight - this.oldScrollTop;
!this.isIos && this._checkScrolledToBottom(scrollDiff);
},
//检测scrollView是否要铺满屏幕
_doCheckScrollViewShouldFullHeight(totalData) {
if (this.autoFullHeight && this.usePageScroll && this.isTotalChangeFromAddData) {
// #ifndef APP-NVUE
this.$nextTick(() => {
this._checkScrollViewShouldFullHeight((scrollViewNode, pagingContainerNode) => {
this._preCheckShowNoMoreInside(totalData, scrollViewNode, pagingContainerNode)
});
})
// #endif
// #ifdef APP-NVUE
this._preCheckShowNoMoreInside(totalData)
// #endif
} else {
this._preCheckShowNoMoreInside(totalData)
}
},
//检测z-paging是否要全屏覆盖(当使用页面滚动并且不满全屏时,默认z-paging需要铺满全屏,避免数据过少时内部的empty-view无法正确展示)
async _checkScrollViewShouldFullHeight(callback) {
try {
const scrollViewNode = await this._getNodeClientRect('.zp-scroll-view');
const pagingContainerNode = await this._getNodeClientRect('.zp-paging-container-content');
if (!scrollViewNode || !pagingContainerNode) return;
const scrollViewHeight = pagingContainerNode[0].height;
const scrollViewTop = scrollViewNode[0].top;
if (this.isAddedData && scrollViewHeight + scrollViewTop <= this.windowHeight) {
this._setAutoHeight(true, scrollViewNode);
callback(scrollViewNode, pagingContainerNode);
} else {
this._setAutoHeight(false);
callback(null, null);
}
} catch (e) {
callback(null, null);
}
},
//scrollTop改变时触发
_scrollTopChange(newVal, isPageScrollTop){
this.$emit('scrollTopChange', newVal);
this.$emit('update:scrollTop', newVal);
this._checkShouldShowBackToTop(newVal);
const scrollTop = this.isIos ? (newVal > 5 ? 6 : 0) : (newVal > 105 ? 106 : (newVal > 5 ? 6 : 0));
if (isPageScrollTop && this.wxsPageScrollTop !== scrollTop) {
this.wxsPageScrollTop = scrollTop;
} else if (!isPageScrollTop && this.wxsScrollTop !== scrollTop) {
this.wxsScrollTop = scrollTop;
if (scrollTop > 6) {
this.scrollEnable = true;
}
}
},
//更新使用页面滚动时slot="top"或"bottom"插入view的高度
_updatePageScrollTopOrBottomHeight(type) {
// #ifndef APP-NVUE
if (!this.usePageScroll) return;
// #endif
this._doCheckScrollViewShouldFullHeight(this.realTotalData);
const node = `.zp-page-${type}`;
const marginText = `margin${type.slice(0,1).toUpperCase() + type.slice(1)}`;
let safeAreaInsetBottomAdd = this.safeAreaInsetBottom;
this.$nextTick(() => {
let delayTime = 0;
// #ifdef MP-BAIDU || APP-NVUE
delayTime = 50;
// #endif
u.delay(() => {
this._getNodeClientRect(node).then((res) => {
if (res) {
let pageScrollNodeHeight = res[0].height;
if (type === 'bottom') {
if (safeAreaInsetBottomAdd) {
pageScrollNodeHeight += this.safeAreaBottom;
}
} else {
this.cacheTopHeight = pageScrollNodeHeight;
}
this.$set(this.scrollViewStyle, marginText, `${pageScrollNodeHeight}px`);
} else if (safeAreaInsetBottomAdd) {
this.$set(this.scrollViewStyle, marginText, `${this.safeAreaBottom}px`);
}
});
}, delayTime)
})
},
}
}
// [z-paging]虚拟列表模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import Enum from '.././z-paging-enum'
export default {
props: {
//是否使用虚拟列表,默认为否
useVirtualList: {
type: Boolean,
default: u.gc('useVirtualList', false)
},
//在使用虚拟列表时,是否使用兼容模式,默认为否
useCompatibilityMode: {
type: Boolean,
default: u.gc('useCompatibilityMode', false)
},
//使用兼容模式时传递的附加数据
extraData: {
type: Object,
default: function() {
return u.gc('extraData', {});
}
},
//是否在z-paging内部循环渲染列表(内置列表),默认为否。若use-virtual-list为true,则此项恒为true
useInnerList: {
type: Boolean,
default: u.gc('useInnerList', false)
},
//强制关闭inner-list,默认为false,如果为true将强制关闭innerList,适用于开启了虚拟列表后需要强制关闭inner-list的情况
forceCloseInnerList: {
type: Boolean,
default: u.gc('forceCloseInnerList', false)
},
//内置列表cell的key名称,仅nvue有效,在nvue中开启use-inner-list时必须填此项
cellKeyName: {
type: String,
default: u.gc('cellKeyName', '')
},
//innerList样式
innerListStyle: {
type: Object,
default: function() {
return u.gc('innerListStyle', {});
}
},
//innerCell样式
innerCellStyle: {
type: Object,
default: function() {
return u.gc('innerCellStyle', {});
}
},
//预加载的列表可视范围(列表高度)页数,默认为12,即预加载当前页及上下各12页的cell。此数值越大,则虚拟列表中加载的dom越多,内存消耗越大(会维持在一个稳定值),但增加预加载页面数量可缓解快速滚动短暂白屏问题
preloadPage: {
type: [Number, String],
default: u.gc('preloadPage', 12),
validator: (value) => {
if (value <= 0) u.consoleErr('preload-page必须大于0!');
return value > 0;
}
},
//虚拟列表cell高度模式,默认为fixed,也就是每个cell高度完全相同,将以第一个cell高度为准进行计算。可选值【dynamic】,即代表高度是动态非固定的,【dynamic】性能低于【fixed】。
cellHeightMode: {
type: String,
default: u.gc('cellHeightMode', Enum.CellHeightMode.Fixed)
},
//虚拟列表列数,默认为1。常用于每行有多列的情况,例如每行有2列数据,需要将此值设置为2
virtualListCol: {
type: [Number, String],
default: u.gc('virtualListCol', 1)
},
//虚拟列表scroll取样帧率,默认为80,过低容易出现白屏问题,过高容易出现卡顿问题
virtualScrollFps: {
type: [Number, String],
default: u.gc('virtualScrollFps', 80)
},
},
data() {
return {
virtualListKey: u.getInstanceId(),
virtualPageHeight: 0,
virtualCellHeight: 0,
virtualScrollTimeStamp: 0,
virtualList: [],
virtualPlaceholderTopHeight: 0,
virtualPlaceholderBottomHeight: 0,
virtualTopRangeIndex: 0,
virtualBottomRangeIndex: 0,
lastVirtualTopRangeIndex: 0,
lastVirtualBottomRangeIndex: 0,
virtualItemInsertedCount: 0,
virtualHeightCacheList: [],
getCellHeightRetryCount: {
fixed: 0,
dynamic: 0
},
pagingOrgTop: -1,
updateVirtualListFromDataChange: false
}
},
watch: {
realTotalData(newVal) {
// #ifndef APP-NVUE
if (this.finalUseVirtualList) {
this.updateVirtualListFromDataChange = true;
this.$nextTick(() => {
this.getCellHeightRetryCount.fixed = 0;
!newVal.length && this._resetDynamicListState(!this.isUserPullDown);
newVal.length && this.cellHeightMode === Enum.CellHeightMode.Fixed && this.isFirstPage && this._updateFixedCellHeight();
this._updateVirtualScroll(this.oldScrollTop);
})
}
// #endif
},
virtualList(newVal){
this.$emit('update:virtualList', newVal);
this.$emit('virtualListChange', newVal);
}
},
computed: {
virtualCellIndexKey() {
return c.listCellIndexKey;
},
finalUseVirtualList() {
if (this.useVirtualList && this.usePageScroll){
u.consoleErr('使用页面滚动时,开启虚拟列表无效!');
}
return this.useVirtualList && !this.usePageScroll;
},
finalUseInnerList() {
return this.useInnerList || (this.finalUseVirtualList && !this.forceCloseInnerList);
},
finalCellKeyName() {
// #ifdef APP-NVUE
if (this.finalUseVirtualList && !this.cellKeyName.length){
u.consoleErr('在nvue中开启use-virtual-list必须设置cell-key-name,否则将可能导致列表渲染错误!');
}
// #endif
return this.cellKeyName;
},
finalVirtualPageHeight(){
return this.virtualPageHeight > 0 ? this.virtualPageHeight : this.windowHeight;
},
virtualRangePageHeight(){
return this.finalVirtualPageHeight * this.preloadPage;
},
virtualScrollDisTimeStamp() {
return 1000 / this.virtualScrollFps;
},
},
methods: {
//在使用动态高度虚拟列表时,若在列表数组中需要插入某个item,需要调用此方法;item:需要插入的item,index:插入的cell位置,若index为2,则插入的item在原list的index=1之后,index从0开始
doInsertVirtualListItem(item, index) {
if (this.cellHeightMode !== Enum.CellHeightMode.Dynamic) return;
this.virtualItemInsertedCount ++;
if (!item || Object.prototype.toString.call(item) !== '[object Object]') {
item = { item };
}
const cellIndexKey = this.virtualCellIndexKey;
item[cellIndexKey] = `custom-${this.virtualItemInsertedCount}`;
item[c.listCellIndexUniqueKey] = `${this.virtualListKey}-${item[cellIndexKey]}`;
this.totalData.splice(index, 0, item);
this.$nextTick(async () => {
let retryCount = 0;
while (retryCount <= 10) {
await u.wait(c.delayTime);
const cellNode = await this._getNodeClientRect(`#zp-id-${item[cellIndexKey]}`, this.finalUseInnerList);
if (!cellNode) {
retryCount ++;
continue;
}
const currentHeight = cellNode ? cellNode[0].height : 0;
const lastHeightCache = this.virtualHeightCacheList[index - 1];
const lastTotalHeight = lastHeightCache ? lastHeightCache.totalHeight : 0;
this.virtualHeightCacheList.splice(index, 0, {
height: currentHeight,
lastTotalHeight,
totalHeight: lastTotalHeight + currentHeight
});
for (let i = index + 1; i < this.virtualHeightCacheList.length; i++) {
const thisNode = this.virtualHeightCacheList[i];
thisNode.lastTotalHeight += currentHeight;
thisNode.totalHeight += currentHeight;
}
this._updateVirtualScroll(this.oldScrollTop);
break;
}
})
},
//在使用动态高度虚拟列表时,手动更新指定cell的缓存高度(当cell高度在初始化之后再次改变时调用);index:需要更新的cell在列表中的位置,从0开始
didUpdateVirtualListCell(index) {
if (this.cellHeightMode !== Enum.CellHeightMode.Dynamic) return;
const currentNode = this.virtualHeightCacheList[index];
this.$nextTick(() => {
this._getNodeClientRect(`#zp-id-${index}`, this.finalUseInnerList).then(cellNode => {
const cellNodeHeight = cellNode ? cellNode[0].height : 0;
const heightDis = cellNodeHeight - currentNode.height;
currentNode.height = cellNodeHeight;
currentNode.totalHeight = currentNode.lastTotalHeight + cellNodeHeight;
for (let i = index + 1; i < this.virtualHeightCacheList.length; i++) {
const thisNode = this.virtualHeightCacheList[i];
thisNode.totalHeight += heightDis;
thisNode.lastTotalHeight += heightDis;
}
});
})
},
//在使用动态高度虚拟列表时,若删除了列表数组中的某个item,需要调用此方法以更新高度缓存数组;index:删除的cell在列表中的位置,从0开始
didDeleteVirtualListCell(index) {
if (this.cellHeightMode !== Enum.CellHeightMode.Dynamic) return;
const currentNode = this.virtualHeightCacheList[index];
for (let i = index + 1; i < this.virtualHeightCacheList.length; i++) {
const thisNode = this.virtualHeightCacheList[i];
thisNode.totalHeight -= currentNode.height;
thisNode.lastTotalHeight -= currentNode.height;
}
this.virtualHeightCacheList.splice(index, 1);
},
//初始化虚拟列表
_virtualListInit() {
this.$nextTick(() => {
u.delay(() => {
this._getNodeClientRect('.zp-scroll-view').then(node => {
if (node) {
this.pagingOrgTop = node[0].top;
this.virtualPageHeight = node[0].height;
}
});
});
})
},
//cellHeightMode为fixed时获取第一个cell高度
_updateFixedCellHeight() {
this.$nextTick(() => {
u.delay(() => {
this._getNodeClientRect(`#zp-id-${0}`,this.finalUseInnerList).then(cellNode => {
if (!cellNode) {
if (this.getCellHeightRetryCount.fixed > 10) return;
this.getCellHeightRetryCount.fixed ++;
this._updateFixedCellHeight();
} else {
this.virtualCellHeight = cellNode[0].height;
this._updateVirtualScroll(this.oldScrollTop);
}
});
}, c.delayTime, 'updateFixedCellHeightDelay');
})
},
//cellHeightMode为dynamic时获取每个cell高度
_updateDynamicCellHeight(list, dataFrom = 'bottom') {
const dataFromTop = dataFrom === 'top';
const heightCacheList = this.virtualHeightCacheList;
const currentCacheList = dataFromTop ? [] : heightCacheList;
let listTotalHeight = 0;
this.$nextTick(() => {
u.delay(async () => {
for (let i = 0; i < list.length; i++) {
const cellNode = await this._getNodeClientRect(`#zp-id-${list[i][this.virtualCellIndexKey]}`, this.finalUseInnerList);
const currentHeight = cellNode ? cellNode[0].height : 0;
if (!cellNode) {
if (this.getCellHeightRetryCount.dynamic <= 10) {
heightCacheList.splice(heightCacheList.length - i, i);
this.getCellHeightRetryCount.dynamic ++;
this._updateDynamicCellHeight(list, dataFrom);
}
return;
}
const lastHeightCache = currentCacheList.length ? currentCacheList.slice(-1)[0] : null;
const lastTotalHeight = lastHeightCache ? lastHeightCache.totalHeight : 0;
currentCacheList.push({
height: currentHeight,
lastTotalHeight,
totalHeight: lastTotalHeight + currentHeight
});
if (dataFromTop) {
listTotalHeight += currentHeight;
}
}
if (dataFromTop && list.length) {
for (let i = 0; i < heightCacheList.length; i++) {
const heightCacheItem = heightCacheList[i];
heightCacheItem.lastTotalHeight += listTotalHeight;
heightCacheItem.totalHeight += listTotalHeight;
}
this.virtualHeightCacheList = currentCacheList.concat(heightCacheList);
}
this._updateVirtualScroll(this.oldScrollTop);
}, c.delayTime, 'updateDynamicCellHeightDelay')
})
},
//设置cellItem的index
_setCellIndex(list, dataFrom = 'bottom') {
let currentItemIndex = 0;
const cellIndexKey = this.virtualCellIndexKey;
if (this.totalData.length) {
if (dataFrom === 'bottom') {
currentItemIndex = this.realTotalData.length;
const lastItem = this.realTotalData.length ? this.realTotalData.slice(-1)[0] : null;
if (lastItem && lastItem[cellIndexKey] !== undefined) {
currentItemIndex = lastItem[cellIndexKey] + 1;
}
} else if (dataFrom === 'top') {
const firstItem = this.realTotalData.length ? this.realTotalData[0] : null;
if (firstItem && firstItem[cellIndexKey] !== undefined) {
currentItemIndex = firstItem[cellIndexKey] - list.length;
}
}
} else {
this._resetDynamicListState();
}
for (let i = 0; i < list.length; i++) {
let item = list[i];
if (!item || Object.prototype.toString.call(item) !== '[object Object]') {
item = { item };
}
item[cellIndexKey] = currentItemIndex + i;
item[c.listCellIndexUniqueKey] = `${this.virtualListKey}-${item[cellIndexKey]}`;
list[i] = item;
}
this.getCellHeightRetryCount.dynamic = 0;
this.cellHeightMode === Enum.CellHeightMode.Dynamic && this._updateDynamicCellHeight(list, dataFrom);
},
//更新scroll滚动
_updateVirtualScroll(scrollTop, scrollDiff = 0) {
const currentTimeStamp = u.getTime();
scrollTop === 0 && this._resetTopRange();
if (scrollTop !== 0 && this.virtualScrollTimeStamp && currentTimeStamp - this.virtualScrollTimeStamp <= this.virtualScrollDisTimeStamp) {
return;
}
this.virtualScrollTimeStamp = currentTimeStamp;
let scrollIndex = 0;
const cellHeightMode = this.cellHeightMode;
if (cellHeightMode === Enum.CellHeightMode.Fixed) {
scrollIndex = parseInt(scrollTop / this.virtualCellHeight) || 0;
this._updateFixedTopRangeIndex(scrollIndex);
this._updateFixedBottomRangeIndex(scrollIndex);
} else if(cellHeightMode === Enum.CellHeightMode.Dynamic) {
const scrollDirection = scrollDiff > 0 ? 'top' : 'bottom';
const rangePageHeight = this.virtualRangePageHeight;
const topRangePageOffset = scrollTop - rangePageHeight;
const bottomRangePageOffset = scrollTop + this.finalVirtualPageHeight + rangePageHeight;
let virtualBottomRangeIndex = 0;
let virtualPlaceholderBottomHeight = 0;
let reachedLimitBottom = false;
const heightCacheList = this.virtualHeightCacheList;
const lastHeightCache = !!heightCacheList ? heightCacheList.slice(-1)[0] : null;
let startTopRangeIndex = this.virtualTopRangeIndex;
if (scrollDirection === 'bottom') {
for (let i = startTopRangeIndex; i < heightCacheList.length; i++){
const heightCacheItem = heightCacheList[i];
if (heightCacheItem && heightCacheItem.totalHeight > topRangePageOffset) {
this.virtualTopRangeIndex = i;
this.virtualPlaceholderTopHeight = heightCacheItem.lastTotalHeight;
break;
}
}
} else {
let topRangeMatched = false;
for (let i = startTopRangeIndex; i >= 0; i--){
const heightCacheItem = heightCacheList[i];
if (heightCacheItem && heightCacheItem.totalHeight < topRangePageOffset) {
this.virtualTopRangeIndex = i;
this.virtualPlaceholderTopHeight = heightCacheItem.lastTotalHeight;
topRangeMatched = true;
break;
}
}
!topRangeMatched && this._resetTopRange();
}
for (let i = this.virtualTopRangeIndex; i < heightCacheList.length; i++){
const heightCacheItem = heightCacheList[i];
if (heightCacheItem && heightCacheItem.totalHeight > bottomRangePageOffset) {
virtualBottomRangeIndex = i;
virtualPlaceholderBottomHeight = lastHeightCache.totalHeight - heightCacheItem.totalHeight;
reachedLimitBottom = true;
break;
}
}
if (!reachedLimitBottom || this.virtualBottomRangeIndex === 0) {
this.virtualBottomRangeIndex = this.realTotalData.length ? this.realTotalData.length - 1 : this.pageSize;
this.virtualPlaceholderBottomHeight = 0;
} else {
this.virtualBottomRangeIndex = virtualBottomRangeIndex;
this.virtualPlaceholderBottomHeight = virtualPlaceholderBottomHeight;
}
this._updateVirtualList();
}
},
//更新fixedCell模式下topRangeIndex&placeholderTopHeight
_updateFixedTopRangeIndex(scrollIndex) {
let virtualTopRangeIndex = this.virtualCellHeight === 0 ? 0 : scrollIndex - (parseInt(this.finalVirtualPageHeight / this.virtualCellHeight) || 1) * this.preloadPage;
virtualTopRangeIndex *= this.virtualListCol;
virtualTopRangeIndex = Math.max(0, virtualTopRangeIndex);
this.virtualTopRangeIndex = virtualTopRangeIndex;
this.virtualPlaceholderTopHeight = (virtualTopRangeIndex / this.virtualListCol) * this.virtualCellHeight;
},
//更新fixedCell模式下bottomRangeIndex&placeholderBottomHeight
_updateFixedBottomRangeIndex(scrollIndex) {
let virtualBottomRangeIndex = this.virtualCellHeight === 0 ? this.pageSize : scrollIndex + (parseInt(this.finalVirtualPageHeight / this.virtualCellHeight) || 1) * (this.preloadPage + 1);
virtualBottomRangeIndex *= this.virtualListCol;
virtualBottomRangeIndex = Math.min(this.realTotalData.length, virtualBottomRangeIndex);
this.virtualBottomRangeIndex = virtualBottomRangeIndex;
this.virtualPlaceholderBottomHeight = (this.realTotalData.length - virtualBottomRangeIndex) * this.virtualCellHeight / this.virtualListCol;
this._updateVirtualList();
},
//更新virtualList
_updateVirtualList() {
const shouldUpdateList = this.updateVirtualListFromDataChange || (this.lastVirtualTopRangeIndex !== this.virtualTopRangeIndex || this.lastVirtualBottomRangeIndex !== this.virtualBottomRangeIndex);
if (shouldUpdateList) {
this.updateVirtualListFromDataChange = false;
this.lastVirtualTopRangeIndex = this.virtualTopRangeIndex;
this.lastVirtualBottomRangeIndex = this.virtualBottomRangeIndex;
this.virtualList = this.realTotalData.slice(this.virtualTopRangeIndex, this.virtualBottomRangeIndex + 1);
}
},
//重置动态cell模式下的高度缓存数据、虚拟列表和滚动状态
_resetDynamicListState(resetVirtualList = false) {
this.virtualHeightCacheList = [];
if (resetVirtualList) {
this.virtualList = [];
}
this.virtualTopRangeIndex = 0;
this.virtualPlaceholderTopHeight = 0;
},
//重置topRangeIndex和placeholderTopHeight
_resetTopRange() {
this.virtualTopRangeIndex = 0;
this.virtualPlaceholderTopHeight = 0;
this._updateVirtualList();
},
//检测虚拟列表当前滚动位置,如发现滚动位置不正确则重新计算虚拟列表相关参数(为解决在App中可能出现的长时间进入后台后打开App白屏的问题)
_checkVirtualListScroll() {
if (this.finalUseVirtualList) {
this.$nextTick(() => {
this._getNodeClientRect('.zp-paging-touch-view').then(node => {
const currentTop = node ? node[0].top : 0;
if (!node || (currentTop === this.pagingOrgTop && this.virtualPlaceholderTopHeight !== 0)) {
this._updateVirtualScroll(0);
}
});
})
}
},
//处理使用内置列表时点击了cell事件
_innerCellClick(item, index) {
this.$emit('innerCellClick', item, index);
}
}
}
// [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.6.2',
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'
}
}
// [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]核心js
import zStatic from './z-paging-static'
import c from './z-paging-constant'
import u from './z-paging-utils'
import zPagingRefresh from '../components/z-paging-refresh'
import zPagingLoadMore from '../components/z-paging-load-more'
import zPagingEmptyView from '../../z-paging-empty-view/z-paging-empty-view'
// modules
import commonLayoutModule from './modules/common-layout'
import dataHandleModule from './modules/data-handle'
import i18nModule from './modules/i18n'
import nvueModule from './modules/nvue'
import emptyModule from './modules/empty'
import refresherModule from './modules/refresher'
import loadMoreModule from './modules/load-more'
import loadingModule from './modules/loading'
import scrollerModule from './modules/scroller'
import backToTopModule from './modules/back-to-top'
import virtualListModule from './modules/virtual-list'
import Enum from './z-paging-enum'
const systemInfo = uni.getSystemInfoSync();
export default {
name: "z-paging",
components: {
zPagingRefresh,
zPagingLoadMore,
zPagingEmptyView
},
mixins: [
commonLayoutModule,
dataHandleModule,
i18nModule,
nvueModule,
emptyModule,
refresherModule,
loadMoreModule,
loadingModule,
scrollerModule,
backToTopModule,
virtualListModule
],
data() {
return {
//--------------静态资源---------------
base64Arrow: zStatic.base64Arrow,
base64Flower: zStatic.base64Flower,
base64BackToTop: zStatic.base64BackToTop,
//-------------全局数据相关--------------
//当前加载类型
loadingType: Enum.LoadingType.Refresher,
requestTimeStamp: 0,
chatRecordLoadingMoreText: '',
wxsPropType: '',
renderPropScrollTop: -1,
checkScrolledToBottomTimeOut: null,
cacheTopHeight: -1,
statusBarHeight: systemInfo.statusBarHeight,
//--------------状态&判断---------------
insideOfPaging: -1,
isLoadFailed: false,
isIos: systemInfo.platform === 'ios',
disabledBounce: false,
fromCompleteEmit: false,
disabledCompleteEmit: false,
pageLaunched: false,
//---------------wxs相关---------------
wxsIsScrollTopInTopRange: true,
wxsScrollTop: 0,
wxsPageScrollTop: 0,
wxsOnPullingDown: false,
};
},
props: {
//调用complete后延迟处理的时间,单位为毫秒,默认0毫秒,优先级高于minDelay
delay: {
type: [Number, String],
default: u.gc('delay', 0),
},
//触发@query后最小延迟处理的时间,单位为毫秒,默认0毫秒,优先级低于delay(假设设置为300毫秒,若分页请求时间小于300毫秒,则在调用complete后延迟[300毫秒-请求时长];若请求时长大于300毫秒,则不延迟),当show-refresher-when-reload为true或reload(true)时,其最小值为400
minDelay: {
type: [Number, String],
default: u.gc('minDelay', 0),
},
//设置z-paging的style,部分平台(如微信小程序)无法直接修改组件的style,可使用此属性代替
pagingStyle: {
type: Object,
default: function() {
return u.gc('pagingStyle', {});
},
},
//z-paging的高度,优先级低于pagingStyle中设置的height;传字符串,如100px、100rpx、100%
height: {
type: String,
default: u.gc('height', '')
},
//z-paging的宽度,优先级低于pagingStyle中设置的width;传字符串,如100px、100rpx、100%
width: {
type: String,
default: u.gc('width', '')
},
//z-paging的背景色,优先级低于pagingStyle中设置的background。传字符串,如"#ffffff"
bgColor: {
type: String,
default: u.gc('bgColor', '')
},
//设置z-paging的容器(插槽的父view)的style
pagingContentStyle: {
type: Object,
default: function() {
return u.gc('pagingContentStyle', {});
},
},
//z-paging是否自动高度,若自动高度则会自动铺满屏幕
autoHeight: {
type: Boolean,
default: u.gc('autoHeight', false)
},
//z-paging是否自动高度时,附加的高度,注意添加单位px或rpx,若需要减少高度,则传负数
autoHeightAddition: {
type: [Number, String],
default: u.gc('autoHeightAddition', '0px')
},
//loading(下拉刷新、上拉加载更多)的主题样式,支持black,white,默认black
defaultThemeStyle: {
type: String,
default: u.gc('defaultThemeStyle', 'black')
},
//z-paging是否使用fixed布局,若使用fixed布局,则z-paging的父view无需固定高度,z-paging高度默认为100%,默认为是(当使用内置scroll-view滚动时有效)
fixed: {
type: Boolean,
default: u.gc('fixed', true)
},
//是否开启底部安全区域适配
safeAreaInsetBottom: {
type: Boolean,
default: u.gc('safeAreaInsetBottom', false)
},
//开启底部安全区域适配后,是否使用placeholder形式实现,默认为否。为否时滚动区域会自动避开底部安全区域,也就是所有滚动内容都不会挡住底部安全区域,若设置为是,则滚动时滚动内容会挡住底部安全区域,但是当滚动到底部时才会避开底部安全区域
useSafeAreaPlaceholder: {
type: Boolean,
default: u.gc('useSafeAreaPlaceholder', false)
},
//slot="top"的view的z-index,默认为99,仅使用页面滚动时有效
topZIndex: {
type: Number,
default: u.gc('topZIndex', 99)
},
//z-paging内容容器父view的z-index,默认为1
superContentZIndex: {
type: Number,
default: u.gc('superContentZIndex', 1)
},
//z-paging内容容器部分的z-index,默认为10
contentZIndex: {
type: Number,
default: u.gc('contentZIndex', 10)
},
//使用页面滚动时,是否在不满屏时自动填充满屏幕,默认为是
autoFullHeight: {
type: Boolean,
default: u.gc('autoFullHeight', true)
},
//是否监听列表触摸方向改变,默认为否
watchTouchDirectionChange: {
type: Boolean,
default: u.gc('watchTouchDirectionChange', false)
},
},
created(){
if (this.createdReload && !this.refresherOnly && this.auto) {
this._startLoading();
this.$nextTick(this._preReload);
}
},
mounted() {
this.wxsPropType = u.getTime().toString();
this.renderJsIgnore;
if (!this.createdReload && !this.refresherOnly && this.auto) {
this.$nextTick(this._preReload);
}
this.finalUseCache && this._setListByLocalCache();
let delay = 0;
// #ifdef H5 || MP
delay = c.delayTime;
// #endif
this.$nextTick(() => {
this.systemInfo = uni.getSystemInfoSync();
!this.usePageScroll && this.autoHeight && this._setAutoHeight();
this.loaded = true;
})
this.updatePageScrollTopHeight();
this.updatePageScrollBottomHeight();
this.updateLeftAndRightWidth();
if (this.finalRefresherEnabled && this.useCustomRefresher) {
this.$nextTick(() => {
this.isTouchmoving = true;
})
}
this._onEmit();
// #ifdef APP-NVUE
if (!this.isIos && !this.useChatRecordMode) {
this.nLoadingMoreFixedHeight = true;
}
this._nUpdateRefresherWidth();
// #endif
// #ifndef APP-NVUE
this.finalUseVirtualList && this._virtualListInit();
// #endif
// #ifndef APP-PLUS
this.$nextTick(() => {
setTimeout(() => {
this._getCssSafeAreaInsetBottom(() => this.safeAreaInsetBottom && this.updatePageScrollBottomHeight());
}, delay)
})
// #endif
},
destroyed() {
this._offEmit();
},
// #ifdef VUE3
unmounted() {
this._offEmit();
},
// #endif
watch: {
defaultThemeStyle: {
handler(newVal) {
if (newVal.length) {
this.finalRefresherDefaultStyle = newVal;
}
},
immediate: true
},
autoHeight(newVal) {
this.loaded && !this.usePageScroll && this._setAutoHeight(newVal);
},
autoHeightAddition(newVal) {
this.loaded && !this.usePageScroll && this.autoHeight && this._setAutoHeight(newVal);
},
},
computed: {
finalPagingStyle() {
const pagingStyle = this.pagingStyle;
if (!this.systemInfo) return pagingStyle;
const { windowTop, windowBottom } = this;
if (!this.usePageScroll && this.fixed) {
if (windowTop && !pagingStyle.top) {
pagingStyle.top = windowTop + 'px';
}
if (windowBottom && !pagingStyle.bottom) {
pagingStyle.bottom = windowBottom + 'px';
}
}
if (this.bgColor.length && !pagingStyle['background']) {
pagingStyle['background'] = this.bgColor;
}
if (this.height.length && !pagingStyle['height']) {
pagingStyle['height'] = this.height;
}
if (this.width.length && !pagingStyle['width']) {
pagingStyle['width'] = this.width;
}
return pagingStyle;
},
finalLowerThreshold() {
return u.convertToPx(this.lowerThreshold);
},
finalPagingContentStyle() {
if (this.contentZIndex != 1) {
this.pagingContentStyle['z-index'] = this.contentZIndex;
this.pagingContentStyle['position'] = 'relative';
}
return this.pagingContentStyle;
},
renderJsIgnore() {
if ((this.usePageScroll && this.useChatRecordMode) || !this.refresherEnabled || !this.useCustomRefresher) {
this.$nextTick(() => {
this.renderPropScrollTop = 10;
})
}
return 0;
},
windowHeight() {
if (!this.systemInfo) return 0;
return this.systemInfo.windowHeight || 0;
},
windowBottom() {
if (!this.systemInfo) return 0;
let windowBottom = this.systemInfo.windowBottom || 0;
if (this.safeAreaInsetBottom && !this.useSafeAreaPlaceholder) {
windowBottom += this.safeAreaBottom;
}
return windowBottom;
},
isIosAndH5() {
// #ifndef H5
return false;
// #endif
return this.isIos;
}
},
methods: {
//当前版本号
getVersion() {
return `z-paging v${c.version}`;
},
//设置nvue List的specialEffects
setSpecialEffects(args) {
this.setListSpecialEffects(args);
},
//与setSpecialEffects等效,兼容旧版本
setListSpecialEffects(args) {
this.nFixFreezing = args && Object.keys(args).length;
if (this.isIos) {
this.privateRefresherEnabled = 0;
}
!this.usePageScroll && this.$refs['zp-n-list'].setSpecialEffects(args);
},
// #ifdef APP-VUE
//当app长时间进入后台后进入前台,因系统内存管理导致app重新加载时,进行一些适配处理
_handlePageLaunch() {
// 首次触发不进行处理,只有进入后台后打开app重新加载时才处理
if (this.pageLaunched) {
// 解决在vue3+ios中,app ReLaunch时顶部下拉刷新展示位置向下偏移的问题
// #ifdef VUE3
this.refresherThresholdUpdateTag = 1;
this.$nextTick(() => {
this.refresherThresholdUpdateTag = 0;
})
// #endif
// 解决使用虚拟列表时,app ReLaunch时白屏问题
this._checkVirtualListScroll();
}
this.pageLaunched = true;
},
// #endif
//使手机发生较短时间的振动(15ms)
_doVibrateShort() {
// #ifndef H5
// #ifdef APP-PLUS
if (this.isIos) {
const UISelectionFeedbackGenerator = plus.ios.importClass('UISelectionFeedbackGenerator');
const feedbackGenerator = new UISelectionFeedbackGenerator();
feedbackGenerator.init();
setTimeout(() => {
feedbackGenerator.selectionChanged();
}, 0)
} else {
plus.device.vibrate(15);
}
// #endif
// #ifndef APP-PLUS
uni.vibrateShort();
// #endif
// #endif
},
//设置z-paging高度
async _setAutoHeight(shouldFullHeight = true, scrollViewNode = null) {
let heightKey = 'min-height';
// #ifndef APP-NVUE
heightKey = 'min-height';
// #endif
try {
if (shouldFullHeight) {
let finalScrollViewNode = scrollViewNode || await this._getNodeClientRect('.zp-scroll-view');
let finalScrollBottomNode = await this._getNodeClientRect('.zp-page-bottom');
if (finalScrollViewNode) {
const scrollViewTop = finalScrollViewNode[0].top;
let scrollViewHeight = this.windowHeight - scrollViewTop;
scrollViewHeight -= finalScrollBottomNode ? finalScrollBottomNode[0].height : 0;
const additionHeight = u.convertToPx(this.autoHeightAddition);
const finalHeight = scrollViewHeight + additionHeight - (this.insideMore ? 1 : 0) + 'px !important';
this.$set(this.scrollViewStyle, heightKey, finalHeight);
this.$set(this.scrollViewInStyle, heightKey, finalHeight);
}
} else {
this.$delete(this.scrollViewStyle, heightKey);
this.$delete(this.scrollViewInStyle, heightKey);
}
} catch (e) {}
},
//触发更新是否超出页面状态
_updateInsideOfPaging() {
this.insideMore && this.insideOfPaging === true && setTimeout(this.doLoadMore, 200)
},
//清除timeout
_cleanTimeout(timeout) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
return timeout;
},
//添加全局emit监听
_onEmit() {
uni.$on(c.errorUpdateKey, () => {
this.loading && this.complete(false).catch(() => {});
})
uni.$on(c.completeUpdateKey, (data) => {
setTimeout(() => {
if (this.loading) {
if (!this.disabledCompleteEmit) {
const type = data.type || 'normal';
const list = data.list || data;
const rule = data.rule;
this.fromCompleteEmit = true;
switch (type){
case 'normal':
this.complete(list);
break;
case 'total':
this.completeByTotal(list, rule);
break;
case 'nomore':
this.completeByNoMore(list, rule);
break;
case 'key':
this.completeByKey(list, rule);
break;
default:
break;
}
} else {
this.disabledCompleteEmit = false;
}
}
}, 1);
})
},
//销毁全局emit和listener监听
_offEmit(){
uni.$off(c.errorUpdateKey);
uni.$off(c.completeUpdateKey);
}
},
};
// [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]公用的静态图片资源
export default {
base64Arrow: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAD1BMVEVHcExRUVFMTExRUVFRUVE9CdWsAAAABHRSTlMAjjrY9ZnUjwAAAQFJREFUWMPt2MsNgzAMgGEEE1B1gKJmAIRYoCH7z9RCXrabh33iYktcIv35EEg5ZBh07pvxJU6MFSPOSRnjnBUjUsaciRUjMsb4xIoRCWNiYsUInzE5sWKEyxiYWDbyefqHx1zIeiYTk7mQYziTYecxHvEJjwmIT3hMQELCYSISEg4TkZj0mYTEpM8kJCU9JiMp6TEZyUmbAUhO2gxAQNJiIAKSFgMRmNQZhMCkziAEJTUGIyipMRjBSZkhCE7KDEFIUmTeGCHJxWz0zXaE0GTCG8ZFtEaS347r/1fe11YyHYVfubxayfjoHmc0YYwmmmiiiSaaaKLJ7ckyz5ve+dw3Xw2emdwm9xSbAAAAAElFTkSuQmCC',
base64ArrowWhite: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAElBMVEVHcEz///////////////////+IGTx/AAAABnRSTlMA/dAkXZOhASU/AAABYElEQVRYw+2YwXLCIBCGsdAHWGbyAKZ4zxi9O017rxLf/1UaWFAgA1m8dcpedNSPf/l/Vh0Ya/Wn6hN0JcGvoCqRM4C8VBFiDwBqqNuJKV0rAnCgy3AUqZE57x0iqTL8Br4U3WBf/YWaIlTKfAcELU/h9w72CSVPa3C3OCDvhpHbRp/s2vq4fHhCeiCl2A3m4Qd71DQR257mFBlMcTlbFnFWzNtHxewYEfSiaLS4el8d8nyhmKJd1CF4eOS0keLMAuSxubLBIeIGQW8YHCFFo7EH9+YDcQt9FMZEswTheaNxTHwHT8SZorJjMrEVwo4Zo0U8HSEyZvJMOg4RjnmmRr8nDYeIz3OMkbfE/QhBo+U9RnZJxjGCRh/WKmHEMWLNkfPKsGh/CWJk1JjG0kcuJggTt34VDP8aWAFhp4nybVb5+9qQhjSkIQ1pSEMa8k+Q5U9rV3dF8MpFBK+/7miVq1/HZ2qmo9D+pAAAAABJRU5ErkJggg==',
base64Flower: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAKlBMVEVHcEzDw8Ovr6+pqamUlJTCwsKenp61tbWxsbGysrLNzc2bm5u5ubmjo6MpovhuAAAACnRSTlMA/P79/sHDhiZS0DxZowAABBBJREFUWMPtl89rE0EUx7ctTXatB3MI1SWnDbUKPUgXqh4ED8Uf7KUVSm3ooVSpSii0Fn/gD4j4o+APiEoVmos9FO2celiqZVgwgaKHPQiCCkv+F99kM7Ozm5kxq1dfD91k9pPve9/3ZjbRNHHok/mKli4eIPNgSuRObuN9SqSEzM20iGnm0yIbqCuV7NSSSIV7uyPM6JMBYdeTOanh/QihJYZsUCSby+VkMj2AvOt0rAeQAwqE3lfKMZVlQCZk1QOCKkkVPadITCfIRNKxfoJI5+0OIFtJx14CMSg1mRSDko7VAfksRQzEbGYqxOJcVTWMCH2I1/IACNW0PWU2M8cmAVHtnH5mM1VRWtwKZjOd5JbF6s1IbaYqaotjNlPHgDAnlAizubTR6ovMYn052g/U5qcmOpi0WL8xTS/3IfSet5m8MEr5ajjF5le6dq/OJpobrdY0t3i9QgefWrxW9/1BLhk0E9m8FeUMhhXal499iD0eQRfDF+ts/tttORRerfp+oV7f4xJj82iUYm1Yzod+ZQEAlS/8mMBwKebVmCVp1f0JLS6zKd17+iwRKTARVg2SHtz3iEbBH+Q+U28zW2Jiza8Tjb1YFoYZMsJyjDqp3M9XBQdSdPLFdxEpvOB37JrHcmR/y9+LgoTlCFGZEa2sc6d4PGlweEa2JSVPoVm+IfGG3ZL037iV9oH+P+Jxc4HGVflNq1M0pivao/EopO4b/ojVCP9GjmiXOeS0DOn1o/iiccT4ORnyvBGF3yUywkQajW4Ti0SGuiy/wVSg/L8w+X/8Q+hvUx8Xd90z4oV5a1i88MbFWHz0WZZ1UrTwBGPX3Rat9AFiXRMRjoMdIdJLEOt2h7jrYOzgOamKZSWSNspOS0X8SAqRYmxRL7sg4eLzYmNehcxh3uoyud/BH2Udux4ywxFTc1xC7Mgf4vMhc5S+kSH3Y7yj+qpwIWSoPTVCOOPVthGx9FbGqrwFw6wSFxJr+17zeKcztt3u+2roAEVgUjDd+AHGuxHy2rZHaa8JMkTHEeyi85ANPO9j9BVuBRD2FY5LDMo/Sz/2hReqGIs/KiFin+CsPsYO/yvM3jL2vE8EbX7/Bf8ejtr2GLN65bioAdgLd8Bis/mD5GmP2qeqyo2ZwQEOtAjRIDH7mBKpUcMoApbZJ5UIxkEwxyMZyMxW/uKFvHCFR3SSmerHyDNQ2dF4JG6zIMpBgLfjSF9x1D6smFcYnGApjmSLICO3ecCDWrQ48geba9DI3STy2i7ax6WIB62fSyIZIiO3GFQqSURp8wCo7GhJBGwuSovJBNjb7kT6FPVnIa9qJ2Ko+l9mefGIdinaMp0yC1URYiwsdfNE45EuA5Cx9EhalfvN5s+UyItm81vaB3p4joniN+SCP7Qc1hblAAAAAElFTkSuQmCC',
base64FlowerWhite: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAElBMVEX///9HcEz///////////////84chYNAAAABnRSTlP/AGzCOYZj5g1nAAACfklEQVRYw+2YTVPDIBCGtza9Jw25a0bvcax30o73OOr//yvma2F3YWlpPTijXNpAHrK8LLALVPFium2vNIFSbwGKTGQA2GUiHcD29yDNy3sMIdUBQl7r2H8mOEVqAHgPkYZUS6Qc2zYhQqtjyDZEximCZwWZLIBeIgYShs2NzxKpSUehYpMJhURGb+O+w5BpMCAREKPnCDHbIY20SzhM5yxziAXpOiBXydrekT9i5XDEq4NIIHHgyU5mRGqviII4mREJJA4QJzMiILwlRJzpKxJKvCBm8OsBBbLux0tsPl4RKYm5aPu6jw1U4mGxEUR9g8M1PcqBEp/WJliNgYOXueBzS4jZSIcgY5lCtevgDSgyzE+rAfuOTQMq0yzvoGH18qju27Mayzs4fPyMziCx81NJa5RNfW7vPYK9KOfDiVkBxFHG8hAj9txuoBuSWORsFfkpBf7xKFLSeaOefEojh5jz22DJEqMP8fUyaKdQx+RnG+yXMpe8Aars8ueR1pVH/bW3FyyvPRw90upLDHwpgBDtg4aUBNkxRLXMAi03IhcZtr1m+FeI/O/JNyDmmL1djLOauSlNflBpW18RQ2bPqXI22MXXEk75KRHTnkPkYbESbdKP2ZFk0r5sIwffAjy1lx+vx7NLjB6/E7Jfv5ERKhzpN0w8IDE8IGFDv5dhz10s7GFiXRZcUeLCEG5P5nDq9k4PFDcoMpE3GY4OuxuCXhmuyNB6k0RsLIAvqp9NE5r8ZCSS8gxnUp7ODdYhZTqxuiJ9uyJJtPmpqJ7wVj+XVieS903iViHziqAhchLEJAyb7jWU647EpUofQ0ziUuXXXhDddtlllSwjgSQu7r4BRWhQqfDPMVwAAAAASUVORK5CYII=',
base64Success: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAElBMVEVRUVFHcExTU1NRUVFRUVFRUVFOSlSUAAAABnRSTlP/AI6+VySB3ZENAAACcElEQVRYw+2YyYKCMAyGI8hdpdxdZu7gcpdZ7jL6/s8yYheSNi0aPdqbwOffpGmaFOYPD3gj4bisN7vddv17N/JVgxn5x12IWgIaWTuO/IE3PseQbwjGPo2cgRmHFLJwdm/X643zwiqOKPPJ1nj3sjEP2iiifZWj5bhopSyGaEO2HX5fbQJzwJ+W7x/jw5ZFjsEU0PMph9xE8i5EqprKALW95eJQURkgzw98uJ/JvwGecR7bIjWWsUgVrrIfFZ2HlLy3sKETD1mmRLRMRhGVssRa0xJkdn3SpJBymBkM8+pSSDXMDNyDaToVHd2fgpNt0sjwiUZO19+jGQ+gQEg9Oq+bufmAVGihomNmjQG7UG3020vrlm7lkFnKFGU3kZ0KGAdmKe821pipQ+qEKcrZeTL2g5FsUks4cStjEZWwXg0b0n4GxmEpkWwIs5VBynjgK7xZaz1/0D7OxkVuLpsY5BQNFyLS84VBjjbg0iL2r2EQHBOxBhikuUOkdxODVF1cxHoWtPPsiyXO455Iv34hssCO8EV4ZIYTjS8SR4qYSHRiTiYQ4ZFbHi0iIhhBTi6dTCgSWRcnw4h4yGTuyTAiOGBIWGoZTgSHJQl+LcOJ4OCnW6yX2bMnJ9pidCOXtkTkTrIGpYuOynAiOF14SamMiOCk5Ke+mq8BcOrrvym8d0zKIQnWT+M1WwOQNO4fFiWb18hhERxJPx2fblbPHHyC41VyiAtKBUFBIih7JMWVoIQTFIr3lKPN80WvoLSWFPC653ioTZA0I0FrQ7qU6asaK0H7JmkSJa2ooOGVtNUsc3j9FYHkIkJy3SG6VHnfXKXGP9t4N9Q4Ye98AAAAAElFTkSuQmCC',
base64SuccessWhite: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAGFBMVEVHcEz///////////////////////////8dS1W+AAAAB3RSTlMAiVYk6KvDHLfaegAAAo1JREFUWMPtWEtzmzAQNhCTq910ytXpiyvxTNOr60zrayepx9d02gnX4sTm7xcEiJX2gdnkGJ1A4tOnfWqXyeR1vMRYzrcPD9v5h5MBl3/Ldvx4cxIg/FWC8X0xjLjalM54uhhCfCrRuJURX0pi3EmIqZV7O59vrRZmguStHL9b7S7ftfLwOtiZDw7AHMtmquAQ12b5Wwbnordm8g9zLLO49qc/m2n6aKnhwPOGZ08hAiNHhheiHae1lOUPGZpQkPKa3q0mOUjaRzSRaGUjpy/mmWSwySSpllcEteBKAT52KEnSbblA51pJEPxBQoiH1FP4E3s5+FJv07h6/ylD6ui7B+9fq/ehrFB98ghec9EoVtyjK8pqCHLmCBOwMWSCeWFNN4MbPAk55NhsvoFHSSVR0k5TCTTEzlUGcqV/nVp7n9oIVkmtaqbAEqEgfdgHJPwsEAyZ9r4VAZXFjpEwyaw3+H2v42KYxKhs1XvY/gSSGv+IHyUSuHXCeZhLAgVI3EjgSGo1Fb3xO0tGGU9S2/KAIbtjxpJASG73qox6w5LUq0cEOa+iIONIWIilQSQ0pPa2jgaRQAgQP7c0mITRWGxpMAmEQFN2NAQJNCV0mI6GIIEO47hlQ0ORQLd0nL+hoUjg1m6I1TRr8uYEAriBHLcVFQ5UEMiBe3XkTBEG04WXlGKGxPnMS305XQPA1Ocn2JiuAZwE66fxnKwBnDTuXxZTMq85lwW6kt5ndLqZPefiU1yvmktcUSooChJF2aMprhQlnKJQ5FxRKkcVRa+itNYU8Io2oVkY14w0NMWYlqft91Bj9VHq+ca3b43BxjWJmla0sfKohlfTVpPN+93L/yLQ/IjQ/O5Q/VR5HdL4D7mlxmjwVdELAAAAAElFTkSuQmCC',
base64Empty: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADIBAMAAABfdrOtAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAbUExURdvc3EdwTMLBwcjIyLSzs/Hx8ff39////19dXXz7IJEAAAAFdFJOU/4A6J9QDyyutAAAB5VJREFUeNrtnM1z4jYYxhUHkR4hdu9eU7Z75Ct7jgPbs9ZZmSuTrUWPmXTA186e+LMrf0uWLMtf2WkHXQgzln88et5XeiVMwPQdGrhCrpAr5Aq5Qv4TkJ07OGQFMLp1B4VYCz+kDblDQhJGeH4eEDLBYdLOHwaDWNBPIeHLYJAJ3meQ83IoCMTHDBKOBoKYGOeM8G0gyD0LObnDQB5ZSCtTNCBfsM9AboaBPLCQcDAIM1zht/dQEkMsd1DjI4hpw2YzMtBJeBbydWpCTJs3YDKGX62YgfGoVwi9KwtZJAzcYHHRm7sYCKD390nQSIoO5JGZIEOYxNoZ4+deISYLyeL5hLHbJ2QK98W0kudMgJe9Qh73odhO+KZHyNYGvgQS9gmJKhUigwSj3iBPUhXxePWmxBqHw0Mej9WQ3qILVjLC177yxNxXQ/7uK+Mn1aNVLsGsBTaWrSAPobYl0aUHt2fIs2Rgz7c9QYL0pSTkSzILLFtAJMH1cidN998T9E0/Sg73/pEEwrgkYRh86wlC949gJsR6EobBcz8hHOVgKYi2m6kZtodIkjEQvF3QjbGpmplB4/lRgJhxgRS2N15iijAvPmByDtCxfQhPJ8J4CR82rgCCBILarScw6X0OcMUyYrFVmbxErl0ZacFIoloOLdJAO42qY+NMDss2kKS8xmiZxcCpFKXWvpRGbQqJp5ixyRfJMmR6x0Fk+z29kmgWDYI5ziFbdug/84HxvduhWhLOJ2StPDQrMJPSjNANklh8QhB7dBO0yTGRwn1fkOk8rbQjiB8Ymww+JuiuN0icmSccK4naLMWYa/euL0+m23GyM8kgAc6sYeL4z04Qa4WjGepcKIliO8EUGSk7d9OGWOsoK31OSdy8TQZ59Y/hWbaV1IVs5/Ed6UzGK4nANAJiyGhRsZPUg2yzLe9hLyiJIyCaDU7udC2uy9pnkKvidlBUEltzFAqxRhBrBZm7HfZnjEQI3boqTsJq15PUDEaKZLgiJYc8OZtCtnM/4G93OFYooXpvdy0guwWWNQkEHl/j7Jw1XRmtlS9HYJkSPjk1IUnyyRqUKQn45NSDlP1mcg9i6En1ZU2IADnEtHF1Q+JwIcS/d5YakPuDUamEShGUHHikAz9oQCaE0CsrpYjDBVkEHQYdyK+EkKPhVErxqh1xbJ/oQf4gEeVsOIEc41WJNAwcd9GBfCZJezXsJhAvH+ImEEIOzlwXgpw5wQ0gH3MIOcsiQAahZuSD69/UQyxcQEggiQARQseVFO/ASAMCgM9gjkHZmhLENzi1AOhA7ullkMWUrfHKfpMiDBHtDIx6yCS6jseEnDUe7zcT6DGCtnrIY3olZw1hrPHkfucIAJa1EDu/lsVEyVmGGA67coKijeogFnMxlEaAV5ghRdDm1kDuuatZTJBGgJdOthIzsvZbDWRRuh6ScgR4EQLgagQvRQIxxQ4sxqcR4GE+c4CkjZQQW9YF89Y4OFAjOCki5KmiDxsBL3PlSJWlAFVogaoIePlYi2ClCJAHRa/cmre5eqTii4uvisqQJxqnip6pNd68DhEvyEs5xIyHBNdh4thCKhU++10kD7Gy1Up1A/o56FKuRJQWSFCuf8dpbisxhqHSKlSSgvG7VTaFKO5TzYD5VMPUxEB2YJNiqq3xYJ0KrroH8mq7xpoXqEZgfgNRUQsDtTVvUOk3sLUKbqrBr7YGvkCkQNC/9SA+vTYtvERrxiKEmcogk4ZqCLUd59MIEiFYHlIoxelCaJWDMmtOPIa80XVLbkb6hzaEwwTcPEmV4AIRlBGNIEmuJBFwLAZoHClJ36J8h+wxihpCqJosAnJrSKwEcQOFAFeWN4RQMYc0Ao4Jhg5gpASzyWcDvjpuDIlTkrGGJEro1rHIjHKR3wJCAj+z5oyi11gJBkXy9QFJIiAu78d+pgSjuWhGN0gUAZAcEncSJf4LRrZ8I94WEmcNCJJqBWYjVbE9bg2JxiyrViBWty6QvO56D8jPVWLA4ZX8dfkxvJJPl8t8aCX+pU/Iz1SCf7lc4OBK0OWfQaKLP0TKjj96VvIp+/BDZjwNKF2ItV2vN7sWStAl87oWkm3dZ+k3lEMoYXe8cT1eq2TOePJDD8KfQdxu6iEPxanUZa4HmZRq3dunGsj3BzFq6yD3wnZNX4n2emI2hXyXQpi6RRZdfSgxHNuxVZBFdyVeBPDmCsiksxKUiDAUEKuzkvRUEs0V08pjVyU2/yqFmF2VZGYop3peitdUiQd1pnrL7qTE01tPzE6eaEKm23dQwh2jNlbiay+/245zl94abw45CzNPyqYQ2++kxHGV1crWzg4A2yvR+BY7wziwnRLN7+O36aA54+ZKGjxZYK3txJpxQyUNn5GwtquII4+ACiWtnvawduu1A3SVtH5uhTvAVSpBG7fDYz6RQ+M6JWjmKm6g+RvTla9UMtspu+s+37VbVCupNqPx43CsNawSb1PbtcmDfQWmUILW7rRXSPHtSq5k5ur0a/hb7DQCUiW3G71ejX/wvV1kSoyNbp8Wvyqn1lCIKvl6gNDkNBYzt0GHdr+Pt9xGl1//ncAVcoVcIVfIFXKFXCFXyP8I8i8SyTW4yTz2lwAAAABJRU5ErkJggg==',
base64Error: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADIBAMAAABfdrOtAAAAJFBMVEVHcEzo6Oibm5ukpKSbm5uampqbm5ubm5u5ubnn5+fm5ub6+vpGpDPdAAAAC3RSTlMA/v4hb+u20dq8aQhnHL4AAATwSURBVHja7ZvNb+JGGMbdjjdVe3NPodzeMhj1ZMUGujkh28B9wKR7iwwBqafWSbPqsbm0uTWtVlrTS9v0Et9yqLQS/1zHNiTZMMB4bO92d+dRUITt8c/PvPPx2h4URUpKSkpKSkpK6m3K6lFN73Q+S/+es3W3fzabTh2Dl0FAXEM+BgrgeXxRsdILTDU9n7J0vz/+EsCYC9KEiSFc06pf5zouqAkjkGWo0OG5Fq6j2IwZ6I4/4DhyTxc20oUjAj5PRTTrwvGAZ9p+ADzlD4RDctCoLhb7JUP87xeLxS3BZUIQ+YNCFi8wRwc4GIqGRK/GkM+5ILV8kFflOmkkkH/LddJ4c05eSSfSyTvrZPEmnCw+UCeuk84QvTSnUF0uCL68fBle/swF+RL1QZ/EpU6gHtOsAM64pnjPwxgTLsjoBM58ODNaAXYBT5QeGdr0KwcEA8He0TkPBLCjUDNHpG4qlg8eTCzXht1FVd1MxTPHj5LTtUiSrKK+7iDf8wBGxk4If3arLv/HF4Tox0A2nlFIGp+CIA+LzSamgbp4TNvZjECtDAjyMcB5HybLO6NxsRA1vmFCNjguNXDGWygbRPXh/B+zn9zPWK5RCkT18QxA57YgAqE+HGS6/tAoD4JO0ts+M2tbyQJpc95a5oI0xXNhCZGQ/x8E0VSCkUZY6Z6CIE/qdO5eL+yPlW6tMMgefPs3o7Bdt8iguJj4DThlZSY/rJ0yB+RraLDK2jAQCHyr4zIhT9mQ7vowygMZjpgQG+CYUV2E1EWqixyzICr8eFFnBB5ba1Y4IIfeKQvysW7ssZrwQLEFmvCBM2U6oRkdI5Wgm1QnO8RUTDlASoiESIiESMh7BDHKhlhu/LbSNUuEqL3lu1p945vO3BArADiaum7vBGDUKQfSIjBykooyrRPQnTIglHFfSagHeqd4iErwa9duEebpckGQjx/VT4v5fC0XhJHAd1mPRvJAVMZiAeQzTpgHYrPKqkGnUEiX+dCoNS4UgniHMTnUS4iESIiE7IS0x+mnVAidglDwVcmQJpy2WQ8VC4UgogfbA1RE4Nuw3UghEBV2rKl7V5ygAJPSY9KGQbP01mVjA5Fa2f1kQN2U3k+M9POWB8gnJUNMZJioWTMzKwOklyxgDrCXVcMMEF90tXM9C2TiCqmfCdIRi/jeewNpyerKok9WkGuzfCdYC+fXRsmBxxpVGG2zY0ZBbieJKvPrDQce3lxppBhIjGFWGkVoxUEoZt0Mukn2XBQH0bTHZpaMIp2sU/6qasU70W6/eHjM09VmYSc6C6Jpvz+orKvVxot8kL3HkMr9IZ9qeZ2o6RrO9mOI9ufdIR9peZ2gNIW31yC/MpyI9ngUDNIsezPks3vIsWDGdYA7cZa9pbqUVeCr/neiaR3U3R4BfXPg75vwb8I/b7HjxChobDZCO+Ny4wuxxaVxPPowcoNnrzPmzGFlX3RJHz2FafbhJ41n8PLx2DCM7KkwQgpqka1DVzKdJNHfJwBe9l/n0eSZFsIPjVSY8xZKZpSXnogwled98wAx3xRcdBNq1f1fhFVdIcL5tvaDolC7XaqaWStEtLOJHkbhlSauMLrma4yHEa03AVUoIUs/M2NQFkchBZiGUPeKonAnqhLOo4hrKf0WTyZ1FcU0Ki0hVrSr+Mucnvya7jYUKSkpKSkpKSmpD0f/AXq+Umj5XnXDAAAAAElFTkSuQmCC',
base64BackToTop: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADIBAMAAABfdrOtAAAAElBMVEVRUVH+/v5HcEyZmZlRUVFRUVGm1ByOAAAABnRSTlPMzADMTZAJBBGsAAAEnElEQVR42t2cS27jMAyGf7/2U+QCQeDsbeQCgZDujaC5/1UmkzaJn+JDFGcw3LdfflKibJkkDnxrL7dbg7sNt6+L4O8OYBM+B0ys+QrGkHZG+OEEQ8g6go8Bx1GIGMdpNOQyIG6XdMgnSPtKhLQDGEZFBgYMkhKFtGBb0EIEjDgFRowoBVaMGAWpMedEfxMiZtwpUsgZCqtlkCNUdpVAWigtCCCDFtLwIWeoreZCWiRYYEKGFEjDg+yRZCUH0iLRAgNyToXUNCRZyMqWhGnUN2IPm3wSlwJ7IUspyCBkIQUZhCykIIeQuRTkEDKXAuM9srrtYbrZN7Y98giZSoFd+t1OxmMITG0dcrSFXFchZ1tIvQZpYWxhBbK3hpQrkMEa0iwh5t4a+QvZvDXyF7J5a+Qv5PPW21/I5623v5DPW29/IaO3Xv5Clrw1y1/Ikrdm+Qs5svw83yNnSJ5BQb4F/F7EIEJSnThGBAXxkFQfLOviQUE8JAUPsosHBfGQfDAtHhREQ1JxIV00KIgmrnRI84S0yAd5BAXxxJUck0f6Qnwr9qmr6xF5xLMjcwn/iudIEAdWnyjkEXlQKZiRVzoqRyLbgeUKKR8Q4alY7cSnoxzSf2ggsqehKr6YVpcXpOd7H93f60cKhOd7Re2LteUF4eLqiVS1mr0ge4io6C2+soaFkJ7MuuuQs1yITEp9hwwKISIpzR2iESKSIoT0rLNwuVHQqoSIpAQJpGce60vIUSdEIuUqgPTsJ5QFZK8UIpBS8iG94GFrDjlrhfCl8CG96Llxmle4kEr6vKWBPIVo9kqDQSRk9/3cWoikcCFPAd33v4dIChPyEvLzBA6RlEYWke4JEUnhKXkLeUEKxRHJFfKCQHGucIW8IdZSRkLeEGMpYyEjiK2UsZARxFTKRMgYYillImQMMZQyFTKB2EmZCplAuFLIHT8TMoWwpQwiIVMIUwqpZP5bp5CCvCTiQKr5f5lCQN+tPCBn2ZvVDFJwIDUP0m1BYAfZYRNSsCB7BqTbhoARePIxtZ9tgwWkoJcwCalmv3MBAemtO4R6dah2HaKQqj8Zvp9sQDjvJ21+SPCBHPJDDk6QITekEV7gqCC19CpKAym9IMfckKv4olMBCeIrWwVEfvkshzQekO9r9P1/ALk+IG1eSPCDiCJfyG+FyU+A6ZCa/piZDinpz7LpkCv5gdkAEshP5emQhv7onw6pGeULyZCSUYiRDAmMkpJkCKs4JhFSq8p8hJBSVbAkhARV6ZUQoisik0FqXTmcDHLVFfbJIEFXoiiCNMpiSxGkVJaNiiBBWQArgTTaUl4JpNQWJUsgQVteXQg+AKkLxQWFGKW+5J2+eVp4S168X3CF1CltCKdTJ8lb84YK2bUBO+wZW0Pqv9nk4tKu49N45NJC5dMM5tLW5tOg59Jq6NM06dL+abFXwr/RkuvTXJwae1abtE/Dt0/ruksTvs84AZ/BCC4jHnyGVfiM3VBQFANEXEah+Ax18RlP4zNox2dkkM/wI58xTn8yDCXGYCDV3W5RGSajtXyGhG1jbpbjzpwGt/0MJft8jqC7iUbQ/QZaxdnKqcIftwAAAABJRU5ErkJggg==',
}
// [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');
}
}
};

Sorry, the diff of this file is not supported yet

<!-- _
____ _ __ __ _ __ _(_)_ __ __ _
|_ /____| '_ \ / _` |/ _` | | '_ \ / _` |
/ /_____| |_) | (_| | (_| | | | | | (_| |
/___| | .__/ \__,_|\__, |_|_| |_|\__, |
|_| |___/ |___/
v2.6.2 (2023-10-31)
by ZXLee
-->
<!-- 文档地址:https://z-paging.zxlee.cn -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群:790460711(已满)、371624008 -->
<template name="z-paging">
<!-- #ifndef APP-NVUE -->
<view :class="{'z-paging-content':true,'z-paging-content-fixed':!usePageScroll&&fixed,'z-paging-content-page':usePageScroll,'z-paging-reached-top':renderPropScrollTop<1}" :style="[finalPagingStyle]">
<!-- #ifndef APP-PLUS -->
<view v-if="cssSafeAreaInsetBottom===-1" class="zp-safe-area-inset-bottom"></view>
<!-- #endif -->
<!-- 顶部固定的slot -->
<slot v-if="!usePageScroll&&zSlots.top" name="top" />
<view class="zp-page-top" @touchmove.stop.prevent v-else-if="usePageScroll&&zSlots.top" :style="[{'top':`${windowTop}px`,'z-index':topZIndex}]">
<slot name="top" />
</view>
<view :class="{'zp-view-super':true,'zp-scroll-view-super':!usePageScroll}" :style="[finalScrollViewStyle]">
<view v-if="zSlots.left" :class="{'zp-page-left':true,'zp-absoulte':finalIsOldWebView}">
<slot name="left" />
</view>
<view :class="{'zp-scroll-view-container':true,'zp-absoulte':finalIsOldWebView}" :style="[scrollViewContainerStyle]">
<scroll-view
ref="zp-scroll-view" :class="{'zp-scroll-view':true,'zp-scroll-view-absolute':!usePageScroll,'zp-scroll-view-hide-scrollbar':!showScrollbar}"
:scroll-top="scrollTop" :scroll-x="scrollX"
:scroll-y="scrollable&&!usePageScroll&&scrollEnable&&(refresherCompleteScrollable?true:refresherStatus!==R.Complete)" :enable-back-to-top="finalEnableBackToTop"
:show-scrollbar="showScrollbar" :scroll-with-animation="finalScrollWithAnimation"
:scroll-into-view="scrollIntoView" :lower-threshold="finalLowerThreshold" :upper-threshold="5"
:refresher-enabled="finalRefresherEnabled&&!useCustomRefresher" :refresher-threshold="finalRefresherThreshold"
:refresher-default-style="finalRefresherDefaultStyle" :refresher-background="refresherBackground"
:refresher-triggered="finalRefresherTriggered" @scroll="_scroll" @scrolltolower="_onScrollToLower"
@scrolltoupper="_onScrollToUpper" @refresherrestore="_onRestore" @refresherrefresh="_onRefresh(true)"
>
<view class="zp-paging-touch-view"
<!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
@touchstart="_refresherTouchstart" @touchmove="_refresherTouchmove" @touchend="_refresherTouchend" @touchcancel="_refresherTouchend"
<!-- #endif -->
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
@touchstart="pagingWxs.touchstart" @touchmove="pagingWxs.touchmove" @touchend="pagingWxs.touchend" @touchcancel="pagingWxs.touchend"
@mousedown="pagingWxs.mousedown" @mousemove="pagingWxs.mousemove" @mouseup="pagingWxs.mouseup" @mouseleave="pagingWxs.mouseleave"
<!-- #endif -->
>
<view v-if="finalRefresherFixedBacHeight>0" class="zp-fixed-bac-view" :style="[{'background': refresherFixedBackground,'height': `${finalRefresherFixedBacHeight}px`}]"></view>
<view class="zp-paging-main" :style="[scrollViewInStyle,{'transform': finalRefresherTransform,'transition': refresherTransition}]"
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
:change:prop="pagingWxs.propObserver" :prop="wxsPropType"
:data-refresherThreshold="finalRefresherThreshold" :data-isIos="isIos"
:data-loading="loading||isRefresherInComplete" :data-useChatRecordMode="useChatRecordMode"
:data-refresherEnabled="refresherEnabled" :data-useCustomRefresher="useCustomRefresher" :data-pageScrollTop="wxsPageScrollTop"
:data-scrollTop="wxsScrollTop" :data-refresherMaxAngle="refresherMaxAngle" :data-refresherNoTransform="refresherNoTransform"
:data-refresherAecc="refresherAngleEnableChangeContinued" :data-usePageScroll="usePageScroll" :data-watchTouchDirectionChange="watchTouchDirectionChange"
:data-oldIsTouchmoving="isTouchmoving" :data-refresherOutRate="finalRefresherOutRate" :data-refresherPullRate="finalRefresherPullRate" :data-hasTouchmove="hasTouchmove"
<!-- #endif -->
<!-- #ifdef APP-VUE || H5 -->
:change:renderPropIsIosAndH5="pagingRenderjs.renderPropIsIosAndH5Change" :renderPropIsIosAndH5="isIosAndH5"
<!-- #endif -->
>
<view v-if="showRefresher" class="zp-custom-refresher-view" :style="[{'margin-top': `-${finalRefresherThreshold+refresherThresholdUpdateTag}px`,'background': refresherBackground,'opacity': isTouchmoving ? 1 : 0}]">
<view class="zp-custom-refresher-container" :style="[{'height': `${finalRefresherThreshold}px`,'background': refresherBackground}]">
<view v-if="useRefresherStatusBarPlaceholder" class="zp-custom-refresher-status-bar-placeholder" :style="[{'height': `${statusBarHeight}px`}]" />
<!-- 下拉刷新view -->
<view class="zp-custom-refresher-slot-view">
<slot v-if="!(zSlots.refresherComplete&&refresherStatus===R.Complete)" :refresherStatus="refresherStatus" name="refresher" />
</view>
<slot v-if="zSlots.refresherComplete&&refresherStatus===R.Complete" name="refresherComplete" />
<z-paging-refresh ref="refresh" v-else-if="!showCustomRefresher" class="zp-custom-refresher-refresh" :style="[{'height': `${finalRefresherThreshold - finalRefresherThresholdPlaceholder}px`}]" :status="refresherStatus"
:defaultThemeStyle="finalRefresherThemeStyle" :defaultText="finalRefresherDefaultText"
:pullingText="finalRefresherPullingText" :refreshingText="finalRefresherRefreshingText" :completeText="finalRefresherCompleteText"
:defaultImg="refresherDefaultImg" :pullingImg="refresherPullingImg" :refreshingImg="refresherRefreshingImg" :completeImg="refresherCompleteImg" :refreshingAnimated="refresherRefreshingAnimated"
:showUpdateTime="showRefresherUpdateTime" :updateTimeKey="refresherUpdateTimeKey" :updateTimeTextMap="finalRefresherUpdateTimeTextMap"
:imgStyle="refresherImgStyle" :titleStyle="refresherTitleStyle" :updateTimeStyle="refresherUpdateTimeStyle" />
</view>
</view>
<view class="zp-paging-container">
<slot v-if="useChatRecordMode&&zSlots.chatLoading&&loadingStatus!==M.NoMore&&realTotalData.length" name="chatLoading" />
<view v-else-if="useChatRecordMode&&loadingStatus!==M.NoMore&&realTotalData.length" class="zp-chat-record-loading-container">
<text v-if="loadingStatus!==M.Loading" @click="_onScrollToUpper"
:class="defaultThemeStyle==='white'?'zp-loading-more-text zp-loading-more-text-white':'zp-loading-more-text zp-loading-more-text-black'">{{chatRecordLoadingMoreText}}</text>
<image v-else :src="base64Flower" class="zp-chat-record-loading-custom-image" />
</view>
<!-- 全屏Loading -->
<slot v-if="showLoading&&zSlots.loading&&!loadingFullFixed" name="loading" />
<!-- 主体内容 -->
<view class="zp-paging-container-content" :style="[{transform:virtualPlaceholderTopHeight>0?`translateY(${virtualPlaceholderTopHeight}px)`:'none'},finalPagingContentStyle]">
<slot />
<!-- 内置列表&虚拟列表 -->
<template v-if="finalUseInnerList">
<slot name="header"/>
<view class="zp-list-container" :style="[innerListStyle]">
<template v-if="finalUseVirtualList">
<view class="zp-list-cell" :style="[innerCellStyle]" :id="`zp-id-${item[virtualCellIndexKey]}`" v-for="(item,index) in virtualList" :key="item['zp_unique_index']" @click="_innerCellClick(item,virtualTopRangeIndex+index)">
<view v-if="useCompatibilityMode">使用兼容模式请在组件源码z-paging.vue第100行中注释这一行,并打开下面一行注释</view>
<!-- <zp-public-virtual-cell v-if="useCompatibilityMode" :extraData="extraData" :item="item" :index="virtualTopRangeIndex+index" /> -->
<slot v-else name="cell" :item="item" :index="virtualTopRangeIndex+index"/>
</view>
</template>
<template v-else>
<view class="zp-list-cell" v-for="(item,index) in realTotalData" :key="index" @click="_innerCellClick(item,index)">
<slot name="cell" :item="item" :index="index"/>
</view>
</template>
</view>
<slot name="footer"/>
</template>
<view v-if="useVirtualList" class="zp-virtual-placeholder" :style="[{height:virtualPlaceholderBottomHeight+'px'}]"/>
<!-- 上拉加载更多view -->
<!-- #ifndef MP-ALIPAY -->
<slot v-if="showLoadingMoreDefault" name="loadingMoreDefault" />
<slot v-else-if="showLoadingMoreLoading" name="loadingMoreLoading" />
<slot v-else-if="showLoadingMoreNoMore" name="loadingMoreNoMore" />
<slot v-else-if="showLoadingMoreFail" name="loadingMoreFail" />
<z-paging-load-more @doClick="_onLoadingMore('click')" v-else-if="showLoadingMoreCustom" :zConfig="zLoadMoreConfig" />
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<slot v-if="loadingStatus===M.Default&&zSlots.loadingMoreDefault&&showLoadingMore&&loadingMoreEnabled&&!useChatRecordMode" name="loadingMoreDefault" />
<slot v-else-if="loadingStatus===M.Loading&&zSlots.loadingMoreLoading&&showLoadingMore&&loadingMoreEnabled" name="loadingMoreLoading" />
<slot v-else-if="loadingStatus===M.NoMore&&zSlots.loadingMoreNoMore&&showLoadingMore&&showLoadingMoreNoMoreView&&loadingMoreEnabled&&!useChatRecordMode" name="loadingMoreNoMore" />
<slot v-else-if="loadingStatus===M.Fail&&zSlots.loadingMoreFail&&showLoadingMore&&loadingMoreEnabled&&!useChatRecordMode" name="loadingMoreFail" />
<z-paging-load-more @doClick="_onLoadingMore('click')" v-else-if="showLoadingMore&&showDefaultLoadingMoreText&&!(loadingStatus===M.NoMore&&!showLoadingMoreNoMoreView)&&loadingMoreEnabled&&!useChatRecordMode" :zConfig="zLoadMoreConfig" />
<!-- #endif -->
<view v-if="safeAreaInsetBottom&&useSafeAreaPlaceholder" class="zp-safe-area-placeholder" :style="[{height:safeAreaBottom+'px'}]" />
</view>
<!-- 空数据图 -->
<view v-if="showEmpty" :class="{'zp-empty-view':true,'zp-empty-view-center':emptyViewCenter}" :style="[emptyViewSuperStyle]">
<slot v-if="zSlots.empty" name="empty" :isLoadFailed="isLoadFailed"/>
<z-paging-empty-view v-else :emptyViewImg="finalEmptyViewImg" :emptyViewText="finalEmptyViewText" :showEmptyViewReload="finalShowEmptyViewReload"
:emptyViewReloadText="finalEmptyViewReloadText" :isLoadFailed="isLoadFailed" :emptyViewStyle="emptyViewStyle" :emptyViewTitleStyle="emptyViewTitleStyle"
:emptyViewImgStyle="emptyViewImgStyle" :emptyViewReloadStyle="emptyViewReloadStyle" :emptyViewZIndex="emptyViewZIndex" :emptyViewFixed="emptyViewFixed"
@reload="_emptyViewReload" @viewClick="_emptyViewClick" />
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<view v-if="zSlots.right" :class="{'zp-page-right':true,'zp-absoulte zp-right':finalIsOldWebView}">
<slot name="right" />
</view>
</view>
<!-- 底部固定的slot -->
<slot v-if="!usePageScroll&&zSlots.bottom" name="bottom" />
<view class="zp-page-bottom" @touchmove.stop.prevent v-else-if="usePageScroll&&zSlots.bottom" :style="[{'bottom': `${windowBottom}px`}]">
<slot name="bottom" />
</view>
<!-- 点击返回顶部view -->
<view v-if="showBackToTopClass" :class="backToTopClass" :style="[finalBackToTopStyle]" @click.stop="_backToTopClick">
<slot v-if="zSlots.backToTop" name="backToTop" />
<image v-else class="zp-back-to-top-img" :src="backToTopImg.length?backToTopImg:base64BackToTop" />
</view>
<!-- 全屏Loading(铺满z-paging并固定) -->
<view v-if="showLoading&&zSlots.loading&&loadingFullFixed" class="zp-loading-fixed">
<slot name="loading" />
</view>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<component :is="finalNvueSuperListIs" :style="[finalPagingStyle]" :class="{'z-paging-content-fixed':fixed&&!usePageScroll}" :scrollable="false">
<!-- 顶部固定的slot -->
<view ref="zp-page-top" v-if="zSlots.top" :class="{'zp-page-top':usePageScroll}" :style="[usePageScroll?{'top':`${windowTop}px`,'z-index':topZIndex}:{}]">
<slot name="top" />
</view>
<component :is="finalNvueSuperListIs" class="zp-n-list-container" :scrollable="false">
<view v-if="zSlots.left" class="zp-page-left">
<slot name="left" />
</view>
<component :is="finalNvueListIs" ref="zp-n-list" :id="nvueListId" :style="[{'flex': 1,'top':isIos?'0px':'-1px'},usePageScroll?scrollViewStyle:{},nChatRecordRotateStyle]" :alwaysScrollableVertical="true"
:fixFreezing="nFixFreezing" :show-scrollbar="showScrollbar&&!useChatRecordMode" :loadmoreoffset="finalLowerThreshold" :enable-back-to-top="enableBackToTop"
:scrollable="scrollable&&scrollEnable&&(refresherCompleteScrollable?true:refresherStatus!==R.Complete)" :bounce="nvueBounce" :column-count="nWaterfallColumnCount" :column-width="nWaterfallColumnWidth"
:column-gap="nWaterfallColumnGap" :left-gap="nWaterfallLeftGap" :right-gap="nWaterfallRightGap" :pagingEnabled="nvuePagingEnabled" :offset-accuracy="offsetAccuracy"
@loadmore="_nOnLoadmore" @scroll="_nOnScroll">
<refresh v-if="(zSlots.top?cacheTopHeight!==-1:true)&&finalNvueRefresherEnabled" class="zp-n-refresh" :style="[nvueRefresherStyle]" :display="nRefresherLoading?'show':'hide'" @refresh="_nOnRrefresh" @pullingdown="_nOnPullingdown">
<view ref="zp-n-refresh-container" class="zp-n-refresh-container" :style="[{background:refresherBackground,width:nRefresherWidth}]" id="zp-n-refresh-container">
<view v-if="useRefresherStatusBarPlaceholder" class="zp-custom-refresher-status-bar-placeholder" :style="[{'height': `${statusBarHeight}px`}]" />
<!-- 下拉刷新view -->
<slot v-if="zSlots.refresherComplete&&refresherStatus===R.Complete" name="refresherComplete" />
<slot v-else-if="(nScopedSlots?nScopedSlots:zSlots).refresher" :refresherStatus="refresherStatus" name="refresher" />
<z-paging-refresh ref="refresh" v-else :status="refresherStatus" :defaultThemeStyle="finalRefresherThemeStyle"
:defaultText="finalRefresherDefaultText" :pullingText="finalRefresherPullingText" :refreshingText="finalRefresherRefreshingText" :completeText="finalRefresherCompleteText"
:defaultImg="refresherDefaultImg" :pullingImg="refresherPullingImg" :refreshingImg="refresherRefreshingImg" :completeImg="refresherCompleteImg" :refreshingAnimated="refresherRefreshingAnimated"
:showUpdateTime="showRefresherUpdateTime" :updateTimeKey="refresherUpdateTimeKey" :updateTimeTextMap="finalRefresherUpdateTimeTextMap"
:imgStyle="refresherImgStyle" :titleStyle="refresherTitleStyle" :updateTimeStyle="refresherUpdateTimeStyle" />
</view>
</refresh>
<component :is="nViewIs" v-if="isIos&&!useChatRecordMode?oldScrollTop>10:true" ref="zp-n-list-top-tag" class="zp-n-list-top-tag" style="margin-top: -1rpx;" :style="[{height:finalNvueRefresherEnabled?'0px':'1px'}]"></component>
<component :is="nViewIs" v-if="nShowRefresherReveal" ref="zp-n-list-refresher-reveal" :style="[{transform:`translateY(-${nShowRefresherRevealHeight}px)`},{background:refresherBackground}]">
<view v-if="useRefresherStatusBarPlaceholder" class="zp-custom-refresher-status-bar-placeholder" :style="[{'height': `${statusBarHeight}px`}]" />
<!-- 下拉刷新view -->
<slot v-if="zSlots.refresherComplete&&refresherStatus===R.Complete" name="refresherComplete" />
<slot v-else-if="(nScopedSlots?nScopedSlots:$slots).refresher" :refresherStatus="R.Loading" name="refresher" />
<z-paging-refresh ref="refresh" v-else :status="R.Loading" :defaultThemeStyle="finalRefresherThemeStyle"
:defaultText="finalRefresherDefaultText" :pullingText="finalRefresherPullingText" :refreshingText="finalRefresherRefreshingText" :completeText="finalRefresherCompleteText"
:defaultImg="refresherDefaultImg" :pullingImg="refresherPullingImg" :refreshingImg="refresherRefreshingImg" :completeImg="refresherCompleteImg" :refreshingAnimated="refresherRefreshingAnimated"
:showUpdateTime="showRefresherUpdateTime" :updateTimeKey="refresherUpdateTimeKey" :updateTimeTextMap="finalRefresherUpdateTimeTextMap"
:imgStyle="refresherImgStyle" :titleStyle="refresherTitleStyle" :updateTimeStyle="refresherUpdateTimeStyle" />
</component>
<template v-if="finalUseInnerList">
<component :is="nViewIs">
<slot name="header"/>
</component>
<component :is="nViewIs" class="zp-list-cell" v-for="(item,index) in realTotalData" :key="finalCellKeyName.length?item[finalCellKeyName]:index">
<slot name="cell" :item="item" :index="index"/>
</component>
<component :is="nViewIs">
<slot name="footer"/>
</component>
</template>
<template v-else>
<slot />
</template>
<!-- 全屏Loading -->
<component :is="nViewIs" v-if="showLoading&&zSlots.loading&&!loadingFullFixed" :class="{'z-paging-content-fixed':usePageScroll}" style="flex:1" :style="[nChatRecordRotateStyle]">
<slot name="loading" />
</component>
<!-- 上拉加载更多view -->
<component :is="nViewIs" v-if="!refresherOnly&&loadingMoreEnabled&&!showEmpty">
<view v-if="useChatRecordMode">
<view v-if="loadingStatus!==M.NoMore&&realTotalData.length">
<slot v-if="zSlots.chatLoading" name="chatLoading" />
<view v-else class="zp-chat-record-loading-container">
<text v-if="loadingStatus!==M.Loading" @click="_onScrollToUpper"
:class="defaultThemeStyle==='white'?'zp-loading-more-text zp-loading-more-text-white':'zp-loading-more-text zp-loading-more-text-black'">{{chatRecordLoadingMoreText}}</text>
<view>
<loading-indicator v-if="loadingStatus===M.Loading" class="zp-line-loading-image" animating />
</view>
</view>
</view>
</view>
<view :style="nLoadingMoreFixedHeight?{height:loadingMoreCustomStyle&&loadingMoreCustomStyle.height?loadingMoreCustomStyle.height:'80rpx'}:{}">
<slot v-if="showLoadingMoreDefault" name="loadingMoreDefault" />
<slot v-else-if="showLoadingMoreLoading" name="loadingMoreLoading" />
<slot v-else-if="showLoadingMoreNoMore" name="loadingMoreNoMore" />
<slot v-else-if="showLoadingMoreFail" name="loadingMoreFail" />
<z-paging-load-more @doClick="_onLoadingMore('click')" v-else-if="showLoadingMoreCustom" :zConfig="zLoadMoreConfig" />
<view v-if="safeAreaInsetBottom&&useSafeAreaPlaceholder" class="zp-safe-area-placeholder" :style="[{height:safeAreaBottom+'px'}]" />
</view>
</component>
<!-- 空数据图 -->
<component :is="nViewIs" v-if="showEmpty" :class="{'z-paging-content-fixed':usePageScroll}" :style="[{flex:emptyViewCenter?1:0},emptyViewSuperStyle,nChatRecordRotateStyle]">
<view :class="{'zp-empty-view':true,'zp-empty-view-center':emptyViewCenter}">
<slot v-if="zSlots.empty" name="empty" :isLoadFailed="isLoadFailed" />
<z-paging-empty-view v-else :emptyViewImg="finalEmptyViewImg" :emptyViewText="finalEmptyViewText" :showEmptyViewReload="finalShowEmptyViewReload"
:emptyViewReloadText="finalEmptyViewReloadText" :isLoadFailed="isLoadFailed" :emptyViewStyle="emptyViewStyle" :emptyViewTitleStyle="emptyViewTitleStyle"
:emptyViewImgStyle="emptyViewImgStyle" :emptyViewReloadStyle="emptyViewReloadStyle" :emptyViewZIndex="emptyViewZIndex" :emptyViewFixed="emptyViewFixed"
@reload="_emptyViewReload" @viewClick="_emptyViewClick" />
</view>
</component>
<component is="header" v-if="!hideNvueBottomTag" ref="zp-n-list-bottom-tag" class="zp-n-list-bottom-tag"></component>
</component>
<view v-if="zSlots.right" class="zp-page-right">
<slot name="right" />
</view>
</component>
<!-- 底部固定的slot -->
<slot name="bottom" />
<!-- 点击返回顶部view -->
<view v-if="showBackToTopClass" :class="backToTopClass" :style="[finalBackToTopStyle]" @click.stop="_backToTopClick">
<slot v-if="zSlots.backToTop" name="backToTop" />
<image v-else class="zp-back-to-top-img" :src="backToTopImg.length?backToTopImg:base64BackToTop" />
</view>
<!-- 全屏Loading(铺满z-paging并固定) -->
<view v-if="showLoading&&zSlots.loading&&loadingFullFixed" class="zp-loading-fixed">
<slot name="loading" />
</view>
</component>
<!-- #endif -->
</template>
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
<script src="./wxs/z-paging-wxs.wxs" module="pagingWxs" lang="wxs"></script>
<!-- #endif -->
<script module="pagingRenderjs" lang="renderjs">
import pagingRenderjs from './wxs/z-paging-renderjs.js';
/**
* z-paging 分页组件
* @description 高性能,全平台兼容。支持虚拟列表,支持nvue、vue3
* @tutorial https://z-paging.zxlee.cn
* @notice 以下仅为部分常用属性、方法和事件,完整文档请查阅z-paging官网
* @property {Number|String} default-page-no 自定义初始的pageNo,默认为1
* @property {Number|String} default-page-size 自定义pageSize,默认为10
* @property {Object} paging-style 设置z-paging的style,部分平台(如微信小程序)无法直接修改组件的style,可使用此属性代替
* @property {String} height z-paging的高度,优先级低于pagingStyle中设置的height,传字符串,如100px、100rpx、100%
* @property {String} width z-paging的宽度,优先级低于pagingStyle中设置的width,传字符串,如100px、100rpx、100%
* @property {Boolean} use-page-scroll 使用页面滚动,默认为否
* @property {Boolean} use-virtual-list 是否使用虚拟列表,默认为否
* @property {Boolean} fixed z-paging是否使用fixed布局,若使用fixed布局,则z-paging的父view无需固定高度,z-paging高度默认为100%,默认为是(当使用内置scroll-view滚动时有效)
* @property {Boolean} auto [z-paging]mounted后是否自动调用reload方法(mounted后自动调用接口),默认为是
* @property {Boolean} use-chat-record-mode 使用聊天记录模式,默认为否
* @event {Function} query 下拉刷新或滚动到底部时会自动触发此方法。z-paging加载时也会触发(若要禁止,请设置:auto="false")。pageNo和pageSize会自动计算好,直接传给服务器即可。
* @example <z-paging ref="paging" v-model="dataList" @query="queryList"></z-paging>
*/
export default {
name:"z-paging",
// #ifdef APP-VUE || H5
mixins: [pagingRenderjs],
// #endif
}
</script>
<script src="./js/z-paging-main.js" />
<style scoped>
@import "./css/z-paging-main.css";
@import "./css/z-paging-static.css";
</style>
{
"id": "z-paging",
"name": "z-paging",
"displayName": "【z-paging下拉刷新、上拉加载】高性能,全平台兼容。支持虚拟列表,支持nvue、vue3",
"version": "2.6.2",
"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"
}
}
}
}
}
# 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.6.2-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) |
### demo下载
* 支持vue2&vue3的`选项式api`写法demo下载,请点击页面右上角的【使用HBuilderX导入示例项目】或【下载示例项目ZIP】。
* 支持vue3的`组合式api`写法demo下载,请访问[github](https://github.com/SmileZXLee/uni-z-paging)。
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color: #333; //基本色
$uni-text-color-inverse: #fff; //反色
$uni-text-color-grey: #999; //辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #ffffff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; //点击状态颜色
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); //遮罩颜色
/* 边框颜色 */
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 24rpx;
$uni-font-size-base: 28rpx;
$uni-font-size-lg: 32rpx;
/* 图片尺寸 */
$uni-img-size-sm: 40rpx;
$uni-img-size-base: 52rpx;
$uni-img-size-lg: 80rpx;
/* Border Radius */
$uni-border-radius-sm: 4rpx;
$uni-border-radius-base: 6rpx;
$uni-border-radius-lg: 12rpx;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 10px;
$uni-spacing-row-base: 20rpx;
$uni-spacing-row-lg: 30rpx;
/* 垂直间距 */
$uni-spacing-col-sm: 8rpx;
$uni-spacing-col-base: 16rpx;
$uni-spacing-col-lg: 24rpx;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 40rpx;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle: 36rpx;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 30rpx;
/* uni.scss */
@import 'uview-plus/theme.scss';
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import path from 'path'
const target = 'http://ums.sxb805.cn:9001/';
export default defineConfig({
plugins: [
uni(),
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
host: '0.0.0.0', //服务器ip地址
port: 5555, //本地端口
fs: {
strict: false, // 支持引用除入口目录的文件
},
// open: true, // 是否自动在浏览器打开
proxy: {
'/cloud': {
target,
changeOrigin: true,
},
},
}
})