Commit 64d49eb9 authored by jyx's avatar jyx

代码优化

parent afe8cef8
<script>
import {
PAKEAGE_NAME
} from './utils/utils.js';
import checkUpdate from './utils/update.js';
export default {
onLaunch: function(options) {
......@@ -86,7 +89,7 @@
bottomSafePadding: 12,
h5Url: 'https://api.mints-id.com/index.html',
baseUrl: 'https://dx.mints-tech.cn/minip-api/miniApi',
// baseUrl: 'http://192.168.110.71:8301/minip-api/miniApi',
// baseUrl: 'http://192.168.110.42:8303/miniApi',
titleButtonWidth: 38,
token: '',
userId: 0,
......@@ -96,7 +99,7 @@
versionName: 'v 1.0.0',
auth: false, // 三要素实名认证
userInfo: null,
pkgName: 'com.duben.dybookhm'
pkgName: PAKEAGE_NAME
}
};
</script>
......@@ -118,4 +121,5 @@
@import './scss/agreement.scss';
@import "@/uni_modules/uview-ui/index.scss";
@import '@/uni.scss';
@import '@/static/readiconfont.css';
</style>
\ No newline at end of file
......@@ -162,10 +162,13 @@ function getSystemDict(types = [], callback) {
* @param {Object} callback
*/
function collectionBook(isCollected, bookId, callback) {
let url = isCollected ? '/readSystem/v1/collect/collect' : "/readSystem/v1/collect/cancel";
url = `${url}?articleId=${bookId}`
apiGET({
let url = isCollected ? '/book/collect' : "/book/collectCancel";
// url = `${url}?articleId=${bookId}`
apiPOST({
url,
data: {
articleId:bookId
},
callback
})
}
......
......@@ -14,8 +14,12 @@ import {
import User from "../models/User.js";
import {
isEmpty,
isNotEmpty
isNotEmpty,
} from "../utils/util.js";
import {
PAKEAGE_NAME
} from "../../utils/utils.js";
import {
printError
} from "../utils/printUtil.js";
......@@ -50,12 +54,14 @@ function successHandler(url, param, res, callback) {
}
})
}
if (res.status != 200) {
toastMessage(`服务器异常,${res.status}`)
printError("请求失败", url, param, res.status, res.errMsg);
console.log(res)
if (res.statusCode != 200) {
toastMessage(`服务器异常,${res.statusCode}`)
printError("请求失败", url, param, res.statusCode, res.errMsg);
if (typeof callback == 'function') callback(false, res.errMsg)
} else {
if (res.data.code == 200) {
if (res.data.status == 200) {
if (typeof callback == 'function') callback(true, res.data.data)
} else if (res.data.code == 401) {
tokenExpireFn()
......@@ -103,7 +109,10 @@ function refreshUserInfo() {
name: userData.username,
nickName: userData.nickname,
avater: userData.avatar,
isVip: userData.expireTime > 0
})
console.log(user)
console.log(userData)
saveNickname(userData.nickname);
saveUserInfo(user); // 存储用户数据
postNotification(KEY_NOTIFICATION_LOGIN_SUCCESS); // 通知登录成功
......@@ -139,7 +148,7 @@ function postPhone(data, callback) {
function requestToken(data, callback) {
let url = `${config["BASE_URL"]}/user/ttLogin`;
let header = {
pkgName: 'com.duben.dybookhm',
pkgname: PAKEAGE_NAME,
token: ``
}
uni.request({
......@@ -158,9 +167,10 @@ function requestToken(data, callback) {
// 请求用户数据
function requestUserInfo(callback) {
let url = `${config['BASE_URL']}/system/user/getCurrentUserInfo`;
let url = `${config['BASE_URL']}/user/baseMsg`;
let header = {
Authorization: `${readToken()}`
token: `${readToken()}`,
pkgname: PAKEAGE_NAME
}
uni.request({
url,
......@@ -221,7 +231,8 @@ function saveToken(token) {
function readToken() {
if (!LOCAL_TOKEN) {
LOCAL_TOKEN = readStorage(KEY_STORAGE_TOKEN)
// LOCAL_TOKEN = readStorage(KEY_STORAGE_TOKEN)
LOCAL_TOKEN = readStorage('token')
}
return LOCAL_TOKEN;
}
......
......@@ -9,8 +9,11 @@ import {
printError,
} from "./printUtil.js"
import {
appendParam
appendParam,
} from "./util";
import {
PAKEAGE_NAME
} from "../../utils/utils";
import {
addNormalNotificationObserver,
removeNotificationObserver
......@@ -65,11 +68,11 @@ let apiRequest = function({
let option = {}; // 请求参数
// url 处理
let handlerUrl = `${config["BASE_URL"]}${urlModule?urlModule:""}${url}`;
if (handlerUrl.indexOf("?") == -1) {
handlerUrl = `${handlerUrl}?t=${new Date().getTime()}`;
} else {
handlerUrl = `${handlerUrl}&t=${new Date().getTime()}`;
}
// if (handlerUrl.indexOf("?") == -1) {
// handlerUrl = `${handlerUrl}?t=${new Date().getTime()}`;
// } else {
// handlerUrl = `${handlerUrl}&t=${new Date().getTime()}`;
// }
option = {
url: handlerUrl,
method
......@@ -97,7 +100,7 @@ let apiRequest = function({
return;
}
option.header = {
pkgName: 'com.duben.dybookhm',
pkgName: PAKEAGE_NAME,
token: `${token}`
}
// header处理
......@@ -238,7 +241,7 @@ function apiUPLOAD({
option.url = `${option.url}?t=${new Date().getTime()}`; // 路径
option.header = {
pkgName: 'com.duben.dybookhm',
pkgName: PAKEAGE_NAME,
token: `${token}`
}
if (header) {
......@@ -312,14 +315,14 @@ function successHandler(originQuery, requestTask, res) {
tokenExpireFn();
} else {
if (typeof res.data == 'string') res.data = JSON.parse(res.data);
if (res.data.code == 200) {
if (res.data.status == 200) {
printDebug("请求结果", originQuery.url, res.data.data);
if (typeof originQuery.callback == 'function') originQuery.callback(true, res.data.data);
} else if (res.data.code == 401) {
} else if (res.data.status == 401) {
tokenExpireFn()
if (typeof originQuery.callback == 'function') originQuery.callback(false, res.data.msg);
} else {
toastMessage(res.data.message ? res.data.message : res.data.code ? res.data.code : "未知错误");
toastMessage(res.data.message ? res.data.message : res.data.status ? res.data.status : "未知错误");
printError("请求失败", originQuery.url, res.data.message);
if (typeof originQuery.callback == 'function') originQuery.callback(false, res.data.message);
}
......
......@@ -26,8 +26,17 @@ function removeStorage(key) {
}
}
function clearStorage(key) {
try {
return uni.clearStorageSync(key);
} catch (error) {
printError("remove storage 错误", error.errMsg, key);
}
}
module.exports = {
saveStorage,
readStorage,
removeStorage
removeStorage,
clearStorage
}
\ No newline at end of file
......@@ -5,7 +5,7 @@
<uni-icons type='search' size='28' color="#e5e5e5"></uni-icons>
</view>
<input class="item c-flex_1" placeholder="搜索书名/作者名" disabled />
<view class="button item">
<view class="mbutton item">
搜索
</view>
</view>
......@@ -42,6 +42,7 @@
padding: 10rpx 20rpx;
.search-zone {
width: 100%;
border-radius: 35rpx;
background: #fff;
padding: 4rpx;
......@@ -51,10 +52,10 @@
margin-left: 20rpx;
}
.button {
.mbutton {
width: 100rpx;
height: 70rpx;
line-height: 70rpx;
width: 120rpx;
text-align: center;
color: #fff;
background: #007aff;
......
<template>
<view>
<c-list ref='list' :showShelfEmpty="true" flag='bookshelf' :needLogin="true" :height="height" url='/v1/collect/'
:param="requestParam" @change='changeData'>
<c-list ref='list' :showShelfEmpty="true" flag='bookshelf' :needLogin="true" :height="height" url='/book/collectList/'
:param="requestParam" @change='changeData' method="POST">
<book-list-item v-for='(item, index) in dataList' :key='index' :item='item'
@tapItem='tapItem($event, index)' @close='tapClose($event, index)'>
<template v-slot:footer>
......@@ -59,7 +59,8 @@
}
},
changeData(e) {
this.dataList = e.detail.data.map(item => {
console.log(e)
this.dataList = e.detail.data.list.map(item => {
return new BookshelfBookItem(item)
})
},
......
<template>
<view>
<c-list ref='list' flag='warehouse' method="POST" :height="height" url='/v1/article/' :param="requestParam"
<c-list ref='list' flag='warehouse' method="POST" :height="height" url='/book/articleList/' :param="requestParam"
@change='changeData'>
<book-list-item v-for='(item, index) in dataList' :key='index' :item='item' :showClose='false'
@tapItem='tapItem($event, index)' @close='tapClose($event, index)'>
......@@ -39,7 +39,7 @@
computed: {
requestParam: function() {
return {
tagId: this.category.value
categoryId: this.category.value
}
}
},
......
......@@ -5,7 +5,7 @@ import {
function getCategorys(callback) {
apiPOST({
url: "/v1/tag/",
url: "/book/categoryList/",
callback
})
}
......
......@@ -89,7 +89,7 @@
show() {
setTimeout(() => {
// 匹配用户直接跳转小说
var bookId=uni.getStorageSync('firstBookId')
var bookId = uni.getStorageSync('firstBookId')
if (bookId != '') {
gotoBookContentPage(bookId);
uni.setStorage({
......@@ -120,7 +120,7 @@
getCategoryData() {
getCategorys((success, data) => {
if (success) {
let result = data ? data.map(item => {
let result = data.records ? data.records.map(item => {
return new Category(item)
}).sort((a, b) => {
return a.sort - b.sort
......
......@@ -9,7 +9,7 @@ const {
}
} = require("../configEnum");
export default {
// BASE_URL: "http://192.168.110.6:8800",
// BASE_URL: "http://192.168.110.42:8303/miniApi",
BASE_URL: "https://dx.mints-tech.cn/minip-api/miniApi",
BASE_URL_MODULE: "",
BASE_SOCKET_URL: "",
......
......@@ -9,7 +9,7 @@ const {
}
} = require("../configEnum");
export default {
// BASE_URL: "https://book.mints-id.com",
// BASE_URL: "http://192.168.110.42:8303/miniApi",
BASE_URL: "https://dx.mints-tech.cn/minip-api/miniApi",
BASE_URL_MODULE: "",
BASE_SOCKET_URL: "",
......
<template>
<view :style="[bgStyle]">
<z-paging :style="[bgStyle]">
<c-empty v-if='showEmpty'></c-empty>
<template v-else>
<detail-warn></detail-warn>
......@@ -24,7 +24,7 @@
</button>
</view>
</popup>
</view>
</z-paging>
</template>
<script>
......@@ -127,6 +127,11 @@
if (info.userInfo) {
addReadRecord(this.bookId)
}
console.log(info)
console.log(this.userInfo)
// this.showVipOpen = data.openVips
// // 用户变动,需要刷新数据
// this.$nextTick(() => {
// uni.startPullDownRefresh({})
......@@ -246,10 +251,10 @@
},
// 文章数据刷新
refreshBookData(bookId) {
getOpens((success, data) => {
// this.showBeanOpen = data.openBeans
this.showVipOpen = data.openVips
})
// getOpens((success, data) => {
// // this.showBeanOpen = data.openBeans
// this.showVipOpen = data.openVips
// })
getBookDetailData(bookId, (success, data) => {
setTimeout(() => {
uni.stopPullDownRefresh();
......
......@@ -168,15 +168,15 @@
this.imageError = true
},
refreshPackData() {
getPackData((success, data) => {
if (success) {
this.vipPackList = data.map(item => {
let result = new Pack(item);
result.isBeanPack = false;
return result;
})
}
})
// getPackData((success, data) => {
// if (success) {
// this.vipPackList = data.map(item => {
// let result = new Pack(item);
// result.isBeanPack = false;
// return result;
// })
// }
// })
getBookBeanPackData((success, data) => {
if (success) {
this.beanPackList = data.map(item => {
......
......@@ -120,11 +120,11 @@
this.showPop = false;
},
refrehsPackData() {
getPackData((success, data) => {
this.packList = data.map(item => {
return new Pack(item);
})
})
// getPackData((success, data) => {
// this.packList = data.map(item => {
// return new Pack(item);
// })
// })
},
choosePack(item, index) {
this.selectedIndex = index;
......
......@@ -19,7 +19,10 @@ import {
*/
function getBookDetailData(bookId, callback) {
apiPOST({
url: `/v1/article/info?id=${bookId}`,
url: `/book/info`,
data: {
id: bookId
},
callback
})
}
......@@ -28,9 +31,12 @@ function getBookDetailData(bookId, callback) {
* @param {Object} bookId
* @param {Object} callback
*/
function getBookRecommendData(bookId,callback) {
function getBookRecommendData(bookId, callback) {
apiPOST({
url: `/v1/article/recommend?id=${bookId}`,
url: `/book/recommend`,
data: {
id: bookId
},
callback
})
}
......@@ -39,8 +45,11 @@ function getBookRecommendData(bookId,callback) {
* @param {Object} callback
*/
function addReadRecord(bookId, callback) {
apiGET({
url: `/readSystem/system/readRecord/addReadRecord?articleId=${bookId}`,
apiPOST({
url: `/book/addReadRecord`,
data: {
articleId: bookId
},
callback
})
}
......
<template>
<view class="search-header-bar">
<view class="input-box item c-flex_1">
<uni-easyinput :value='searchKeyword' placeholder="请输入书名或者作者名" :focus='focus' :styles="inputStyles"
confirmType="search" trim="all" :inputBorder="true" @clear='clearInput'
@change='changeInput'></uni-easyinput>
<uni-easyinput v-model="searchKeyword" placeholder="请输入书名或者作者名" :focus='focus' confirmType="search"
trim="all" :inputBorder="true" @clear="clearInput" @change="changeInput"
@confirm="changeInput"></uni-easyinput>
</view>
<view class="button-box item" @click="tapSearch">
<view class="title">
......
......@@ -26,6 +26,7 @@
import {
saveSearchHistory,
watchSearchHistoryChange,
clearSearchHistory,
removeSearchHistoryChangeWatch,
getHotRecommondData,
} from "../services/index.js"
......@@ -68,7 +69,7 @@
},
methods: {
tapDeleteHistory() {
saveSearchHistory([])
clearSearchHistory()
},
tapItem(e, flag) {
this.$emit('chooseKeyword', {
......
<template>
<c-list ref="list" flag='search' :needLogin="needLogin" :height="height" :url='requestUrl' :param='requestParam'
<c-list ref="list" flag='search' method="POST" :needLogin="needLogin" :height="height" :url='requestUrl' :param='requestParam'
@change='changeData'>
<book-list-item v-for='(item, index) in dataList' :key='index' :item='item' :showClose="false"
@tapItem='tapItem($event, index)'>
......@@ -40,7 +40,7 @@
},
data: function() {
return {
dataList: null
dataList: null,
}
},
computed: {
......@@ -50,7 +50,7 @@
requestUrl: function() {
return !this.searchType || this.searchType.value == ENUM_SEARCH_TYPE.WAREHOUSE.value ?
"/book/articleList/" :
"/readSystem/v1/collect/"
"/book/collect/"
},
requestParam: function() {
return {
......
......@@ -5,7 +5,8 @@ import {
} from "../../../../common/utils/notificationCenter";
import {
readStorage,
saveStorage
saveStorage,
clearStorage
} from "../../../../common/utils/storageUtil";
const KEY_STORAGE_SEARCH_HISTORY = "KEY_STORAGE_SEARCH_HISTORY";
const KEY_NOTIFICATION_SEARCH_HISTORY_CHANGE = 'KEY_NOTIFICATION_SEARCH_HISTORY_CHANGE';
......@@ -23,6 +24,11 @@ function saveSearchHistory(keyword) {
noticeSearchHistoryChange(result);
}
function clearSearchHistory() {
clearStorage(KEY_STORAGE_SEARCH_HISTORY);
noticeSearchHistoryChange([]);
}
function readSearchHistory() {
let result = readStorage(KEY_STORAGE_SEARCH_HISTORY);
return result || []
......@@ -58,6 +64,7 @@ module.exports = {
*/
saveSearchHistory,
readSearchHistory,
clearSearchHistory,
watchSearchHistoryChange,
removeSearchHistoryChangeWatch,
/**
......
......@@ -134,11 +134,11 @@
})
},
refrehsPackData() {
getPackData((success, data) => {
this.packList = data.map(item => {
return new Pack(item);
})
})
// getPackData((success, data) => {
// this.packList = data.map(item => {
// return new Pack(item);
// })
// })
},
choosePack(item, index) {
this.selectedIndex = index;
......
......@@ -110,6 +110,6 @@
<style lang="scss">
.body {
height: 100%;
background-color: white;
// background-color: white;
}
</style>
\ No newline at end of file
......@@ -69,6 +69,16 @@
});
}
// app.globalData.userId = data.idcode;
// uni.setStorage({
// key: 'token',
// data: 'D6E76AC8E89ABE548B56568B8814D578D6980EAB68926EB1E7C07BF9E96A316F5F433703067DF5142735505C42F58997'
// });
// setTimeout(() => {
// redirectTo('home');
// }, 1000);
// return
tt.login({
force: true,
success(res) {
......
......@@ -301,3 +301,16 @@ body {
padding: 0 16rpx;
display: inline-block;
}
.c-flex_row {
display: flex;
flex-direction: row;
}
.c-align_center{
align-items: center;
}
.c-flex_1{
flex: 1;
}
\ No newline at end of file
## 1.1.18(2024-04-11)
- 修复 easyinput组件双向绑定问题
## 1.1.17(2024-03-28)
- 修复 在头条小程序下丢失事件绑定的问题
## 1.1.16(2024-03-20)
- 修复 在密码输入情况下 清除和小眼睛覆盖bug 在edge浏览器下显示双眼睛bug
## 1.1.15(2024-02-21)
- 新增 左侧插槽:left
## 1.1.14(2024-02-19)
- 修复 onBlur的emit传值错误
## 1.1.12(2024-01-29)
- 补充 adjust-position文档属性补充
## 1.1.11(2024-01-29)
- 补充 adjust-position属性传递值:(Boolean)当键盘弹起时,是否自动上推页面
## 1.1.10(2024-01-22)
- 去除 移除无用的log输出
## 1.1.9(2023-04-11)
- 修复 vue3 下 keyboardheightchange 事件报错的bug
## 1.1.8(2023-03-29)
- 优化 trim 属性默认值
## 1.1.7(2023-03-29)
- 新增 cursor-spacing 属性
## 1.1.6(2023-01-28)
- 新增 keyboardheightchange 事件,可监听键盘高度变化
## 1.1.5(2022-11-29)
- 优化 主题样式
## 1.1.4(2022-10-27)
- 修复 props 中背景颜色无默认值的bug
## 1.1.0(2022-06-30)
- 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容
- 新增 clear 事件,点击右侧叉号图标触发
- 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发
- 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等
## 1.0.5(2022-06-07)
- 优化 clearable 显示策略
## 1.0.4(2022-06-07)
- 优化 clearable 显示策略
## 1.0.3(2022-05-20)
- 修复 关闭图标某些情况下无法取消的 bug
## 1.0.2(2022-04-12)
- 修复 默认值不生效的 bug
## 1.0.1(2022-04-02)
- 修复 value 不能为 0 的 bug
## 1.0.0(2021-11-19)
- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
## 0.1.4(2021-08-20)
- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
## 0.1.3(2021-08-11)
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.1.2(2021-07-30)
- 优化 vue3 下事件警告的问题
## 0.1.1
- 优化 errorMessage 属性支持 Boolean 类型
## 0.1.0(2021-07-13)
- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.16(2021-06-29)
- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
## 0.0.15(2021-06-21)
- 修复 passwordIcon 属性拼写错误的 bug
## 0.0.14(2021-06-18)
- 新增 passwordIcon 属性,当 type=password 时是否显示小眼睛图标
- 修复 confirmType 属性不生效的问题
## 0.0.13(2021-06-04)
- 修复 disabled 状态可清出内容的 bug
## 0.0.12(2021-05-12)
- 新增 组件示例地址
## 0.0.11(2021-05-07)
- 修复 input-border 属性不生效的问题
## 0.0.10(2021-04-30)
- 修复 ios 遮挡文字、显示一半的问题
## 0.0.9(2021-02-05)
- 调整为 uni_modules 目录规范
- 优化 兼容 nvue 页面
/**
* @desc 函数防抖
* @param func 目标函数
* @param wait 延迟执行毫秒数
* @param immediate true - 立即执行, false - 延迟执行
*/
export const debounce = function(func, wait = 1000, immediate = true) {
let timer;
return function() {
let context = this,
args = arguments;
if (timer) clearTimeout(timer);
if (immediate) {
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timer = setTimeout(() => {
func.apply(context, args);
}, wait)
}
}
}
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 使用表时间戳,在时间段开始的时候触发 2 使用表定时器,在时间段结束的时候触发
*/
export const throttle = (func, wait = 1000, type = 1) => {
let previous = 0;
let timeout;
return function() {
let context = this;
let args = arguments;
if (type === 1) {
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
} else if (type === 2) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}
<template>
<view class="uni-easyinput" :class="{ 'uni-easyinput-error': msg }" :style="boxStyle">
<view class="uni-easyinput__content" :class="inputContentClass" :style="inputContentStyle">
<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc"
@click="onClickIcon('prefix')" size="22"></uni-icons>
<slot name="left">
</slot>
<!-- #ifdef MP-ALIPAY -->
<textarea :enableNative="enableNative" v-if="type === 'textarea'" class="uni-easyinput__content-textarea"
:class="{ 'input-padding': inputBorder }" :name="name" :value="val" :placeholder="placeholder"
:placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class"
:maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" :cursor-spacing="cursorSpacing"
:adjust-position="adjustPosition" @input="onInput" @blur="_Blur" @focus="_Focus" @confirm="onConfirm"
@keyboardheightchange="onkeyboardheightchange"></textarea>
<input :enableNative="enableNative" v-else :type="type === 'password' ? 'text' : type"
class="uni-easyinput__content-input" :style="inputStyle" :name="name" :value="val"
:password="!showPassword && type === 'password'" :placeholder="placeholder" :placeholderStyle="placeholderStyle"
placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength"
:focus="focused" :confirmType="confirmType" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition"
@focus="_Focus" @blur="_Blur" @input="onInput" @confirm="onConfirm"
@keyboardheightchange="onkeyboardheightchange" />
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea"
:class="{ 'input-padding': inputBorder }" :name="name" :value="val" :placeholder="placeholder"
:placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class"
:maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" :cursor-spacing="cursorSpacing"
:adjust-position="adjustPosition" @input="onInput" @blur="_Blur" @focus="_Focus" @confirm="onConfirm"
@keyboardheightchange="onkeyboardheightchange"></textarea>
<input v-else :type="type === 'password' ? 'text' : type" class="uni-easyinput__content-input" :style="inputStyle"
:name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder"
:placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled"
:maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" :cursor-spacing="cursorSpacing"
:adjust-position="adjustPosition" @focus="_Focus" @blur="_Blur" @input="onInput" @confirm="onConfirm"
@keyboardheightchange="onkeyboardheightchange" />
<!-- #endif -->
<template v-if="type === 'password' && passwordIcon">
<!-- 开启密码时显示小眼睛 -->
<uni-icons v-if="isVal" class="content-clear-icon" :class="{ 'is-textarea-icon': type === 'textarea' }"
:type="showPassword ? 'eye-slash-filled' : 'eye-filled'" :size="22"
:color="focusShow ? primaryColor : '#c0c4cc'" @click="onEyes"></uni-icons>
</template>
<template v-if="suffixIcon">
<uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc"
@click="onClickIcon('suffix')" size="22"></uni-icons>
</template>
<template v-else>
<uni-icons v-if="clearable && isVal && !disabled && type !== 'textarea'" class="content-clear-icon"
:class="{ 'is-textarea-icon': type === 'textarea' }" type="clear" :size="clearSize"
:color="msg ? '#dd524d' : focusShow ? primaryColor : '#c0c4cc'" @click="onClear"></uni-icons>
</template>
<slot name="right"></slot>
</view>
</view>
</template>
<script>
/**
* Easyinput 输入框
* @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。
* @tutorial https://ext.dcloud.net.cn/plugin?id=3455
* @property {String} value 输入内容
* @property {String } type 输入框的类型(默认text) password/text/textarea/..
* @value text 文本输入键盘
* @value textarea 多行文本输入键盘
* @value password 密码输入键盘
* @value number 数字输入键盘,注意iOS上app-vue弹出的数字键盘并非9宫格方式
* @value idcard 身份证输入键盘,信、支付宝、百度、QQ小程序
* @value digit 带小数点的数字键盘 ,App的nvue页面、微信、支付宝、百度、头条、QQ小程序支持
* @property {Boolean} clearable 是否显示右侧清空内容的图标控件,点击可清空输入框内容(默认true)
* @property {Boolean} autoHeight 是否自动增高输入区域,type为textarea时有效(默认true)
* @property {String } placeholder 输入框的提示文字
* @property {String } placeholderStyle placeholder的样式(内联样式,字符串),如"color: #ddd"
* @property {Boolean} focus 是否自动获得焦点(默认false)
* @property {Boolean} disabled 是否禁用(默认false)
* @property {Number } maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认140)
* @property {String } confirmType 设置键盘右下角按钮的文字,仅在type="text"时生效(默认done)
* @property {Number } clearSize 清除图标的大小,单位px(默认15)
* @property {String} prefixIcon 输入框头部图标
* @property {String} suffixIcon 输入框尾部图标
* @property {String} primaryColor 设置主题色(默认#2979ff)
* @property {Boolean} trim 是否自动去除两端的空格
* @property {Boolean} cursorSpacing 指定光标与键盘的距离,单位 px
* @property {Boolean} ajust-position 当键盘弹起时,是否上推内容,默认值:true
* @value both 去除两端空格
* @value left 去除左侧空格
* @value right 去除右侧空格
* @value start 去除左侧空格
* @value end 去除右侧空格
* @value all 去除全部空格
* @value none 不去除空格
* @property {Boolean} inputBorder 是否显示input输入框的边框(默认true)
* @property {Boolean} passwordIcon type=password时是否显示小眼睛图标
* @property {Object} styles 自定义颜色
* @event {Function} input 输入框内容发生变化时触发
* @event {Function} focus 输入框获得焦点时触发
* @event {Function} blur 输入框失去焦点时触发
* @event {Function} confirm 点击完成按钮时触发
* @event {Function} iconClick 点击图标时触发
* @example <uni-easyinput v-model="mobile"></uni-easyinput>
*/
function obj2strClass(obj) {
let classess = '';
for (let key in obj) {
const val = obj[key];
if (val) {
classess += `${key} `;
}
}
return classess;
}
function obj2strStyle(obj) {
let style = '';
for (let key in obj) {
const val = obj[key];
style += `${key}:${val};`;
}
return style;
}
export default {
name: 'uni-easyinput',
emits: [
'click',
'iconClick',
'update:modelValue',
'input',
'focus',
'blur',
'confirm',
'clear',
'eyes',
'change',
'keyboardheightchange'
],
model: {
prop: 'modelValue',
event: 'update:modelValue'
},
options: {
// #ifdef MP-TOUTIAO
virtualHost: false,
// #endif
// #ifndef MP-TOUTIAO
virtualHost: true
// #endif
},
inject: {
form: {
from: 'uniForm',
default: null
},
formItem: {
from: 'uniFormItem',
default: null
}
},
props: {
name: String,
value: [Number, String],
modelValue: [Number, String],
type: {
type: String,
default: 'text'
},
clearable: {
type: Boolean,
default: true
},
autoHeight: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ' '
},
placeholderStyle: String,
focus: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
confirmType: {
type: String,
default: 'done'
},
clearSize: {
type: [Number, String],
default: 24
},
inputBorder: {
type: Boolean,
default: true
},
prefixIcon: {
type: String,
default: ''
},
suffixIcon: {
type: String,
default: ''
},
trim: {
type: [Boolean, String],
default: false
},
cursorSpacing: {
type: Number,
default: 0
},
passwordIcon: {
type: Boolean,
default: true
},
adjustPosition: {
type: Boolean,
default: true
},
primaryColor: {
type: String,
default: '#2979ff'
},
styles: {
type: Object,
default() {
return {
color: '#333',
backgroundColor: '#fff',
disableColor: '#F7F6F6',
borderColor: '#e5e5e5'
};
}
},
errorMessage: {
type: [String, Boolean],
default: ''
},
// #ifdef MP-ALIPAY
enableNative: {
type: Boolean,
default: false
}
// #endif
},
data() {
return {
focused: false,
val: '',
showMsg: '',
border: false,
isFirstBorder: false,
showClearIcon: false,
showPassword: false,
focusShow: false,
localMsg: '',
isEnter: false // 用于判断当前是否是使用回车操作
};
},
computed: {
// 输入框内是否有值
isVal() {
const val = this.val;
// fixed by mehaotian 处理值为0的情况,字符串0不在处理范围
if (val || val === 0) {
return true;
}
return false;
},
msg() {
// console.log('computed', this.form, this.formItem);
// if (this.form) {
// return this.errorMessage || this.formItem.errMsg;
// }
// TODO 处理头条 formItem 中 errMsg 不更新的问题
return this.localMsg || this.errorMessage;
},
// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,用户可以传入字符串数值
inputMaxlength() {
return Number(this.maxlength);
},
// 处理外层样式的style
boxStyle() {
return `color:${
this.inputBorder && this.msg ? '#e43d33' : this.styles.color
};`;
},
// input 内容的类和样式处理
inputContentClass() {
return obj2strClass({
'is-input-border': this.inputBorder,
'is-input-error-border': this.inputBorder && this.msg,
'is-textarea': this.type === 'textarea',
'is-disabled': this.disabled,
'is-focused': this.focusShow
});
},
inputContentStyle() {
const focusColor = this.focusShow
? this.primaryColor
: this.styles.borderColor;
const borderColor =
this.inputBorder && this.msg ? '#dd524d' : focusColor;
return obj2strStyle({
'border-color': borderColor || '#e5e5e5',
'background-color': this.disabled
? this.styles.disableColor
: this.styles.backgroundColor
});
},
// input右侧样式
inputStyle() {
const paddingRight =
this.type === 'password' || this.clearable || this.prefixIcon
? ''
: '10px';
return obj2strStyle({
'padding-right': paddingRight,
'padding-left': this.prefixIcon ? '' : '10px'
});
}
},
watch: {
value(newVal) {
this.val = newVal;
},
modelValue(newVal) {
this.val = newVal;
},
focus(newVal) {
this.$nextTick(() => {
this.focused = this.focus;
this.focusShow = this.focus;
});
}
},
created() {
this.init();
// TODO 处理头条vue3 computed 不监听 inject 更改的问题(formItem.errMsg)
if (this.form && this.formItem) {
this.$watch('formItem.errMsg', newVal => {
this.localMsg = newVal;
});
}
},
mounted() {
this.$nextTick(() => {
this.focused = this.focus;
this.focusShow = this.focus;
});
},
methods: {
/**
* 初始化变量值
*/
init() {
if (this.value || this.value === 0) {
this.val = this.value;
} else if (
this.modelValue ||
this.modelValue === 0 ||
this.modelValue === ''
) {
this.val = this.modelValue;
} else {
this.val = null;
}
},
/**
* 点击图标时触发
* @param {Object} type
*/
onClickIcon(type) {
this.$emit('iconClick', type);
},
/**
* 显示隐藏内容,密码框时生效
*/
onEyes() {
this.showPassword = !this.showPassword;
this.$emit('eyes', this.showPassword);
},
/**
* 输入时触发
* @param {Object} event
*/
onInput(event) {
let value = event.detail.value;
// 判断是否去除空格
if (this.trim) {
if (typeof this.trim === 'boolean' && this.trim) {
value = this.trimStr(value);
}
if (typeof this.trim === 'string') {
value = this.trimStr(value, this.trim);
}
}
if (this.errMsg) this.errMsg = '';
this.val = value;
// TODO 兼容 vue2
this.$emit('input', value);
// TODO 兼容 vue3
this.$emit('update:modelValue', value);
},
/**
* 外部调用方法
* 获取焦点时触发
* @param {Object} event
*/
onFocus() {
this.$nextTick(() => {
this.focused = true;
});
this.$emit('focus', null);
},
_Focus(event) {
this.focusShow = true;
this.$emit('focus', event);
},
/**
* 外部调用方法
* 失去焦点时触发
* @param {Object} event
*/
onBlur() {
this.focused = false;
this.$emit('blur', null);
},
_Blur(event) {
let value = event.detail.value;
this.focusShow = false;
this.$emit('blur', event);
// 根据类型返回值,在event中获取的值理论上讲都是string
if (this.isEnter === false) {
this.$emit('change', this.val);
}
// 失去焦点时参与表单校验
if (this.form && this.formItem) {
const { validateTrigger } = this.form;
if (validateTrigger === 'blur') {
this.formItem.onFieldChange();
}
}
},
/**
* 按下键盘的发送键
* @param {Object} e
*/
onConfirm(e) {
this.$emit('confirm', this.val);
this.isEnter = true;
this.$emit('change', this.val);
this.$nextTick(() => {
this.isEnter = false;
});
},
/**
* 清理内容
* @param {Object} event
*/
onClear(event) {
this.val = '';
// TODO 兼容 vue2
this.$emit('input', '');
// TODO 兼容 vue2
// TODO 兼容 vue3
this.$emit('update:modelValue', '');
// 点击叉号触发
this.$emit('clear');
},
/**
* 键盘高度发生变化的时候触发此事件
* 兼容性:微信小程序2.7.0+、App 3.1.0+
* @param {Object} event
*/
onkeyboardheightchange(event) {
this.$emit('keyboardheightchange', event);
},
/**
* 去除空格
*/
trimStr(str, pos = 'both') {
if (pos === 'both') {
return str.trim();
} else if (pos === 'left') {
return str.trimLeft();
} else if (pos === 'right') {
return str.trimRight();
} else if (pos === 'start') {
return str.trimStart();
} else if (pos === 'end') {
return str.trimEnd();
} else if (pos === 'all') {
return str.replace(/\s+/g, '');
} else if (pos === 'none') {
return str;
}
return str;
}
}
};
</script>
<style lang="scss">
$uni-error: #e43d33;
$uni-border-1: #dcdfe6 !default;
.uni-easyinput {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
flex: 1;
position: relative;
text-align: left;
color: #333;
font-size: 14px;
}
.uni-easyinput__content {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
// min-height: 36px;
/* #endif */
flex-direction: row;
align-items: center;
// 处理border动画刚开始显示黑色的问题
border-color: #fff;
transition-property: border-color;
transition-duration: 0.3s;
}
.uni-easyinput__content-input {
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
position: relative;
overflow: hidden;
flex: 1;
line-height: 1;
font-size: 14px;
height: 35px;
// min-height: 36px;
/*ifdef H5*/
& ::-ms-reveal {
display: none;
}
& ::-ms-clear {
display: none;
}
& ::-o-clear {
display: none;
}
/*endif*/
}
.uni-easyinput__placeholder-class {
color: #999;
font-size: 12px;
// font-weight: 200;
}
.is-textarea {
align-items: flex-start;
}
.is-textarea-icon {
margin-top: 5px;
}
.uni-easyinput__content-textarea {
position: relative;
overflow: hidden;
flex: 1;
line-height: 1.5;
font-size: 14px;
margin: 6px;
margin-left: 0;
height: 80px;
min-height: 80px;
/* #ifndef APP-NVUE */
min-height: 80px;
width: auto;
/* #endif */
}
.input-padding {
padding-left: 10px;
}
.content-clear-icon {
padding: 0 5px;
}
.label-icon {
margin-right: 5px;
margin-top: -1px;
}
// 显示边框
.is-input-border {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
border: 1px solid $uni-border-1;
border-radius: 4px;
/* #ifdef MP-ALIPAY */
overflow: hidden;
/* #endif */
}
.uni-error-message {
position: absolute;
bottom: -17px;
left: 0;
line-height: 12px;
color: $uni-error;
font-size: 12px;
text-align: left;
}
.uni-error-msg--boeder {
position: relative;
bottom: 0;
line-height: 22px;
}
.is-input-error-border {
border-color: $uni-error;
.uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);
}
}
.uni-easyinput--border {
margin-bottom: 0;
padding: 10px 15px;
// padding-bottom: 0;
border-top: 1px #eee solid;
}
.uni-easyinput-error {
padding-bottom: 0;
}
.is-first-border {
/* #ifndef APP-NVUE */
border: none;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
.is-disabled {
background-color: #f7f6f6;
color: #d5d5d5;
.uni-easyinput__placeholder-class {
color: #d5d5d5;
font-size: 12px;
}
}
</style>
{
"id": "yingbing-flip",
"displayName": "好用翻页组件",
"version": "1.0.4",
"description": "高性能翻页组件",
"id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框",
"version": "1.1.18",
"description": "Easyinput 组件是对原生input组件的增强",
"keywords": [
"翻页"
"uni-ui",
"uniui",
"input",
"uni-easyinput",
"输入框"
],
"repository": "https://gitee.com/yingbing-developer/yingbing-flip.git",
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.1.0"
"HBuilderX": ""
},
"dcloudext": {
"type": "component-vue",
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
......@@ -28,52 +34,53 @@
"data": "无",
"permissions": "无"
},
"npmurl": ""
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
......
## 1.0.4(2023-09-02)
* 解决插槽中有scroll-view等滚动组件时滑动报错的问题
## 1.0.3(2023-08-23)
* 解决APP-NVUE端resetLoading不生效的问题
## 1.0.2(2023-07-25)
* 解决竖直翻页时,点击上下2侧翻页不准确得问题
## 1.0.1(2023-07-24)
* 解决APP-NVUE无法点击左右2侧翻页得问题
## 1.0.0(2023-07-22)
* 第一次更新
function touchstart (event, ins) {
var state = ins.getState()
if ( state.isTouch || state.disableTouch ) {
return
}
state.isTouch = true
state.touchTime = 0
state.interval = true
setInterval(ins)
var touch = event.touches[0]
state.startX = touch.pageX
state.startY = touch.pageY
}
function touchmove (event, ins) {
var state = ins.getState()
if ( state.isTouch && !state.disableTouch ) {
var touch = event.touches[0]
state.offset = state.vertical ? touch.pageY - state.startY : touch.pageX - state.startX;
if (state.direction) {
var rect = ins.getBoundingClientRect()
var size = state.vertical ? rect.height : rect.width
state.offset = state.direction == 'next' ? state.offset + state.sliderFault : state.offset - state.sliderFault
if ( (state.offset > 0 && state.direction == 'next') || (state.offset < 0 && state.direction == 'prev') ) {
state.offset = 0
}
if ( Math.abs(state.offset) <= size ) {
animation(state.offset, 0, ins)
}
} else {
if ( Math.abs(state.offset) < state.sliderFault ) {
return
}
if ( state.offset < 0 ) {
if ( state.nextIndex < state.count && state.nextIndex != 0 ) {
if ( state.type != 'none' ) {state.direction = 'next'}
} else if ( state.pullupable && state.loadingState != 'loading' && state.loadingState != 'success' && state.loadingState != 'fail' ) {
state.loadingType = 'pullup'
state.offset = state.offset + state.sliderFault
pulling(state.offset, ins)
}
} else {
if ( state.prevIndex >= 0 && state.prevIndex != state.count - 1 ) {
if ( state.type != 'none' ) {state.direction = 'prev'}
} else if ( state.pulldownable && state.loadingState != 'loading' && state.loadingState != 'success' && state.loadingState != 'fail' ) {
state.loadingType = 'pulldown'
state.offset = state.offset - state.sliderFault
pulling(state.offset, ins)
}
}
}
}
}
function touchend (event, ins) {
touchaction(event, ins)
}
function touchcancel (event, ins) {
touchaction(event, ins)
}
function touchaction (event, ins,isFlipTo) {
var state = ins.getState()
clearInterval(ins)
if ( state.isTouch && !state.disableTouch ) {
var rect = ins.getBoundingClientRect()
var size = state.vertical ? rect.height : rect.width
var start = state.vertical ? state.startY : state.startX
if ( !state.direction && state.touchTime <= 200 && (!state.unableClickPage || state.type == 'none') && !isFlipTo ) {
//获取点击位置,判断向哪里翻页
if (start > (size / 4) * 3 && state.nextIndex < state.count && state.nextIndex != 0 ) {
state.direction = 'next'
}
if (start < (size / 4) && state.prevIndex >= 0 && state.prevIndex != state.count - 1 ) {
state.direction = 'prev'
}
}
if (state.direction) {
state.disableTouch = true
if (state.touchTime <= 200) {
var duration = state.type == 'none' ? 0 : state.duration;
var value = state.direction == 'next' ? 1 : -1;
animation(-value * size, duration, ins);
ins.setTimeout(function () {
resetShadow(ins, true)
ins.callMethod('handleFlipChange', value);
}, duration + 50)
} else {
var duration = state.type == 'none' ? 0 : state.duration;
if (Math.abs(state.offset) >= size / 4) {
var value = state.direction == 'next' ? 1 : -1;
animation(-value * size, duration, ins);
ins.setTimeout(function () {
resetShadow(ins, true)
ins.callMethod('handleFlipChange', value);
}, duration + 50)
} else {
animation(0, duration, ins);
ins.setTimeout(function () {
resetShadow(ins)
resetFlip(ins);
}, duration + 50)
}
}
} else if ( state.loadingState == 'default' ) {
resetPulling(ins)
} else if ( state.loadingState == 'ready' ) {
pullingRefresh(ins)
} else {
resetShadow(ins, false)
resetFlip(ins)
}
}
}
function propWatcher (newVal, oldVal, ins) {
ins.setTimeout(function () {
var state = ins.getState()
state.vertical = newVal.vertical
state.pulldownable = newVal.pulldownable
state.pullupable = newVal.pullupable
state.pulldownHeight = newVal.pulldownHeight
state.loadingState = newVal.loadingState
state.pullupHeight = newVal.pullupHeight
state.duration = newVal.duration
state.nextIndex = newVal.nextIndex
state.prevIndex = newVal.prevIndex
state.currentIndex = newVal.currentIndex
state.sliderFault = newVal.sliderFault
state.count = newVal.count
state.type = newVal.type
state.unableClickPage = newVal.unableClickPage
state.translate = newVal.translate
if ( oldVal && newVal.currentIndex != oldVal.currentIndex ) {
resetFlip(ins);
}
if ( oldVal && newVal.loadingState != oldVal.loadingState && state.loadingState ) {
resetPulling(ins)
ins.callMethod('resetLoading')
}
if (oldVal && newVal.flipTo != oldVal.flipTo && newVal.flipTo != 0 ) {
if ( !state.disableTouch ) {
if ( newVal.flipTo < 0 && state.prevIndex >= 0 && (state.prevIndex != state.count - 1) ) {
state.isTouch = true
state.touchTime = 0
state.direction = 'prev'
touchaction(null, ins, true)
}
if ( newVal.flipTo > 0 && state.nextIndex < state.count && state.nextIndex != 0 ) {
state.isTouch = true
state.touchTime = 0
state.direction = 'next'
touchaction(null, ins, true)
}
}
}
}, 100)
}
function setInterval (ins) {
var state = ins.getState()
state.touchTimer = ins.setTimeout(function () {
state.touchTime += 10
if ( state.interval ) {
setInterval(ins)
}
}, 10)
}
function clearInterval (ins) {
var state = ins.getState()
state.interval = false
if ( state.touchTimer ) {
ins.clearTimeout(state.touchTimer)
state.touchTimer = null
}
}
function resetShadow (ins, isChange) {
var state = ins.getState()
var direction = state.direction
if ( !direction ) {
return
}
var index = direction == 'next' ? state.currentIndex : state.prevIndex
var translate = state.translate
var rect = ins.getBoundingClientRect()
var size = state.vertical ? rect.height : rect.width;
var draw = function () {
ins.selectComponent('.yingbing-flip-item_' + index).setStyle({
'box-shadow': '',
transform: translate + '(' + (isChange ? (direction == 'next'? -size : 0) : (direction == 'next'? 0 : -size)) + 'px)',
transition: ''
})
if ( state.type == 'real' ) {
ins.selectComponent('.yingbing-flip-item-bg_' + index).setStyle({
'box-shadow': '',
transform: translate + '(' + (isChange ? (direction == 'next' ? 0 : size) : (direction == 'next' ? size : 0)) + 'px)',
transition: ''
})
}
ins.selectComponent('.yingbing-flip-item-shadow_' + index).setStyle({
'box-shadow': '',
transition: ''
})
}
ins.requestAnimationFrame(draw)
}
function resetFlip (ins) {
var state = ins.getState()
state.direction = null
state.isTouch = false
state.disableTouch = false
state.offset = 0
state.touchTime = 0
state.startX = 0
state.startY = 0
}
function animation (offset, duration, ins, noshadow) {
var state = ins.getState()
var rect = ins.getBoundingClientRect()
var size = state.vertical ? rect.height : rect.width
var translate = state.translate
var late = offset
var draw = function () {
if ( state.direction == 'prev' ) {
if ( state.prevIndex >= 0 ) {
ins.selectComponent('.yingbing-flip-item_' + state.prevIndex).setStyle({
transform: translate + '(' + (late - size) + 'px)',
'box-shadow': noshadow ? '' : state.type == 'real' ? '0 0 30px 20px rgba(0,0,0,0.4)' : state.type == 'cover' ? '0 0 10px 5px rgba(0,0,0,0.3)' : '',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
if ( state.type == 'real' ) {
ins.selectComponent('.yingbing-flip-item-content_' + state.prevIndex).setStyle({
transform: translate + '(' + (size-late) + 'px)',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
ins.selectComponent('.yingbing-flip-item-bg_' + state.prevIndex).setStyle({
transform: translate + '(' + (late) + 'px)',
'box-shadow': noshadow ? '' : '-5px 0 20px rgba(0,0,0,0.1)',
transition: duration > 0 ? 'transform ' + duration + 'ms, ' + 'boxShadow ' + duration + 'ms' : ''
})
ins.selectComponent('.yingbing-flip-item-shadow_' + state.prevIndex).setStyle({
'box-shadow': noshadow ? '' : '0 0 60px 30px rgba(0,0,0,0.4)',
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
})
}
}
} else {
if ( state.nextIndex < state.count ) {
ins.selectComponent('.yingbing-flip-item_' + state.nextIndex).setStyle({
transform: translate + '(0)',
transition: ''
})
}
ins.selectComponent('.yingbing-flip-item_' + state.currentIndex).setStyle({
transform: translate + '(' + late + 'px)',
'box-shadow': noshadow ? '' : state.type == 'real' ? '0 0 30px 20px rgba(0,0,0,0.4)' : state.type == 'cover' ? '0 0 10px 5px rgba(0,0,0,0.3)' : '',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
if ( state.type == 'real' ) {
ins.selectComponent('.yingbing-flip-item-content_' + state.currentIndex).setStyle({
transform: translate + '(' + (-late) + 'px)',
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
})
ins.selectComponent('.yingbing-flip-item-bg_' + state.currentIndex).setStyle({
transform: translate + '(' + (late + size) + 'px)',
'box-shadow': noshadow ? '' : '-5px 0 20px rgba(0,0,0,0.1)',
transition: duration > 0 ? 'transform ' + duration + 'ms, ' + 'boxShadow ' + duration + 'ms' : ''
})
ins.selectComponent('.yingbing-flip-item-shadow_' + state.currentIndex).setStyle({
'box-shadow': noshadow ? '' : '0 0 60px 30px rgba(0,0,0,0.4)',
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
})
}
}
}
ins.requestAnimationFrame(draw)
}
function pulling (offset, ins) {
var state = ins.getState()
var loadingType = state.loadingType
var translate = state.translate
var size = loadingType == 'pullup' ? state.pullupHeight : state.pulldownHeight
var late = offset
if ( Math.abs(state.offset) < size ) {
state.loadingState = 'default'
} else {
state.loadingState = 'ready'
}
var draw = function () {
var pullingItems = ins.selectAllComponents('.yingbing-flip-' + loadingType + '-item')
for ( var i = 0; i < pullingItems.length; i++ ) {
if ( pullingItems[i].hasClass('yingbing-flip-' + loadingType + '-' + state.loadingState) ) {
pullingItems[i].setStyle({
visibility: 'visible'
})
} else {
pullingItems[i].setStyle({
visibility: 'hidden'
})
}
}
if ( Math.abs(late) <= size ) {
ins.selectComponent('.yingbing-flip-' + loadingType).setStyle({
transform: translate + '(' + (loadingType == 'pullup' ? late + size : late - size) + 'px)',
transition: ''
})
}
}
ins.requestAnimationFrame(draw)
}
function resetPulling (ins) {
var state = ins.getState()
var loadingType = state.loadingType
var translate = state.translate
var size = loadingType == 'pullup' ? state.pullupHeight : state.pulldownHeight
var draw = function () {
var pullingItems = ins.selectAllComponents('.yingbing-flip-' + loadingType + '-item')
for ( var i = 0; i < pullingItems.length; i++ ) {
if ( pullingItems[i].hasClass('yingbing-flip-' + loadingType + '-' + state.loadingState) ) {
pullingItems[i].setStyle({
visibility: 'visible'
})
} else {
pullingItems[i].setStyle({
visibility: 'hidden'
})
}
}
ins.selectComponent('.yingbing-flip-' + loadingType).setStyle({
transform: translate + '(' + (loadingType == 'pullup' ? size : -size) + 'px)',
transition: 'transform .3s'
})
}
ins.requestAnimationFrame(draw)
if ( state.loadingState ) {
ins.setTimeout( function () {
state.loadingState = ''
resetPulling(ins)
}, 300)
} else {
state.loadingType = ''
resetFlip(ins)
}
}
function pullingRefresh (ins) {
var state = ins.getState()
state.loadingState = 'loading'
var loadingType = state.loadingType
var draw = function () {
var pullingItems = ins.selectAllComponents('.yingbing-flip-' + loadingType + '-item')
for ( var i = 0; i < pullingItems.length; i++ ) {
if ( pullingItems[i].hasClass('yingbing-flip-' + loadingType + '-loading') ) {
pullingItems[i].setStyle({
visibility: 'visible'
})
} else {
pullingItems[i].setStyle({
visibility: 'hidden'
})
}
}
}
ins.requestAnimationFrame(draw)
ins.callMethod('pullingRefresh', state.loadingType)
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
touchcancel: touchcancel,
propWatcher: propWatcher
}
\ No newline at end of file
const Binding = uni.requireNativePlugin('bindingx')
const animation = uni.requireNativePlugin('animation')
const dom = uni.requireNativePlugin('dom')
import Util from '../../js_sdk/util.js'
export default {
data () {
return {
disableTouch: false,
isTouch: false,
touchTime: 0,
interval: false,
loadingType: '',
direction: ''
}
},
beforeDestroy () {
if ( this.flipBinding ) {
Binding.unbind({
token: this.flipBinding.token,
eventType: 'pan'
})
this.sliderBinding = null
}
this.resetFlipAnimationBinding()
},
methods: {
fliptouchstart (event) {
if ( this.isTouch || this.disableTouch ) {
return
}
this.isTouch = true
this.touchTime = 0
this.interval = true
this.setInterval()
let touch = event.touches[0]
this.startX = touch.pageX
this.startY = touch.pageY
},
fliptouchmove (event) {
if ( this.isTouch && !this.disableTouch ) {
let touch = event.touches[0]
let offset = this.vertical ? touch.pageY - this.startY : touch.pageX - this.startX
if ( !this.direction ) {
if ( Math.abs(offset) < this.sliderFault ) return
if ( offset < 0 ) {
if ( this.nextIndex < this.count && this.nextIndex != 0 ) {
if ( this.type != 'none' ) {this.direction = 'next'}
} else if ( this.pullupable && this.loadingState != 'loading' && this.loadingState != 'success' && this.loadingState != 'fail' ) {
this.loadingType = 'pullup'
this.disableTouch = true
this.clearInterval()
this.pulling()
}
} else {
if ( this.prevIndex > -1 && this.prevIndex != this.count - 1 ) {
if ( this.type != 'none' ) {this.direction = 'prev'}
} else if ( this.pulldownable && this.loadingState != 'loading' && this.loadingState != 'success' && this.loadingState != 'fail' ) {
this.loadingType = 'pulldown'
this.disableTouch = true
this.clearInterval()
this.pulling()
}
}
}
if ( this.direction ) {
this.disableTouch = true
this.flipTouchAction()
} else {
this.resetPageBinding()
}
}
},
async fliptouchend (e) {
if ( this.isTouch && !this.disableTouch ) {
let rect = await this.getRect(this.$refs.yingbingFlip)
let size = this.vertical ? rect.height : rect.width
this.clearInterval()
let start = this.vertical ? this.startY : this.startX
if ( this.touchTime <= 200 && (!this.unableClickPage || this.type == 'none') ) {
if (start > (size / 4) * 3 && this.nextIndex < this.count && this.nextIndex != 0 ) {
this.flipToNextBindingX()
} else if (start < (size / 4) && this.prevIndex >= 0 && this.prevIndex != this.count - 1 ) {
this.flipToPrevBindingX()
} else {
this.resetPageBinding()
}
} else {
this.resetPageBinding()
}
}
},
async flipTouchAction () {
let props = []
let rect = await this.getRect(this.$refs.yingbingFlip)
let size = this.vertical ? rect.height : rect.width
let translate = this.translate
let shadowProperty = this.vertical ? 'height' : 'width'
let key = this.vertical ? 'y' : 'x'
if ( this.direction == 'prev' ) {
if ( this.prevIndex > -1 ) {
props.push({
element: this.getEl('yingbingFlipItem_' + this.prevIndex),
property: 'transform.' + translate,
expression: `${key} < 0 ? ${-size} : (${key} > ${size} ? 0 : ${key}-${size})`
})
if ( this.type == 'real' ) {
props.push({
element: this.getEl('yingbingFlipItemContent_' + this.prevIndex),
property: 'transform.' + translate,
expression: `${key} < 0 ? ${size} : (${key} > ${size} ? 0 : ${size}-${key})`
})
props.push({
element: this.getEl('yingbingFlipItemBg_' + this.prevIndex),
property: 'transform.' + translate,
expression: `${key} < 0 ? 0 : (${key} > ${size} ? ${size} : ${key} + 0)`
})
props.push({
element: this.getEl('yingbingFlipItemShadow_' + this.prevIndex),
property: shadowProperty,
expression: `${size} / 2 - abs(${key}) / 2'`
})
}
}
} else {
if ( this.nextIndex < this.count ) {
props.push({
element: this.getEl('yingbingFlipItem_' + this.nextIndex),
property: 'transform.' + translate,
expression: '0+0'
})
}
props.push({
element: this.getEl('yingbingFlipItem_' + this.currentIndex),
property: 'transform.' + translate,
expression: `${key} > 0 ? 0 : (${key} < ${-size} ? ${-size} : ${key} + 0)`
})
if ( this.type == 'real' ) {
props.push({
element: this.getEl('yingbingFlipItemContent_' + this.currentIndex),
property: 'transform.' + translate,
expression: `${key} > 0 ? 0 : (${key} < ${-size} ? ${size} : 0 - ${key})'`
})
props.push({
element: this.getEl('yingbingFlipItemBg_' + this.currentIndex),
property: 'transform.' + translate,
expression: `${key} > 0 ? ${size} : (${key} < ${-size} ? 0 : ${key}+${size})`
})
props.push({
element: this.getEl('yingbingFlipItemShadow_' + this.currentIndex),
property: shadowProperty,
expression: `abs(${key})/2+0`
})
}
}
this.flipBinding = Binding.bind({
anchor: this.getEl('yingbingFlip'),
eventType: 'pan',
props: props
}, (e) => {
if ((e.state == 'end' || e.state == 'cancel') && this.flipBinding) {
this.clearInterval()
Binding.unbind({
token: this.flipBinding.token,
eventType: 'pan'
})
this.flipBinding = null
let value = this.direction == 'next' ? 1 : -1;
if (this.touchTime <= 200) {
this.pageAnimation(-value * size, size);
} else {
let index = this.direction == 'next' ? this.currentIndex : this.prevIndex
let deltaX = Binding.getComputedStyle(this.getEl('yingbingFlipItem_' + index))[translate]
let offset = this.direction == 'next' ? Math.abs(deltaX) : size - Math.abs(deltaX)
if ( offset >= size / 4) {
this.pageAnimation(-value * size, size)
} else {
this.pageAnimation(0, size);
}
}
}
})
},
flipToNextBindingX () {
if ( !this.disableTouch && this.nextIndex < this.count && (this.nextIndex != 0 || this.circular) ) {
this.direction = 'next'
this.flipToBindingX()
}
},
flipToPrevBindingX () {
if ( !this.disableTouch && this.prevIndex >= 0 && (this.prevIndex != this.count - 1 || this.circular) ) {
this.direction = 'prev'
this.flipToBindingX()
}
},
async flipToBindingX () {
this.disableTouch = true
let value = this.direction == 'next' ? 1 : -1;
let rect = await this.getRect(this.$refs.yingbingFlip)
let size = this.vertical ? rect.height : rect.width
this.pageAnimation(-value * size, size)
},
getRect (el) {
return new Promise(resolve => {
dom.getComponentRect(el, res => {
resolve(res.size)
})
})
},
setInterval () {
this.touchTimer = setTimeout(() => {
this.touchTime += 10
if ( this.interval ) {
this.setInterval()
}
}, 10)
},
clearInterval () {
this.interval = false
if ( this.touchTimer ) {
clearTimeout(this.touchTimer)
this.touchTimer = null
}
},
pageAnimation (offset, size) {
let duration = this.type == 'none' ? 0.1 : this.duration
let late = offset
let translate = this.translate
let shadowProperty = this.vertical ? 'height' : 'width'
let props = []
if ( this.direction == 'prev' ) {
if ( this.prevIndex > -1 ) {
let itemTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItem_' + this.prevIndex))[translate]
props.push({
element: this.getEl('yingbingFlipItem_' + this.prevIndex),
property: 'transform.' + translate,
expression: `linear(t, ${itemTrans}, ${late - size - itemTrans}, ${duration})`
})
if ( this.type == 'real' ) {
let contentTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItemContent_' + this.prevIndex))[translate]
props.push({
element: this.getEl('yingbingFlipItemContent_' + this.prevIndex),
property: 'transform.' + translate,
expression: `linear(t, ${contentTrans}, ${-(late - size) - contentTrans}, ${duration})`
})
let bgTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItemBg_' + this.prevIndex))[translate]
props.push({
element: this.getEl('yingbingFlipItemBg_' + this.prevIndex),
property: 'transform.' + translate,
expression: `linear(t, ${bgTrans}, ${late - bgTrans}, ${duration})`
})
let shadowSize = Binding.getComputedStyle(this.getEl('yingbingFlipItemShadow_' + this.prevIndex))[shadowProperty] || size
props.push({
element: this.getEl('yingbingFlipItemShadow_' + this.prevIndex),
property: shadowProperty,
expression: `linear(t, ${shadowSize}, ${-(late - size) - shadowSize}, ${duration})`
})
}
this.flipAnimationBinding = Binding.bind({
eventType: 'timing',
exitExpression: 't>' + duration,
props: props
}, (e) => {
if (e.state == 'exit' && this.flipAnimationBinding && e.t > duration) {
Binding.unbind({
token: this.flipAnimationBinding.token,
eventType: 'timing'
})
this.flipAnimationBinding = null
if ( Math.abs(offset) > 0 ) {
this.handleFlipChange(this.direction == 'next' ? 1 : -1)
this.$nextTick(function () {
this.resetPageBinding()
})
} else {
this.resetPageBinding();
}
}
})
}
} else {
if ( this.nextIndex < this.count ) {
animation.transition(this.getRef('yingbingFlipItem_' + this.nextIndex), {
styles: {
transform: `${translate}(0)`
},
duration: 0,
timingFunction: 'linear',
needLayout: true
})
}
animation.transition(this.getRef('yingbingFlipItem_' + this.currentIndex), {
styles: {
transform: `${translate}(${late}px)`
},
duration: duration,
timingFunction: 'linear',
needLayout: true
}, () => {
if ( Math.abs(offset) > 0 ) {
this.handleFlipChange(this.direction == 'next' ? 1 : -1)
this.$nextTick(function () {
this.resetPageBinding()
})
} else {
this.resetPageBinding();
}
})
if ( this.type == 'real' ) {
animation.transition(this.getRef('yingbingFlipItemContent_' + this.currentIndex), {
styles: {
transform: `${translate}(${-late}px)`
},
duration: duration,
timingFunction: 'linear',
needLayout: true
})
animation.transition(this.getRef('yingbingFlipItemBg_' + this.currentIndex), {
styles: {
transform: `${translate}(${size + late}px)`
},
duration: duration,
timingFunction: 'linear',
needLayout: true
})
let styles = {}
styles[shadowProperty] = -late + 'px'
animation.transition(this.getRef('yingbingFlipItemShadow_' + this.currentIndex), {
styles: styles,
duration: duration,
timingFunction: 'linear',
needLayout: true
})
}
// if ( this.nextIndex < this.count ) {
// props.push({
// element: this.getEl('yingbingFlipItem_' + this.nextIndex),
// property: 'transform.' + translate,
// expression: '0+0'
// })
// }
// let itemTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItem_' + this.currentIndex))[translate]
// props.push({
// element: this.getEl('yingbingFlipItem_' + this.currentIndex),
// property: 'transform.' + translate,
// expression: `linear(t, ${itemTrans}, ${late - itemTrans}, ${duration})`
// })
// if ( this.type == 'real' ) {
// let contentTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItemContent_' + this.currentIndex))[translate]
// props.push({
// element: this.getEl('yingbingFlipItemContent_' + this.currentIndex),
// property: 'transform.' + translate,
// expression: `linear(t, ${contentTrans}, ${-late - contentTrans}, ${duration})`
// })
// let bgTrans = Binding.getComputedStyle(this.getEl('yingbingFlipItemBg_' + this.currentIndex))[translate]
// props.push({
// element: this.getEl('yingbingFlipItemBg_' + this.currentIndex),
// property: 'transform.' + translate,
// expression: `linear(t, ${bgTrans}, ${size + late - bgTrans}, ${duration})`
// })
// let shadowSize = Binding.getComputedStyle(this.getEl('yingbingFlipItemShadow_' + this.currentIndex))[shadowProperty]
// props.push({
// element: this.getEl('yingbingFlipItemShadow_' + this.currentIndex),
// property: shadowProperty,
// expression: `linear(t, ${shadowSize}, ${-late - shadowSize}, ${duration})`
// })
// }
}
},
pulling () {
let loadingType = this.loadingType
let size = loadingType == 'pullup' ? this.pullupHeight : this.pulldownHeight
let key = this.vertical ? 'y' : 'x'
let translate = this.translate
let props = [
{
element: this.getEl('yingbing_flip_' + loadingType),
property: 'transform.' + translate,
expression: loadingType == 'pullup' ? `abs(${key}) > ${size} ? 0 : ${key} + ${size}` : `abs(${key}) > ${size} ? 0 : ${key} - ${size}`
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_default'),
property: 'opacity',
expression: `abs(${key}) < ${size} ? 1 : 0`
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_ready'),
property: 'opacity',
expression: `abs(${key}) < ${size} ? 0 : 1`
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_loading'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_success'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_fail'),
property: 'opacity',
expression: '0+0'
}
]
this.flipBinding = Binding.bind({
anchor: this.getEl('yingbingFlip'),
eventType: 'pan',
props: props
}, (e) => {
if ((e.state == 'end' || e.state == 'cancel') && this.flipBinding) {
Binding.unbind({
token: this.flipBinding.token,
eventType: 'pan'
})
this.flipBinding = null
let deltaX = Binding.getComputedStyle(this.getEl('yingbing_flip_' + loadingType))[translate]
if ( deltaX == 0 ) {
this.loadingState = 'ready'
this.pullingRefreshBindingx()
} else {
this.loadingState = 'default'
this.resetPullingBindingx()
}
}
})
},
resetPullingBindingx () {
let loadingType = this.loadingType
let translate = this.translate
let size = loadingType == 'pullup' ? this.pullupHeight : this.pulldownHeight
let trans = loadingType == 'pullup' ? size : -size
let deltaX = Binding.getComputedStyle(this.getEl('yingbing_flip_' + loadingType))[translate]
let duration = 300
let props = [
{
element: this.getEl('yingbing_flip_' + loadingType),
property: 'transform.' + translate,
expression: `linear(t, ${deltaX}, ${trans}, ${duration})`
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_default'),
property: 'opacity',
expression: this.loadingState == 'default' ? '1+0' : '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_ready'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_loading'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_success'),
property: 'opacity',
expression: this.loadingState == 'success' ? '1+0' : '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_fail'),
property: 'opacity',
expression: this.loadingState == 'fail' ? '1+0' : '0+0'
}
]
this.flipAnimationBinding = Binding.bind({
eventType: 'timing',
exitExpression: 't>' + duration,
props: props
}, (e) => {
if (e.state == 'exit' && this.flipAnimationBinding && e.t > duration) {
this.resetFlipAnimationBinding()
this.loadingState = ''
this.resetPageBinding()
}
})
},
pullingRefreshBindingx () {
let loadingType = this.loadingType
let duration = 1
let props = [
{
element: this.getEl('yingbing_flip_' + loadingType + '_default'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_ready'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_loading'),
property: 'opacity',
expression: '1+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_success'),
property: 'opacity',
expression: '0+0'
},
{
element: this.getEl('yingbing_flip_' + loadingType + '_fail'),
property: 'opacity',
expression: '0+0'
}
]
this.flipAnimationBinding = Binding.bind({
eventType: 'timing',
exitExpression: 't>' + duration,
props: props
}, (e) => {
if (e.state == 'exit' && this.flipAnimationBinding && e.t > duration) {
this.resetFlipAnimationBinding()
this.pullingRefresh(loadingType)
}
})
},
resetFlipAnimationBinding () {
if ( this.flipAnimationBinding ) {
Binding.unbind({
token: this.flipAnimationBinding.token,
eventType: 'timing'
})
this.flipAnimationBinding = null
}
},
getEl (selector) {
return this.$refs[selector] ? Util.getEl(this.$refs[selector].length > 0 ? this.$refs[selector][0] : this.$refs[selector]) : null
},
getRef (selector) {
return this.$refs[selector] ? this.$refs[selector].length > 0 ? this.$refs[selector][0] : this.$refs[selector] : null
},
resetPageBinding () {
this.direction = ''
this.touchTime = 0
this.startX = 0
this.startY = 0
this.$nextTick(function () {
this.isTouch = false
this.disableTouch = false
})
}
}
}
\ No newline at end of file
<template>
<!-- #ifndef APP-NVUE -->
<view
class="yingbing-flip"
:prop="flipProp"
:change:prop="flip.propWatcher"
@touchstart="flip.touchstart"
@touchmove="flip.touchmove"
@touchend="flip.touchend"
@touchcancel="flip.touchcancel"
:style="{
background: bgColor
}">
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view
class="yingbing-flip"
ref="yingbingFlip"
@touchstart="fliptouchstart"
@touchmove="fliptouchmove"
@touchend="fliptouchend"
:style="{
background: bgColor
}">
<!-- #endif -->
<view
class="yingbing-flip-item"
:class="'yingbing-flip-item_' + item"
v-for="(item, index) in dataSync"
:ref="'yingbingFlipItem_' + item"
:style="{
'background': bgColor,
'transform': item > currentIndex ? translate + '(' + fullSize + ')' : item < currentIndex ? translate + '(-' + fullSize + ')' : '',
'box-shadow': vertical ? '0 0 15rpx rgba(0,0,0,.2)' : ''
}"
:key="item">
<view
class="yingbing-flip-item-content"
:ref="'yingbingFlipItemContent_' + item"
:class="'yingbing-flip-item-content_' + item"
:style="{
'background': bgColor,
'transform': item < currentIndex ? type == 'real' ? translate + '(' + fullSize + ')' : translate + '(0)' : translate + '(0)'
}">
<!-- #ifdef MP -->
<slot v-if="item > -1 && item < count" :name="item"></slot>
<!-- #endif -->
<!-- #ifndef MP -->
<slot v-if="item > -1 && item < count" :item="data[item]" :index="item"></slot>
<!-- #endif -->
</view>
<view
class="yingbing-flip-item-bg"
:ref="'yingbingFlipItemBg_' + item"
:class="'yingbing-flip-item-bg_' + item"
:style="{
background: bgColor,
transform: item < currentIndex && type == 'real' ? translate + '(0)' : translate + '(' + fullSize + ')',
}"></view>
<view
class="yingbing-flip-item-shadow"
:ref="'yingbingFlipItemShadow_' + item"
:class="'yingbing-flip-item-shadow_' + item"
:style="[shadowStyle]"></view>
</view>
<view class="yingbing-flip-pulldown"
:style="[pulldownStyle]" v-if="pulldownable" ref="yingbing_flip_pulldown">
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-default" ref="yingbing_flip_pulldown_default">
<slot name="pulldownDefault"></slot>
</view>
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-ready" ref="yingbing_flip_pulldown_ready">
<slot name="pulldownReady"></slot>
</view>
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-loading" ref="yingbing_flip_pulldown_loading">
<slot name="pulldownLoading"></slot>
</view>
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-success" ref="yingbing_flip_pulldown_success">
<slot name="pulldownSuccess"></slot>
</view>
<view class="yingbing-flip-pulldown-item yingbing-flip-pulldown-fail" ref="yingbing_flip_pulldown_fail">
<slot name="pulldownFail"></slot>
</view>
</view>
<view class="yingbing-flip-pullup"
:style="[pullupStyle]" v-if="pullupable" ref="yingbing_flip_pullup">
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-default" ref="yingbing_flip_pullup_default">
<slot name="pullupDefault"></slot>
</view>
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-ready" ref="yingbing_flip_pullup_ready">
<slot name="pullupReady"></slot>
</view>
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-loading" ref="yingbing_flip_pullup_loading">
<slot name="pullupLoading"></slot>
</view>
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-success" ref="yingbing_flip_pullup_success">
<slot name="pullupSuccess"></slot>
</view>
<view class="yingbing-flip-pullup-item yingbing-flip-pullup-fail" ref="yingbing_flip_pullup_fail">
<slot name="pullupFail"></slot>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
import flipBindingx from '../modules/flip_bindingx.js'
// #endif
export default {
// #ifdef APP-NVUE
mixins: [flipBindingx],
// #endif
props: {
data: {
type: Array,
default () {
return new Array
}
},
vertical: {
type: Boolean,
default: false
},
current: {
type: Number,
default: 0
},
//翻页方式
type: {
type: String,
default: 'real'
},
//滑动周期
duration: {
type: Number,
default: 100
},
//容错距离
sliderFault: {
type: Number,
default: 20
},
//背景颜色
bgColor: {
type: String,
default: '#fcd281'
},
//是否关闭点击左右2侧位置翻页
unableClickPage: {
type: Boolean,
default: false
},
//开启下拉刷新
pulldownable: {
type: Boolean,
default: false
},
//下拉刷新高度
pulldownHeight: {
type: Number,
default: 80
},
//开启上拉加载
pullupable: {
type: Boolean,
default: false
},
//上拉加载高度
pullupHeight: {
type: Number,
default: 80
}
},
computed: {
dataSync () {
let arr = []
if ( this.prevIndex >= 0 ) {
arr.push(this.prevIndex)
}
arr.push(this.currentIndex)
if ( this.nextIndex < this.count ) {
arr.push(this.nextIndex)
}
return this.refreshing ? [] : arr.sort((a, b) => b-a)
},
nextIndex () {
return this.currentIndex + 1 > this.count - 1 && this.count > 2 ? 0 : this.currentIndex + 1
},
prevIndex () {
return this.currentIndex - 1 < 0 && this.count > 2 ? this.count - 1 : this.currentIndex - 1
},
count () {
return this.data.length
},
flipProp () {
return {
vertical: this.vertical,
pulldownable: this.pulldownable,
pullupable: this.pullupable,
pulldownHeight: this.pulldownHeight,
pullupHeight: this.pullupHeight,
loadingState: this.loadingState,
duration: this.duration,
unableClickPage: this.unableClickPage,
nextIndex: this.nextIndex,
prevIndex: this.prevIndex,
currentIndex: this.currentIndex,
type: this.type,
count: this.count,
flipTo: this.flipTo,
sliderFault: this.sliderFault,
translate: this.translate
}
},
pulldownStyle () {
return this.vertical ? {
left: 0,
right: 0,
top: 0,
height: this.pulldownHeight + 'px',
transform: this.translate + '(-' + this.pulldownHeight + 'px)'
} : {
left: 0,
top: 0,
bottom: 0,
width: this.pulldownHeight + 'px',
transform: this.translate + '(-' + this.pulldownHeight + 'px)'
}
},
pullupStyle () {
return this.vertical ? {
left: 0,
right: 0,
bottom: 0,
height: this.pullupHeight + 'px',
transform: this.translate + '(' + this.pullupHeight + 'px)'
} : {
right: 0,
top: 0,
bottom: 0,
width: this.pullupHeight + 'px',
transform: this.translate + '(' + this.pullupHeight + 'px)'
}
},
shadowStyle () {
return this.vertical ? {
bottom: 0,
right: 0,
left: 0,
height: 0,
// #ifdef APP-NVUE
'background-image': 'linear-gradient(to bottom, rgba(255,255,255, 0), rgba(0,0,0,.5))'
// #endif
} : {
top: 0,
bottom: 0,
right: 0,
width: 0,
// #ifdef APP-NVUE
'background-image': 'linear-gradient(to right, rgba(255,255,255, 0), rgba(0,0,0,.5))'
// #endif
}
},
translate () {
return this.vertical ? 'translateY' : 'translateX'
},
fullSize () {
return this.vertical ? '3050rpx' : '750rpx'
}
},
data () {
return {
refreshing: false,
currentIndex: 0,
flipTo: 0,
loadingState: '',
}
},
created() {
this.currentIndex = this.current
},
methods: {
handleFlipChange (value) {
if ( value > 0 ) {
this.currentIndex = this.currentIndex + value > this.count - 1 ? 0 : this.currentIndex + value
} else {
this.currentIndex = this.currentIndex + value < 0 ? this.count - 1 : this.currentIndex + value
}
this.$emit('change', {
current: this.currentIndex,
detail: this.data[this.currentIndex]
})
this.$emit('update:current', this.currentIndex)
},
pullingRefresh (type) {
this.$emit(type, (state) => {
this.loadingState = state
// #ifdef APP-NVUE
this.resetPullingBindingx()
// #endif
})
},
flipToNext () {
// #ifdef APP-NVUE
this.flipToNextBindingX()
// #endif
// #ifndef APP-NVUE
this.flipTo = 0
this.$nextTick(function () {
this.flipTo = 1
})
// #endif
},
flipToPrev () {
// #ifdef APP-NVUE
this.flipToPrevBindingX()
// #endif
// #ifndef APP-NVUE
this.flipTo = 0
this.$nextTick(function () {
this.flipTo = -1
})
// #endif
},
refresh () {
this.refreshing = true
this.$nextTick(function () {
this.currentIndex = this.current
this.refreshing = false
})
},
resetLoading () {
this.loadingState = ''
// #ifdef APP-NVUE
this.resetPullingBindingx()
// #endif
}
},
watch: {
current (newVal) {
this.currentIndex = newVal
}
}
}
</script>
<!-- #ifdef APP-VUE || H5 || MP-QQ || MP-WEIXIN -->
<script lang="wxs" module="flip" src="../modules/flip.wxs"></script>
<!-- #endif -->
<style>
.yingbing-flip {
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
height: 100%;
/* #endif */
overflow: hidden;
position: relative;
}
.yingbing-flip-item {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
overflow: hidden;
/* #endif */
}
.yingbing-flip-item-content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.yingbing-flip-item-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* #ifdef APP-NVUE */
box-shadow: 0 0 20rpx rgba(0,0,0,0.2);
/* #endif */
}
.yingbing-flip-item-shadow {
position: absolute;
/* #ifdef APP-NVUE */
opacity: 0.5;
/* #endif */
}
.yingbing-flip-pulldown, .yingbing-flip-pullup {
position: absolute;
}
.yingbing-flip-pulldown-item, .yingbing-flip-pullup-item {
/* #ifndef APP-NVUE */
visibility: hidden;
/* #endif */
/* #ifdef APP-NVUE */
opacity: 0;
/* #endif */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* #ifdef MP */
/deep/ .scoped-ref {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
yingbing-flip {
display: block;
}
/* #endif */
</style>
export default {
/**
* 补零
* @param {Number} val 数字
**/
zeroize (val) {
return zeroize(val);
},
/**
* 时间格式化
* @param {String} time 时间戳or时间
**/
dateFormat (time, formats = 'yyyy-mm-dd hh:mm:ss') {
let arr = formats.split(' ')
let dateFormats = ''
let timeFormats = ''
arr.forEach(item => {
if ( item.indexOf('yy') > -1 ) {
dateFormats = item
} else {
timeFormats = item
}
})
const d = new Date(time);
let result = ''
if ( dateFormats.indexOf('yyyy') > -1 ) {
result += d.getFullYear() + '-'
}
if ( dateFormats.indexOf('mm') > -1 ) {
result += zeroize(d.getMonth() + 1) + '-'
}
if ( dateFormats.indexOf('dd') > -1 ) {
result += zeroize(d.getDate()) + ' '
}
if ( timeFormats.indexOf('hh') > -1 ) {
result += zeroize(d.getHours()) + ':'
}
if ( timeFormats.indexOf('mm') > -1 ) {
result += zeroize(d.getMinutes()) + ':'
}
if ( timeFormats.indexOf('ss') > -1 ) {
result += zeroize(d.getSeconds()) + ':'
}
return result.substring(0, result.length - 1)
},
/**
* 秒数转化为分秒
* @param {String} value 秒数
**/
minutesFormat (value) {
let minutes = Math.floor(value / 60 % 60) >= 10 ? Math.floor(value / 60 % 60) : '0' + Math.floor(value / 60 % 60);
let seconds = Math.floor(value % 60) >= 10 ? Math.floor(value % 60) : '0' + Math.floor(value % 60);
return minutes + ':' + seconds;
},
/**
* 时间转化为秒数
* @param {String} time 时间(HH:mm:ss)
**/
time2seconds (time){
const seconds = parseInt(time.split(':')[0]) * 60 + parseInt(time.split(':')[1].split('.')[0]) + parseInt(time.split(':')[1].split('.')[1]) / 1000;
return seconds;
},
/**
* 移除url地址域名
* @param {String} str http地址
**/
removeUrl (url) {
let str = url.replace(/^http:\/\/[^/]+/, '');
return str.substr(1);
},
/**
* 获取文件后缀
* @param {String} name 带后缀的文件名称
**/
suffix (name) {
//获取图片后缀
let fileName = name.lastIndexOf(".");
let fileNameLength = name.length;
let fileFormat = name.substring(fileName + 1, fileNameLength);
return fileFormat;
},
/**
* 清除文件后缀
* @param {String} name 带后缀的文件名称
*/
removeSuffix (name) {
//获取图片后缀
let fileName = name.lastIndexOf(".");
if ( fileName > -1 ) {
let fileNameFormat = name.substring(0, fileName);
return fileNameFormat;
} else {
return name
}
},
/**
* 数组查找符合条件元素并返回下标
* @param {Array} arr 传入数组
* @param {String} value 条件元素
* @param {String} query 对比key值
*/
indexOf (arr, query, value) {
let len = arr.length;
for ( let i = 0; i < len; i++ ) {
if ( arr[i][query] == value ) {
return parseInt(i);
}
}
return -1;
},
/**
* 正则匹配
* @param {String} type 匹配类型
* @param {String} value 匹配值
*/
reg (type, value) {
const regs = {
//身份证证则
idcard: new RegExp(/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/),
//手机正则
mobile: new RegExp(/^1[3456789]\d{9}$/),
//固定电话正则
phone: new RegExp(/^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/),
//金额验证
price: new RegExp(/^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0.\d{1,2}$/),
//邮箱验证
email: new RegExp(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/),
//银行卡
bankcard: new RegExp(/^([1-9]{1})(\d{15}|\d{18})$/)
}
return regs[type].test(value);
},
/**
* 计算2个时间差的分钟数或者秒钟数或时钟数
* @param {datetime} time1 开始时间
* @param {datetime} time2 结束时间
*/
timeMinuse (time1, time2, type = 'minutes') {
//判断开始时间是否大于结束日期
let date1 = new Date(time1);
let date2 = new Date(time2);
if ( date1 > date2 ) {
console.log("开始时间不能大于结束时间!");
return false;
}
let seconds = date2.getTime() / 1000 - date1.getTime() / 1000;
return type == 'minutes' ? (seconds / 60) : type == 'hours' ? (seconds / 60 / 60) : seconds;
},
/**
* 判断值类型返回字符
* @param {datetime} value 需要判断类型的值
*/
typeof (value) {
let type = Object.prototype.toString.call(value);
return type.slice(8, type.length - 1)
},
/**
* 生成随机字符串
* @param {Number} len 长度
*/
randomString (len) {
len = len || 32;
var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
var maxPos = $chars.length;
var pwd = '';
for (let i = 0; i < len; i++) {
  pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
},
/**
* 生成随机ID
*/
randomID () {
let mydate = new Date();
return mydate.getMinutes() + mydate.getSeconds() + mydate.getMilliseconds() + Math.round(Math.random() * 10000);
},
/**
* 生成随机不重复整数
* @param {Number} len 长度
*/
randomSoleNumber (len) {
let min = 0;
let max = len - 1;
let arr = [];
while ( arr.length < len ) {
let value = Math.floor(Math.random() * (max - min + 1)) + min;
if ( arr.indexOf(value) == -1 ) {
arr.push( value )
}
}
return arr;
},
/**
* 16进制颜色转化为rgb
* @param {String} hex 16进制颜色
*/
hex2rgb (hex) {
hex = hex.length == 7 ? hex : '#' + hex.slice(1, 4) + hex.slice(1, 4)
let str="rgb("
const r = parseInt(hex.slice(1,3),16).toString(); //ff slice不包括end
const g = parseInt(hex.slice(3,5),16).toString(); //00
const b = parseInt(hex.slice(5,7),16).toString(); //ff
str += r+","+g+","+b+")";
return str
},
/**
* 16进制颜色转化为rgba
* @param {String} hex 16进制颜色
*/
hex2rgba (hex, opacity) {
hex = hex.length == 7 ? hex : '#' + hex.slice(1, 4) + hex.slice(1, 4)
let str="rgba("
const r = parseInt(hex.slice(1,3),16).toString(); //ff slice不包括end
const g = parseInt(hex.slice(3,5),16).toString(); //00
const b = parseInt(hex.slice(5,7),16).toString(); //ff
str += r+","+g+","+b+","+opacity+")";
return str
},
/**
* byte转化为文件大小
* @param {Number} byte 位
*/
byte2Size (byte) {
let sizeString = ''
if(byte == 0){
sizeString = "0B";
}else if(byte < 1024){
sizeString = byte + "B";
}else if(byte < 1048576){
sizeString = (byte/1024).toFixed(2) + "KB";
}else if (byte < 1073741824){
sizeString = (byte/1048576).toFixed(2) + "MB";
}else{
sizeString = (byte/1073741824).toFixed(2) + "GB";
}
return sizeString;
},
// 深度克隆
deepClone (obj) {
if(typeof obj !== "object" && typeof obj !== 'function') {
//原始类型直接返回
return obj;
}
var o = isArray(obj) ? [] : {};
for(let i in obj) {
if(obj.hasOwnProperty(i)){
o[i] = typeof obj[i] === "object" ? this.deepClone(obj[i]) : obj[i];
}
}
return o;
},
/**
* 将数字转为带中文单位的字符串
* @param {Number} num 数字
*/
numtounit (num) {
let units = [{
label: '万',
value: 10000,
min: 1000
},{
label: '亿',
value: 100000000,
min: 100000000
},{
label: '兆',
value: 10000000000000000,
min: 100000000000000000
}]
let value = num
units.forEach(unit => {
if ( num >= unit.min ) {
value = (num / unit.value).toFixed(2) + unit.label
}
})
return value
},
/**
* 判断像素单位,没有则加上rpx
* @param {String} value 像素
*/
pixelunit (value) {
if ( value.toString().indexOf('px') > -1 || value.toString().indexOf('em') > -1 || value.toString().indexOf('auto') > -1 || value.toString().indexOf('%') > -1 ) {
return value
} else {
return value + 'rpx'
}
},
/**
* 判断像素单位,全部转为px
* @param {String} value 像素
*/
unitpixel (value) {
if ( value.toString().indexOf('rpx') > -1 ) {
return uni.upx2px(value.replace('rpx', ''))
} else if ( value.toString().indexOf('px') > -1 ) {
return parseFloat(value.replace('px', ''))
} else if ( value.toString().indexOf('em') > -1 || value.toString().indexOf('auto') > -1 || value.toString().indexOf('%') > -1 ) {
return value
} else {
return parseFloat(uni.upx2px(value))
}
},
/**
* 判断像素单位,转化为rpx
* @param {String} value 值
* @param {String} unit 返回结果是否带上单位
*/
anytorpx (value, unit = true) {
if ( value.toString().indexOf('rpx') > -1 ) {
return unit ? value : parseFloat(value.replace('rpx', ''))
} else if ( value.toString().indexOf('px') > -1 ) {
return parseFloat(value.replace('px', '') * (750 / uni.getSystemInfoSync().windowWidth)) + (unit ? 'rpx' : 0)
} else if ( value.toString().indexOf('auto') > -1 ) {
return 'auto'
} else if ( value.toString().indexOf('%') > -1 ) {
return parseFloat((value.replace('%', '') / 100) * 750) + (unit ? 'rpx' : 0)
} else if (value.toString().indexOf('em') > -1 || value.toString().indexOf('rem') > -1 ) {
return parseFloat(value.replace('em', '').replace('rem', '') * 32) + (unit ? 'rpx' : 0)
} else if ( /^\d+$/.test(value) ) {
return parseFloat(value) + (unit ? 'rpx' : 0)
}
},
/**
* 判断像素单位,转化为px
* @param {String} value 值
* @param {String} unit 返回结果是否带上单位
*/
anytopx (value, unit = false) {
if ( value.toString().indexOf('rpx') > -1 ) {
return uni.upx2px(value.replace('rpx', '')) + (unit ? 'px' : 0)
} else if ( value.toString().indexOf('px') > -1 ) {
return parseFloat(value.replace('px', '')) + (unit ? 'px' : 0)
} else if ( value.toString().indexOf('auto') > -1 ) {
return 'auto'
} else if ( value.toString().indexOf('%') > -1 ) {
return parseFloat((value.replace('%', '') / 100) * uni.getSystemInfoSync().windowWidth) + (unit ? 'px' : 0)
} else if (value.toString().indexOf('em') > -1 || value.toString().indexOf('rem') > -1 ) {
return parseFloat(value.replace('em', '').replace('rem', '') * uni.getSystemInfoSync().windowWidth) + (unit ? 'px' : 0)
} else if ( /^\d+$/.test(value) ) {
return parseFloat(value) + (unit ? 'px' : 0)
}
},
getRefs (components, name, current) {
// #ifndef MP
return current >= 0 ? components.$refs[name][current] : components.$refs[name]
// #endif
// #ifdef MP
return {}
// #endif
},
//获取节点
getEl (el) {
if (typeof el === 'string' || typeof el === 'number') return el;
if (WXEnvironment) {
return el.ref;
} else {
return el instanceof HTMLElement ? el : el.$el;
}
},
/**
* 获取指定父节点
* @param {String} components 当前实例
* @param {String} name 父节点名称
*/
getParent(name, components) {
let parent = components.$parent
if (parent) {
let parentName = parent.$options.name
while (parentName !== name) {
parent = parent.$parent
if (parent) {
parentName = parent.$options.name
} else {
return null
}
}
return parent
}
return null
},
/**
* 获取指定子节点
* @param {String} components 当前实例
* @param {String} name 父节点名称
*/
getChildrens(names, components) {
let arr = []
let childs = names.split(',')
const dowhile = (children) => {
if ( this.typeof(children) == 'Array' ) {
children.forEach(child => {
if ( childs.indexOf(child.$options.name) > -1 ) {
arr.push(child)
}
if ( child.$children && child.$children.length > 0 ) {
dowhile(child.$children)
}
})
}
}
dowhile(components.$children)
return arr;
},
/**
* 获取指定子节点
* @param {String} selector 节点class或者id
* @param {String} el 节点
* @param {String} components 当前实例
*/
getRect (selector, el, components) {
return new Promise(resolve => {
// #ifdef APP-NVUE
uni.requireNativePlugin('dom').getComponentRect(el, res => {
resolve(res.size)
})
// #endif
// #ifndef APP-NVUE
uni.createSelectorQuery().in(components).select(selector).boundingClientRect(data => {
resolve(data)
}).exec();
// #endif
})
}
}
// 判断arr是否为一个数组,返回一个bool值
function isArray (arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
function zeroize (val) {
return val >= 10 ? val : '0' + val;
}
export const PAKEAGE_NAME = 'com.duben.dybookhm'
export function trim(str) {
return str ? str.replace(/(^\s*)|(\s*$)/g, "") : ''
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment